import { Injectable, Logger } from '@nestjs/common';
import * as crypto from 'crypto';
import { JobDefinitionRegistry } from './job-definition-registry';

/**
 * M-AI-06-03: ReviewCard Generation Snapshot Builder
 *
 * 为 ReviewCard Generation Child Job 构建不可变输入快照。
 *
 * 契约依据：docs/architecture/m-ai-06-review-card-child-job-contract.md §4
 *
 * 职责：
 * 1. 接收已验证的 Feynman 输出（非模型原始响应）
 * 2. 从 JobDefinitionRegistry 读取 prompt/model 配置（单一事实来源）
 * 3. 构建版本化、最小化、脱敏的快照
 * 4. 计算稳定的 contentHash（相同输入→相同 hash）
 *
 * 禁止：
 * - 再次读取数据库（所有数据由调用方传入）
 * - 再次读取未经验证的模型原始响应
 * - 存储 JWT / API Key / Cookie / DB 连接 / PII
 * - 将随机值或当前毫秒时间加入 hash
 * - 硬编码 prompt/model 配置（应从 Definition 读取）
 *
 * Snapshot Schema（review-card-generation-v1）：
 *   userId, parentJobId, sourceResultId, knowledgeItemId, knowledgeBaseId,
 *   title, summary, strengths, weaknesses, cardCount,
 *   promptKey, promptVersion, modelTier,
 *   inputSchemaVersion, outputSchemaVersion, createdAt
 */

const SNAPSHOT_SCHEMA_VERSION = 'review-card-generation-v1';

/**
 * ReviewCard Generation 快照（版本化包装）
 */
export interface ReviewCardGenerationSnapshot {
  /** Snapshot Schema 版本 */
  schemaVersion: string;
  /** 快照内容（不可变，全部进入 AiJobSnapshot.content） */
  snapshot: {
    /** 用户 ID（从 Parent Job 继承，不得在 Child Worker 中重新决定） */
    userId: string;
    /** 父 Feynman Job ID */
    parentJobId: string;
    /** 来源 Feynman 评估结果 ID（fe_<parentJobId>） */
    sourceResultId: string;
    /** 知识点 ID（从 Parent Snapshot 读取） */
    knowledgeItemId: string;
    /** 知识库 ID（从 Parent Snapshot 读取） */
    knowledgeBaseId: string;
    /** 评估摘要截断（用作卡片生成标题，max 80 字符） */
    title: string;
    /** 拼接摘要文本（summary + strengths + weaknesses） */
    summary: string;
    /** 掌握项（从 Feynman validatedOutput） */
    strengths: string[];
    /** 薄弱项（从 Feynman validatedOutput，用于决定 cardCount） */
    weaknesses: string[];
    /** 目标卡片数量（冻结规则：min(3, max(1, weaknesses.length || 1))） */
    cardCount: number;
    /** 冻结的 Prompt Key（从 Definition） */
    promptKey: string;
    /** 冻结的 Prompt Version（从 Definition） */
    promptVersion: string;
    /** 模型层级（从 Definition，冻结为 cheap） */
    modelTier: string;
    /** 输入 Schema 版本（与 Definition.input.schemaVersion 一致） */
    inputSchemaVersion: string;
    /** 输出 Schema 版本（与 Definition.output.schemaVersion 一致） */
    outputSchemaVersion: string;
    /** 快照创建时间（ISO8601 归一化到秒，保证稳定 hash） */
    createdAt: string;
  };
}

/**
 * ReviewCard Generation Snapshot Build 输入参数。
 *
 * 所有字段来源于已验证的 Feynman 输出，非模型原始响应。
 */
export interface ReviewCardGenerationSnapshotInput {
  /** 用户 ID */
  userId: string;
  /** 父 Feynman Job ID */
  parentJobId: string;
  /** 来源 Feynman 评估结果 ID */
  sourceResultId: string;
  /** 知识点 ID */
  knowledgeItemId: string;
  /** 知识库 ID */
  knowledgeBaseId: string;
  /** 评估摘要（用于 title 和 summary 拼接） */
  summary: string;
  /** 掌握项（字符串数组） */
  strengths: string[];
  /** 薄弱项（字符串数组） */
  weaknesses: string[];
}

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

  constructor(private readonly registry: JobDefinitionRegistry) {}

  /**
   * 构建 ReviewCard Generation 输入快照。
   *
   * prompt/model 配置从 JobDefinitionRegistry 读取（单一事实来源），
   * 避免与 review-card-generation-job-definition.ts 重复硬编码。
   *
   * 稳定性保证：
   * - createdAt 归一化到秒（截断毫秒）
   * - cardCount 确定性计算（无随机性）
   * - 不包含时间戳或随机值
   *
   * @param input - 快照构建参数（来源于已验证的 Feynman 输出）
   * @returns 版本化、脱敏的快照对象
   */
  build(input: ReviewCardGenerationSnapshotInput): ReviewCardGenerationSnapshot {
    // 1. 从 Registry 读取配置（单一事实来源）
    const def = this.registry.get('review_card_generation');

    // 2. 确定性计算 cardCount（冻结规则：min(3, max(1, weaknesses.length || 1))）
    const cardCount = this.computeCardCount(input.weaknesses);

    // 3. 构建 title（摘要截断 80 字符）和 summary（拼接）
    const title = input.summary
      ? input.summary.slice(0, 80)
      : 'AI 分析结果';
    const summary = this.buildSummaryText(input.summary, input.strengths, input.weaknesses);

    // 4. 构建快照（仅包含模型调用所需最小字段）
    //    prompt/model 值全部来自 Definition
    const now = new Date();
    const snapshot: ReviewCardGenerationSnapshot = {
      schemaVersion: SNAPSHOT_SCHEMA_VERSION,
      snapshot: {
        userId: input.userId,
        parentJobId: input.parentJobId,
        sourceResultId: input.sourceResultId,
        knowledgeItemId: input.knowledgeItemId,
        knowledgeBaseId: input.knowledgeBaseId,
        title,
        summary,
        strengths: input.strengths ?? [],
        weaknesses: input.weaknesses ?? [],
        cardCount,
        promptKey: def.prompt.promptKey,
        promptVersion: def.prompt.promptVersion,
        modelTier: def.model.modelTier,
        inputSchemaVersion: SNAPSHOT_SCHEMA_VERSION,
        outputSchemaVersion: def.output.schemaVersion,
        // 归一化到秒（截断毫秒以保证相同输入→相同hash）
        createdAt: now.toISOString().replace(/\.\d{3}Z$/, 'Z'),
      },
    };

    this.logger.log(
      `Built ReviewCard Generation snapshot for parentJobId=${input.parentJobId} ` +
      `userId=${input.userId} cardCount=${cardCount} ` +
      `promptKey=${def.prompt.promptKey}`,
    );

    return snapshot;
  }

  /**
   * 计算快照的 contentHash（SHA256 前 16 字符）。
   *
   * 相同输入 → 相同输出；用于幂等比较和审计追溯。
   * 使用稳定序列化（JSON 紧凑格式，字段按字母序排序）。
   */
  computeHash(snapshot: ReviewCardGenerationSnapshot): string {
    const serialized = JSON.stringify(
      snapshot.snapshot,
      Object.keys(snapshot.snapshot).sort(),
    );
    return crypto
      .createHash('sha256')
      .update(serialized)
      .digest('hex')
      .substring(0, 16);
  }

  // ── Private Helpers ──

  /**
   * 确定性计算卡片数量。
   *
   * 规则（契约 §1.4）：
   *   Math.min(3, Math.max(1, weaknesses.length || 1))
   *
   * 范围 [1, 3]。无 weaknesses 时生成 1 张卡片。
   */
  private computeCardCount(weaknesses: string[]): number {
    const count = weaknesses?.length || 0;
    return Math.min(3, Math.max(1, count));
  }

  /**
   * 拼接 summary 文本（用于模型 user message）。
   *
   * 格式："摘要：{summary}\n\n掌握点：{strengths}\n\n薄弱点：{weaknesses}"
   * 与 Legacy ReviewCardSubscriber 的拼接方式一致（review-card.subscriber.ts:36-37）。
   */
  private buildSummaryText(
    summary: string,
    strengths: string[],
    weaknesses: string[],
  ): string {
    const s = (strengths ?? []).join('；');
    const w = (weaknesses ?? []).join('；');
    return `摘要：${summary || ''}\n\n掌握点：${s}\n\n薄弱点：${w}`;
  }
}
