/**
 * ASL LLM筛选服务
 * 使用双模型策略进行文献筛选
 */

import { LLMFactory } from '../../../common/llm/adapters/LLMFactory.js';
import { ModelType } from '../../../common/llm/adapters/types.js';
import { parseJSON } from '../../../common/utils/jsonParser.js';
import Ajv from 'ajv';
import { screeningOutputSchema, generateScreeningPrompt, type ScreeningStyle } from '../schemas/screening.schema.js';
import { LLMScreeningOutput, DualModelScreeningResult, PicoCriteria } from '../types/index.js';
import { logger } from '../../../common/logging/index.js';

const ajv = new Ajv();
const validate = ajv.compile(screeningOutputSchema);

// 模型名称映射：从模型ID映射到ModelType
const MODEL_TYPE_MAP: Record<string, ModelType> = {
  'deepseek-chat': 'deepseek-v3',
  'deepseek-v3': 'deepseek-v3',
  'qwen-max': 'qwen3-72b',      // ⭐ qwen-max = Qwen最新最强模型
  'qwen-plus': 'qwen3-72b',     // qwen-plus = Qwen2.5-72B (次选)
  'qwen3-72b': 'qwen3-72b',
  'qwen-long': 'qwen-long',
  'gpt-4o': 'gpt-5',            // ⭐ gpt-4o 映射到 gpt-5
  'gpt-5-pro': 'gpt-5',
  'gpt-5': 'gpt-5',
  'claude-sonnet-4.5': 'claude-4.5',  // ⭐ claude-sonnet-4.5 映射
  'claude-sonnet-4-5-20250929': 'claude-4.5',
  'claude-4.5': 'claude-4.5',
};

export class LLMScreeningService {
  /**
   * 使用单个模型进行筛选
   */
  async screenWithModel(
    modelName: string,
    title: string,
    abstract: string,
    picoCriteria: PicoCriteria,
    inclusionCriteria: string,
    exclusionCriteria: string,
    style: ScreeningStyle = 'standard',
    authors?: string,
    journal?: string,
    publicationYear?: number
  ): Promise<LLMScreeningOutput> {
    try {
      // 映射模型名称到ModelType
      const modelType = MODEL_TYPE_MAP[modelName];
      if (!modelType) {
        throw new Error(`Unsupported model name: ${modelName}. Supported models: ${Object.keys(MODEL_TYPE_MAP).join(', ')}`);
      }

      const prompt = generateScreeningPrompt(
        title,
        abstract,
        picoCriteria,
        inclusionCriteria,
        exclusionCriteria,
        style,
        authors,
        journal,
        publicationYear
      );

      const llmAdapter = LLMFactory.getAdapter(modelType);
      const response = await llmAdapter.chat([
        { role: 'user', content: prompt },
      ]);

      // 解析JSON输出
      const parseResult = parseJSON(response.content);
      if (!parseResult.success || !parseResult.data) {
        logger.error('Failed to parse LLM output as JSON', {
          error: parseResult.error,
          rawOutput: parseResult.rawOutput,
        });
        throw new Error(`Failed to parse LLM output as JSON: ${parseResult.error}`);
      }

      // JSON Schema验证
      const valid = validate(parseResult.data);
      if (!valid) {
        logger.error('LLM output validation failed', {
          errors: validate.errors,
          output: parseResult.data,
          rawOutput: parseResult.rawOutput,
        });
        throw new Error('LLM output does not match expected schema');
      }

      return parseResult.data as LLMScreeningOutput;
    } catch (error) {
      logger.error(`LLM screening failed with model ${modelName}`, {
        error,
        title,
      });
      throw error;
    }
  }

  /**
   * 双模型并行筛选（核心功能）
   */
  async dualModelScreening(
    literatureId: string,
    title: string,
    abstract: string,
    picoCriteria: PicoCriteria,
    inclusionCriteria: string,
    exclusionCriteria: string,
    models: [string, string] = ['deepseek-chat', 'qwen-max'],
    style: ScreeningStyle = 'standard',
    authors?: string,
    journal?: string,
    publicationYear?: number
  ): Promise<DualModelScreeningResult> {
    const [model1, model2] = models;

    try {
      // 并行调用两个模型（使用相同的筛选风格）
      const [result1, result2] = await Promise.all([
        this.screenWithModel(model1, title, abstract, picoCriteria, inclusionCriteria, exclusionCriteria, style, authors, journal, publicationYear),
        this.screenWithModel(model2, title, abstract, picoCriteria, inclusionCriteria, exclusionCriteria, style, authors, journal, publicationYear),
      ]);

      // 冲突检测（只检测conclusion冲突，不检测PICO维度差异）
      const conclusionMatch = result1.conclusion === result2.conclusion;
      const hasConflict = !conclusionMatch;
      
      // 记录PICO维度差异（用于日志，不影响冲突判断）
      const { conflictFields } = this.detectConflict(result1, result2);

      // 最终决策
      let finalDecision: 'include' | 'exclude' | 'pending' = 'pending';
      if (conclusionMatch) {
        // conclusion一致时，采纳结论
        finalDecision = result1.conclusion === 'uncertain' ? 'pending' : result1.conclusion;
      } else {
        // conclusion不一致时，标记为pending（需人工复核）
        finalDecision = 'pending';
      }

      return {
        literatureId,
        deepseek: result1,
        deepseekModel: model1,
        qwen: result2,
        qwenModel: model2,
        hasConflict,
        conflictFields: hasConflict ? conflictFields : undefined,
        finalDecision,
      };
    } catch (error) {
      logger.error('Dual model screening failed', {
        error,
        literatureId,
        title,
      });
      throw error;
    }
  }

  /**
   * 检测两个模型结果是否冲突
   */
  private detectConflict(
    result1: LLMScreeningOutput,
    result2: LLMScreeningOutput
  ): { hasConflict: boolean; conflictFields: string[] } {
    const conflictFields: string[] = [];

    // 检查PICO四个维度
    const dimensions = ['P', 'I', 'C', 'S'] as const;
    for (const dim of dimensions) {
      if (result1.judgment[dim] !== result2.judgment[dim]) {
        conflictFields.push(dim);
      }
    }

    // 检查最终结论
    if (result1.conclusion !== result2.conclusion) {
      conflictFields.push('conclusion');
    }

    return {
      hasConflict: conflictFields.length > 0,
      conflictFields,
    };
  }

  /**
   * 批量筛选文献
   */
  async batchScreening(
    literatures: Array<{
      id: string;
      title: string;
      abstract: string;
    }>,
    picoCriteria: PicoCriteria,
    inclusionCriteria: string,
    exclusionCriteria: string,
    models?: [string, string],
    style: ScreeningStyle = 'standard',
    concurrency: number = 3
  ): Promise<DualModelScreeningResult[]> {
    const results: DualModelScreeningResult[] = [];

    // 分批处理（并发控制）
    for (let i = 0; i < literatures.length; i += concurrency) {
      const batch = literatures.slice(i, i + concurrency);
      const batchResults = await Promise.all(
        batch.map((lit) =>
          this.dualModelScreening(
            lit.id,
            lit.title,
            lit.abstract,
            picoCriteria,
            inclusionCriteria,
            exclusionCriteria,
            models,
            style
          )
        )
      );
      results.push(...batchResults);
    }

    return results;
  }
}

export const llmScreeningService = new LLMScreeningService();

