import { Injectable, Logger, NotFoundException, ForbiddenException } from '@nestjs/common';
import * as crypto from 'crypto';
import { PrismaService } from '../../infrastructure/database/prisma.service';
import { JobDefinitionRegistry } from './job-definition-registry';

/**
 * M-AI-05-02: Feynman Snapshot Builder
 *
 * 为统一 Job Engine 构建 Feynman 评估输入快照。
 *
 * 契约依据：docs/architecture/m-ai-05-feynman-migration-contract.md §3
 *
 * 职责：
 * 1. 加载 KnowledgeItem 并验证所有权（item.userId === userId）
 * 2. 加载关联知识库信息和参考材料摘要
 * 3. 从 JobDefinitionRegistry 读取 prompt/model 配置（单一事实来源）
 * 4. 构建版本化、最小化、脱敏的快照
 * 5. 计算 contentHash（SHA256 前 16 字符）
 *
 * 禁止：
 * - 存储 JWT / API Key / Cookie / DB 连接 / PII
 * - 存储完整用户画像
 * - 硬编码 prompt/model 配置（应从 Definition 读取）
 * - 将每次生成时间加入 hash 输入
 *
 * Snapshot Schema（feynman-evaluation-v1）：
 *   userId, knowledgeItemId, knowledgeItemTitle, knowledgeItemContent,
 *   userExplanation, submissionId, knowledgeBaseId,
 *   referenceMaterials (summary only), promptKey, promptVersion,
 *   modelTier, inputSchemaVersion, outputSchemaVersion, createdAt
 */

const SNAPSHOT_SCHEMA_VERSION = 'feynman-evaluation-v1';

export interface FeynmanSnapshot {
  schemaVersion: string;
  snapshot: {
    userId: string;
    knowledgeItemId: string;
    knowledgeItemTitle: string;
    knowledgeItemContent: string;
    userExplanation: string;
    submissionId: string;
    knowledgeBaseId: string;
    referenceMaterials: Array<{
      id: string;
      title: string;
      summary: string;
    }>;
    promptKey: string;
    promptVersion: string;
    modelTier: string;
    inputSchemaVersion: string;
    outputSchemaVersion: string;
    createdAt: string;  // ISO8601 normalized to second
  };
}

/**
 * Feynman Snapshot Build 输入参数。
 *
 * knowledgeItemId 为必填 — 若当前请求体不含此字段，
 * 调用方（M-AI-05-05）需在路由层通过标题+内容匹配或要求客户端传入。
 */
export interface FeynmanSnapshotInput {
  userId: string;
  knowledgeItemId: string;
  knowledgeItemTitle: string;
  knowledgeItemContent: string;
  userExplanation: string;
  submissionId: string;
  sessionId?: string;
  answerId?: string;
}

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

  constructor(
    private readonly prisma: PrismaService,
    private readonly registry: JobDefinitionRegistry,
  ) {}

  /**
   * 构建 Feynman 评估输入快照。
   *
   * prompt/model 配置从 JobDefinitionRegistry 读取（单一事实来源），
   * 避免与 feynman-job-definition.ts 重复硬编码。
   *
   * @param input - Feynman 快照构建参数
   * @returns 版本化、脱敏的快照对象
   *
   * @throws NotFoundException   KnowledgeItem 不存在
   * @throws ForbiddenException  KnowledgeItem 不属于当前用户
   */
  async build(input: FeynmanSnapshotInput): Promise<FeynmanSnapshot> {
    // 1. 从 Registry 读取配置（单一事实来源）
    const def = this.registry.get('feynman_evaluation');

    // 2. 加载 KnowledgeItem 并验证所有权
    const knowledgeItem = await this.prisma.knowledgeItem.findUnique({
      where: { id: input.knowledgeItemId },
    });
    if (!knowledgeItem) {
      throw new NotFoundException(
        `KnowledgeItem ${input.knowledgeItemId} not found`,
      );
    }
    if (knowledgeItem.userId !== input.userId) {
      throw new ForbiddenException(
        `KnowledgeItem ${input.knowledgeItemId} does not belong to user ${input.userId}`,
      );
    }

    // 3. 加载参考材料摘要（同一知识库内最多 5 条关联知识点，仅取摘要不取全文）
    const referenceMaterials = await this.loadReferenceMaterials(
      knowledgeItem.knowledgeBaseId,
    );

    // 4. 构建快照（仅包含模型调用所需最小字段）
    //    prompt/model 值全部来自 Definition
    const now = new Date();
    const snapshot: FeynmanSnapshot = {
      schemaVersion: SNAPSHOT_SCHEMA_VERSION,
      snapshot: {
        userId: input.userId,
        knowledgeItemId: input.knowledgeItemId,
        knowledgeItemTitle: input.knowledgeItemTitle,
        knowledgeItemContent: input.knowledgeItemContent,
        userExplanation: input.userExplanation,
        submissionId: input.submissionId,
        knowledgeBaseId: knowledgeItem.knowledgeBaseId,
        referenceMaterials,
        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 Feynman snapshot for knowledgeItem=${input.knowledgeItemId} ` +
      `userId=${input.userId} submissionId=${input.submissionId} ` +
      `promptKey=${def.prompt.promptKey}`,
    );

    return snapshot;
  }

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

  // ── Private Helpers ──

  /**
   * 加载参考材料：同一知识库内的活跃知识点摘要（排除当前项，最多 5 条）。
   *
   * 只取 id/title/summary，不加载完整 content 以防止快照膨胀。
   */
  private async loadReferenceMaterials(
    knowledgeBaseId: string,
  ): Promise<Array<{ id: string; title: string; summary: string }>> {
    const items = await this.prisma.knowledgeItem.findMany({
      where: {
        knowledgeBaseId,
        status: 'active',
        deletedAt: null,
      },
      select: {
        id: true,
        title: true,
        summary: true,
      },
      orderBy: { orderIndex: 'asc' },
      take: 5,
    });

    return items.map((item) => ({
      id: item.id,
      title: item.title,
      summary: item.summary ?? '',
    }));
  }
}
