/**
 * Phase 3: 批处理模式 - 批处理服务
 * 
 * 核心功能：
 * 1. 执行批处理任务（3并发）
 * 2. 处理单个文档
 * 3. 进度推送（WebSocket）
 * 4. 错误处理和重试
 */

import PQueue from 'p-queue';
import { prisma } from '../../config/database.js';
import { LLMFactory } from '../../common/llm/adapters/LLMFactory.js';
import { ModelType } from '../../common/llm/adapters/types.js';
import { getTemplate } from '../templates/clinicalResearch.js';
import { parseJSON } from '../../common/utils/jsonParser.js';

export interface ExecuteBatchTaskParams {
  userId: string;
  kbId: string;
  documentIds: string[];
  templateType: 'preset' | 'custom';
  templateId?: string; // 预设模板ID
  customPrompt?: string; // 自定义提示词
  modelType: ModelType;
  taskName?: string;
  existingTaskId?: string; // 已存在的任务ID（可选）
  onProgress?: (progress: BatchProgress) => void;
}

export interface BatchProgress {
  taskId: string;
  completed: number;
  total: number;
  failed: number;
  currentDocument?: string;
  estimatedSeconds?: number;
}

export interface BatchTaskResult {
  taskId: string;
  status: 'processing' | 'completed' | 'failed';
  totalDocuments: number;
  completedCount: number;
  failedCount: number;
  durationSeconds?: number;
}

/**
 * 执行批处理任务
 */
export async function executeBatchTask(
  params: ExecuteBatchTaskParams
): Promise<BatchTaskResult> {
  const {
    userId,
    kbId,
    documentIds,
    templateType,
    templateId,
    customPrompt,
    modelType,
    taskName,
    existingTaskId,
    onProgress,
  } = params;

  console.log('📦 [BatchService] 开始执行批处理任务', {
    userId,
    kbId,
    documentCount: documentIds.length,
    templateType,
    modelType,
    existingTaskId: existingTaskId || '新建',
  });

  // 验证文献数量 (3-50篇)
  if (documentIds.length < 3 || documentIds.length > 50) {
    throw new Error(`文献数量必须在3-50篇之间，当前：${documentIds.length}篇`);
  }

  // 获取模板或使用自定义提示词
  let systemPrompt: string;
  let userPromptTemplate: string;
  let expectedFields: string[] = [];

  if (templateType === 'preset') {
    if (!templateId) {
      throw new Error('预设模板类型需要提供templateId');
    }

    const template = getTemplate(templateId);
    if (!template) {
      throw new Error(`未找到模板: ${templateId}`);
    }

    systemPrompt = template.systemPrompt;
    userPromptTemplate = template.userPrompt;
    expectedFields = template.outputFields.map(f => f.key);
  } else {
    // 自定义模板
    if (!customPrompt) {
      throw new Error('自定义模板需要提供customPrompt');
    }

    systemPrompt = '你是一个专业的文献分析助手。请根据用户的要求分析文献内容。';
    userPromptTemplate = customPrompt;
  }

  // 使用已存在的任务或创建新任务
  let task;
  if (existingTaskId) {
    task = await prisma.batchTask.findUnique({
      where: { id: existingTaskId },
    });
    if (!task) {
      throw new Error(`任务不存在: ${existingTaskId}`);
    }
    console.log(`✅ [BatchService] 使用已存在的任务: ${task.id}`);
  } else {
    task = await prisma.batchTask.create({
      data: {
        userId,
        kbId,
        name: taskName || `批处理任务_${new Date().toLocaleString('zh-CN')}`,
        templateType,
        templateId: templateId || null,
        prompt: userPromptTemplate,
        status: 'processing',
        totalDocuments: documentIds.length,
        completedCount: 0,
        failedCount: 0,
        modelType,
        concurrency: 3, // 固定3并发
        startedAt: new Date(),
      },
    });
    console.log(`✅ [BatchService] 创建任务记录: ${task.id}`);
  }

  const startTime = Date.now();
  let completedCount = 0;
  let failedCount = 0;

  // 创建并发队列（固定3并发）
  const queue = new PQueue({ concurrency: 3 });

  // 处理所有文档
  const promises = documentIds.map((docId, index) =>
    queue.add(async () => {
      try {
        console.log(`🔄 [BatchService] 处理文档 ${index + 1}/${documentIds.length}: ${docId}`);

        // 获取文档
        const document = await prisma.document.findUnique({
          where: { id: docId },
          select: {
            id: true,
            filename: true,
            extractedText: true,
            tokensCount: true,
          },
        });

        if (!document) {
          throw new Error(`文档不存在: ${docId}`);
        }

        if (!document.extractedText) {
          throw new Error(`文档未提取文本: ${document.filename}`);
        }

        // 调用LLM处理
        const result = await processDocument({
          document,
          systemPrompt,
          userPromptTemplate,
          modelType,
          templateType,
          expectedFields,
        });

        // 保存结果
        await prisma.batchResult.create({
          data: {
            taskId: task.id,
            documentId: docId,
            status: 'success',
            data: result.data,
            rawOutput: result.rawOutput,
            processingTimeMs: result.processingTimeMs,
            tokensUsed: result.tokensUsed,
          },
        });

        completedCount++;
        console.log(`✅ [BatchService] 文档处理成功: ${document.filename} (${result.processingTimeMs}ms)`);

      } catch (error: any) {
        // 处理失败
        console.error(`❌ [BatchService] 文档处理失败: ${docId}`, error);

        await prisma.batchResult.create({
          data: {
            taskId: task.id,
            documentId: docId,
            status: 'failed',
            errorMessage: error.message,
          },
        });

        failedCount++;
      }

      // 推送进度
      if (onProgress) {
        const progress: BatchProgress = {
          taskId: task.id,
          completed: completedCount + failedCount,
          total: documentIds.length,
          failed: failedCount,
          estimatedSeconds: calculateEstimatedTime(
            completedCount + failedCount,
            documentIds.length,
            Date.now() - startTime
          ),
        };
        onProgress(progress);
      }

      // 更新任务进度
      await prisma.batchTask.update({
        where: { id: task.id },
        data: {
          completedCount,
          failedCount,
        },
      });
    })
  );

  // 等待所有任务完成
  await Promise.allSettled(promises);

  // 计算总时长
  const durationSeconds = Math.round((Date.now() - startTime) / 1000);

  // 更新任务状态
  await prisma.batchTask.update({
    where: { id: task.id },
    data: {
      status: 'completed',
      completedAt: new Date(),
      durationSeconds,
    },
  });

  console.log(`🎉 [BatchService] 批处理任务完成: ${task.id}`, {
    total: documentIds.length,
    success: completedCount,
    failed: failedCount,
    durationSeconds,
  });

  return {
    taskId: task.id,
    status: 'completed',
    totalDocuments: documentIds.length,
    completedCount,
    failedCount,
    durationSeconds,
  };
}

/**
 * 处理单个文档
 */
async function processDocument(params: {
  document: {
    id: string;
    filename: string;
    extractedText: string;
    tokensCount: number | null;
  };
  systemPrompt: string;
  userPromptTemplate: string;
  modelType: ModelType;
  templateType: 'preset' | 'custom';
  expectedFields: string[];
}): Promise<{
  data: any;
  rawOutput: string;
  processingTimeMs: number;
  tokensUsed?: number;
}> {
  const {
    document,
    systemPrompt,
    userPromptTemplate,
    modelType,
    templateType,
    expectedFields,
  } = params;

  const startTime = Date.now();

  // 构造完整的用户消息
  const userMessage = `${userPromptTemplate}\n\n【文献：${document.filename}】\n\n${document.extractedText}`;

  // 调用LLM
  const adapter = LLMFactory.getAdapter(modelType);
  const response = await adapter.chat(
    [
      { role: 'system', content: systemPrompt },
      { role: 'user', content: userMessage },
    ],
    {
      temperature: 0.3, // 降低温度提高稳定性
      maxTokens: 2000,
    }
  );

  const processingTimeMs = Date.now() - startTime;
  const rawOutput = response.content;

  // 解析结果
  let data: any;

  if (templateType === 'preset') {
    // 预设模板：解析JSON
    const parseResult = parseJSON(rawOutput, expectedFields);

    if (!parseResult.success) {
      throw new Error(`JSON解析失败: ${parseResult.error}`);
    }

    data = parseResult.data;
  } else {
    // 自定义模板：直接使用文本
    data = {
      extracted_text: rawOutput,
    };
  }

  return {
    data,
    rawOutput,
    processingTimeMs,
    tokensUsed: response.usage?.totalTokens,
  };
}

/**
 * 计算预估剩余时间
 */
function calculateEstimatedTime(
  completed: number,
  total: number,
  elapsedMs: number
): number {
  if (completed === 0) return 0;

  const avgTimePerDoc = elapsedMs / completed;
  const remaining = total - completed;
  return Math.round((avgTimePerDoc * remaining) / 1000);
}

/**
 * 重试失败的文档
 */
export async function retryFailedDocuments(
  taskId: string,
  onProgress?: (progress: BatchProgress) => void
): Promise<{ retriedCount: number }> {
  console.log(`🔄 [BatchService] 重试失败文档: ${taskId}`);

  // 获取任务信息
  const task = await prisma.batchTask.findUnique({
    where: { id: taskId },
    include: {
      results: {
        where: { status: 'failed' },
      },
    },
  });

  if (!task) {
    throw new Error(`任务不存在: ${taskId}`);
  }

  const failedDocIds = task.results.map(r => r.documentId);

  if (failedDocIds.length === 0) {
    return { retriedCount: 0 };
  }

  // 删除旧的失败记录
  await prisma.batchResult.deleteMany({
    where: {
      taskId,
      status: 'failed',
    },
  });

  // 重新执行
  await executeBatchTask({
    userId: task.userId,
    kbId: task.kbId,
    documentIds: failedDocIds,
    templateType: task.templateType as 'preset' | 'custom',
    templateId: task.templateId || undefined,
    customPrompt: task.templateType === 'custom' ? task.prompt : undefined,
    modelType: task.modelType as ModelType,
    taskName: `${task.name} (重试)`,
    onProgress,
  });

  return { retriedCount: failedDocIds.length };
}

