import {
  Injectable,
  Inject,
  NotFoundException,
  BadRequestException,
  Logger,
} from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { OssService } from '../oss/oss.service';
import { CreateTaskDto } from './dto/create-task.dto';
import { QueryTaskDto } from './dto/query-task.dto';
import { AI3DProvider, AI3D_PROVIDER } from './providers/ai-3d-provider.interface';

// 配置常量
const MAX_CONCURRENT_TASKS = 3; // 每用户最大并行任务数
const TASK_TIMEOUT_MS = 10 * 60 * 1000; // 10分钟超时
const MAX_RETRY_COUNT = 3; // 最大重试次数

@Injectable()
export class AI3DService {
  private readonly logger = new Logger(AI3DService.name);

  constructor(
    private prisma: PrismaService,
    private ossService: OssService,
    @Inject(AI3D_PROVIDER) private ai3dProvider: AI3DProvider,
  ) {}

  /**
   * 创建生成任务
   */
  async createTask(
    userId: number,
    tenantId: number,
    dto: CreateTaskDto,
  ) {
    // 1. 检查用户当前进行中的任务数量
    const activeTaskCount = await this.prisma.aI3DTask.count({
      where: {
        userId,
        status: { in: ['pending', 'processing'] },
      },
    });

    if (activeTaskCount >= MAX_CONCURRENT_TASKS) {
      throw new BadRequestException(
        `您当前有 ${activeTaskCount} 个任务正在处理中，最多同时处理 ${MAX_CONCURRENT_TASKS} 个任务，请等待完成后再提交`,
      );
    }

    // 2. 创建数据库记录
    const task = await this.prisma.aI3DTask.create({
      data: {
        userId,
        tenantId,
        inputType: dto.inputType,
        inputContent: dto.inputContent,
        generateType: dto.generateType || 'Normal',
        status: 'pending',
      },
    });

    // 3. 提交到 AI 服务
    try {
      const externalTaskId = await this.ai3dProvider.submitTask(
        dto.inputType,
        dto.inputContent,
        {
          generateType: dto.generateType,
          faceCount: dto.faceCount,
        },
      );

      // 4. 更新状态为处理中
      await this.prisma.aI3DTask.update({
        where: { id: task.id },
        data: {
          status: 'processing',
          externalTaskId,
        },
      });

      // 5. 启动轮询检查任务状态
      this.pollTaskStatus(task.id, externalTaskId, Date.now());

      this.logger.log(`任务 ${task.id} 创建成功，外部ID: ${externalTaskId}`);

      return this.getTask(userId, task.id);
    } catch (error) {
      // 提交失败，更新状态
      await this.prisma.aI3DTask.update({
        where: { id: task.id },
        data: {
          status: 'failed',
          errorMessage: error.message || 'AI服务提交失败',
          completeTime: new Date(),
        },
      });

      this.logger.error(`任务 ${task.id} 提交失败: ${error.message}`);
      throw error;
    }
  }

  /**
   * 获取任务列表
   */
  async getTasks(userId: number, query: QueryTaskDto) {
    const { page = 1, pageSize = 10, status } = query;

    const where: any = { userId };
    if (status) {
      where.status = status;
    }

    const [list, total] = await Promise.all([
      this.prisma.aI3DTask.findMany({
        where,
        skip: (page - 1) * pageSize,
        take: pageSize,
        orderBy: { createTime: 'desc' },
      }),
      this.prisma.aI3DTask.count({ where }),
    ]);

    return {
      list,
      total,
      page,
      pageSize,
    };
  }

  /**
   * 获取任务详情
   */
  async getTask(userId: number, id: number) {
    const task = await this.prisma.aI3DTask.findFirst({
      where: { id, userId },
    });

    if (!task) {
      throw new NotFoundException('任务不存在');
    }

    return task;
  }

  /**
   * 删除任务
   */
  async deleteTask(userId: number, id: number) {
    const task = await this.getTask(userId, id);

    await this.prisma.aI3DTask.delete({
      where: { id: task.id },
    });

    this.logger.log(`任务 ${id} 已删除`);

    return null;
  }

  /**
   * 重试任务
   */
  async retryTask(userId: number, id: number) {
    const task = await this.prisma.aI3DTask.findFirst({
      where: { id, userId },
    });

    if (!task) {
      throw new NotFoundException('任务不存在');
    }

    // 只有失败或超时的任务可以重试
    if (!['failed', 'timeout'].includes(task.status)) {
      throw new BadRequestException('只有失败或超时的任务可以重试');
    }

    // 检查重试次数
    if (task.retryCount >= MAX_RETRY_COUNT) {
      throw new BadRequestException(
        `已达到最大重试次数 ${MAX_RETRY_COUNT} 次，请创建新任务`,
      );
    }

    // 检查并发限制
    const activeTaskCount = await this.prisma.aI3DTask.count({
      where: {
        userId,
        status: { in: ['pending', 'processing'] },
      },
    });

    if (activeTaskCount >= MAX_CONCURRENT_TASKS) {
      throw new BadRequestException(
        `您当前有 ${activeTaskCount} 个任务正在处理中，请等待完成后再重试`,
      );
    }

    // 重置任务状态
    await this.prisma.aI3DTask.update({
      where: { id },
      data: {
        status: 'pending',
        errorMessage: null,
        completeTime: null,
        retryCount: { increment: 1 },
      },
    });

    // 重新提交任务
    try {
      const externalTaskId = await this.ai3dProvider.submitTask(
        task.inputType as 'text' | 'image',
        task.inputContent,
      );

      await this.prisma.aI3DTask.update({
        where: { id },
        data: {
          status: 'processing',
          externalTaskId,
        },
      });

      this.pollTaskStatus(id, externalTaskId, Date.now());

      this.logger.log(`任务 ${id} 重试成功，外部ID: ${externalTaskId}`);

      return this.getTask(userId, id);
    } catch (error) {
      await this.prisma.aI3DTask.update({
        where: { id },
        data: {
          status: 'failed',
          errorMessage: error.message || 'AI服务提交失败',
          completeTime: new Date(),
        },
      });

      this.logger.error(`任务 ${id} 重试失败: ${error.message}`);
      throw error;
    }
  }

  /**
   * 轮询检查任务状态
   */
  private async pollTaskStatus(
    taskId: number,
    externalTaskId: string,
    startTime: number,
  ) {
    const checkStatus = async () => {
      // 1. 检查是否超时
      if (Date.now() - startTime > TASK_TIMEOUT_MS) {
        await this.prisma.aI3DTask.update({
          where: { id: taskId },
          data: {
            status: 'timeout',
            errorMessage: '任务处理超时，请重试',
            completeTime: new Date(),
          },
        });
        this.logger.warn(`任务 ${taskId} 超时`);
        return;
      }

      // 2. 查询外部任务状态
      try {
        const result = await this.ai3dProvider.queryTask(externalTaskId);

        if (result.status === 'completed' || result.status === 'failed') {
          let finalResultUrl = result.resultUrl;
          let finalPreviewUrl = result.previewUrl;
          let finalResultUrls = result.resultUrls || null;
          let finalPreviewUrls = result.previewUrls || null;

          // 3. 如果任务成功且COS已启用，转存文件到自己的COS
          if (result.status === 'completed' && this.ossService.isEnabled()) {
            try {
              // 转存所有模型文件
              if (result.resultUrls && result.resultUrls.length > 0) {
                this.logger.log(`开始转存 ${result.resultUrls.length} 个模型文件到COS: ${taskId}`);
                const uploadedModelUrls: string[] = [];
                for (let i = 0; i < result.resultUrls.length; i++) {
                  const modelResult = await this.ossService.uploadAI3DModel(
                    result.resultUrls[i],
                    taskId,
                    i,
                  );
                  uploadedModelUrls.push(modelResult.modelUrl);
                  this.logger.log(`模型文件 ${i + 1} 转存完成: ${modelResult.modelUrl}`);
                }
                finalResultUrl = uploadedModelUrls[0];
                finalResultUrls = uploadedModelUrls;
              }

              // 转存所有预览图
              if (result.previewUrls && result.previewUrls.length > 0) {
                this.logger.log(`开始转存 ${result.previewUrls.length} 个预览图到COS: ${taskId}`);
                const uploadedPreviewUrls: string[] = [];
                for (let i = 0; i < result.previewUrls.length; i++) {
                  const previewResult = await this.ossService.uploadAI3DPreview(
                    result.previewUrls[i],
                    taskId,
                    i,
                  );
                  uploadedPreviewUrls.push(previewResult.previewUrl);
                  this.logger.log(`预览图 ${i + 1} 转存完成: ${previewResult.previewUrl}`);
                }
                finalPreviewUrl = uploadedPreviewUrls[0];
                finalPreviewUrls = uploadedPreviewUrls;
              }
            } catch (transferError) {
              // 转存失败不影响任务完成，只记录日志
              this.logger.error(
                `文件转存失败，使用原始URL: ${transferError.message}`,
              );
            }
          }

          await this.prisma.aI3DTask.update({
            where: { id: taskId },
            data: {
              status: result.status,
              resultUrl: finalResultUrl,
              previewUrl: finalPreviewUrl,
              resultUrls: finalResultUrls,
              previewUrls: finalPreviewUrls,
              errorMessage: result.errorMessage,
              completeTime: new Date(),
            },
          });

          this.logger.log(
            `任务 ${taskId} ${result.status === 'completed' ? '完成' : '失败'}`,
          );
        } else {
          // 继续轮询，每2秒检查一次
          setTimeout(checkStatus, 2000);
        }
      } catch (error) {
        this.logger.error(`轮询任务 ${taskId} 状态出错: ${error.message}`);
        // 出错后延长轮询间隔，每5秒重试
        setTimeout(checkStatus, 5000);
      }
    };

    // 首次检查延迟2秒
    setTimeout(checkStatus, 2000);
  }
}
