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

/**
 * M-AI-07-02: Quiz Generation Snapshot Builder
 *
 * 为统一 Job Engine 构建 Quiz 生成输入快照。
 *
 * 契约依据：docs/architecture/m-ai-07-quiz-generation-migration-contract.md §5
 *
 * 职责：
 * 1. 校验 knowledgeBase 存在且属于当前用户
 * 2. 加载知识点列表（限制数量 + 截断内容长度）
 * 3. 规范化 questionCount / questionTypes / difficultyLevel
 * 4. 从 JobDefinitionRegistry 读取 prompt/model 配置
 * 5. 构建版本化、最小化、脱敏的快照
 * 6. 计算稳定的 contentHash
 *
 * 禁止：
 * - 存储 JWT / API Key / Cookie / PII
 * - 存储完整知识库未筛选内容
 * - 存储其他用户内容
 * - 将随机值或时间加入 hash
 *
 * Snapshot Schema（quiz-generation-v1）：
 *   userId, targetType, targetId, submissionId,
 *   knowledgeBaseTitle/Description, knowledgeItems (truncated),
 *   questionCount, questionTypes, difficultyLevel, knowledgePointIds,
 *   promptKey, promptVersion, modelTier,
 *   inputSchemaVersion, outputSchemaVersion, createdAt
 */

const SNAPSHOT_SCHEMA_VERSION = 'quiz-generation-v1';

/** 允许的题型（契约 §4.1） */
const VALID_QUESTION_TYPES = ['choice', 'fill', 'judge'] as const;

/** 允许的难度级别 */
const VALID_DIFFICULTY_LEVELS = ['easy', 'medium', 'hard'] as const;

/** 题目数量范围 */
const MIN_QUESTION_COUNT = 1;
const MAX_QUESTION_COUNT = 50;

/** 知识点内容截断长度 */
const MAX_ITEM_CONTENT_LENGTH = 2000;
/** 知识点最大加载数量 */
const MAX_KNOWLEDGE_ITEMS = 50;

export interface QuizGenerationSnapshot {
  schemaVersion: string;
  snapshot: {
    userId: string;
    targetType: string;
    targetId: string;
    submissionId: string;
    knowledgeBaseTitle: string;
    knowledgeBaseDescription: string | null;
    knowledgeItems: Array<{
      id: string;
      title: string;
      content: string;       // truncated to MAX_ITEM_CONTENT_LENGTH
      summary: string;
    }>;
    questionCount: number;
    questionTypes: string[];
    difficultyLevel: string;
    knowledgePointIds: string[];
    promptKey: string;
    promptVersion: string;
    modelTier: string;
    inputSchemaVersion: string;
    outputSchemaVersion: string;
    createdAt: string;       // ISO8601 normalized to second
  };
}

export interface QuizGenerationSnapshotInput {
  userId: string;
  targetType: string;
  targetId: string;
  submissionId: string;
  questionCount?: number;
  questionTypes?: string[];
  difficultyLevel?: string;
  knowledgePointIds?: string[];
}

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

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

  /**
   * 构建 Quiz 生成输入快照。
   *
   * @param input - 快照构建参数
   * @returns 版本化、脱敏的快照对象
   *
   * @throws NotFoundException  knowledgeBase 不存在
   * @throws ForbiddenException knowledgeBase 不属于当前用户
   * @throws BadRequestException 参数非法
   */
  async build(input: QuizGenerationSnapshotInput): Promise<QuizGenerationSnapshot> {
    // 1. 从 Registry 读取配置（单一事实来源）
    const def = this.registry.get('quiz_generation');

    // 2. 校验 targetType
    if (input.targetType !== 'knowledge_base') {
      throw new BadRequestException(
        `quiz_generation requires targetType="knowledge_base", got "${input.targetType}"`,
      );
    }

    // 3. 规范化参数（在 DB 查询前，fail-fast）
    const questionCount = this.normalizeQuestionCount(input.questionCount);
    const questionTypes = this.normalizeQuestionTypes(input.questionTypes);
    const difficultyLevel = this.normalizeDifficultyLevel(input.difficultyLevel);
    const knowledgePointIds = input.knowledgePointIds ?? [];

    // 4. 加载并校验 knowledgeBase
    const kb = await this.prisma.knowledgeBase.findUnique({
      where: { id: input.targetId },
    });
    if (!kb || kb.deletedAt) {
      throw new NotFoundException(`KnowledgeBase ${input.targetId} not found`);
    }
    if (kb.userId !== input.userId) {
      throw new ForbiddenException(
        `KnowledgeBase ${input.targetId} does not belong to user ${input.userId}`,
      );
    }

    // 5. 加载知识点（限制数量 + 截断内容）
    const where: any = {
      knowledgeBaseId: input.targetId,
      deletedAt: null,
      status: 'active',
    };
    if (knowledgePointIds.length > 0) {
      where.id = { in: knowledgePointIds };
    }

    const items = await this.prisma.knowledgeItem.findMany({
      where,
      select: {
        id: true,
        title: true,
        content: true,
        summary: true,
      },
      orderBy: { orderIndex: 'asc' },
      take: MAX_KNOWLEDGE_ITEMS,
    });

    if (items.length === 0) {
      throw new BadRequestException(
        'Knowledge base has no active knowledge items',
      );
    }

    // 截断内容（最小化快照）
    const knowledgeItems = items.map((item) => ({
      id: item.id,
      title: item.title,
      content: (item.content ?? '').slice(0, MAX_ITEM_CONTENT_LENGTH),
      summary: item.summary ?? '',
    }));

    // 6. 构建快照
    const now = new Date();
    const snapshot: QuizGenerationSnapshot = {
      schemaVersion: SNAPSHOT_SCHEMA_VERSION,
      snapshot: {
        userId: input.userId,
        targetType: input.targetType,
        targetId: input.targetId,
        submissionId: input.submissionId,
        knowledgeBaseTitle: kb.title,
        knowledgeBaseDescription: kb.description ?? null,
        knowledgeItems,
        questionCount,
        questionTypes,
        difficultyLevel,
        knowledgePointIds,
        promptKey: def.prompt.promptKey,
        promptVersion: def.prompt.promptVersion,
        modelTier: def.model.modelTier,
        inputSchemaVersion: SNAPSHOT_SCHEMA_VERSION,
        outputSchemaVersion: def.output.schemaVersion,
        createdAt: now.toISOString().replace(/\.\d{3}Z$/, 'Z'),
      },
    };

    this.logger.log(
      `Built Quiz Generation snapshot for kb=${input.targetId} ` +
      `userId=${input.userId} items=${knowledgeItems.length} ` +
      `questionCount=${questionCount} types=${questionTypes.join(',')} ` +
      `difficulty=${difficultyLevel}`,
    );

    return snapshot;
  }

  /**
   * 计算快照的 contentHash（SHA256 前 16 字符）。
   */
  computeHash(snapshot: QuizGenerationSnapshot): 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 ──

  private normalizeQuestionCount(raw?: number): number {
    if (raw == null) return 5; // default
    if (!Number.isInteger(raw) || raw < MIN_QUESTION_COUNT) {
      throw new BadRequestException(
        `questionCount must be >= ${MIN_QUESTION_COUNT}, got ${raw}`,
      );
    }
    if (raw > MAX_QUESTION_COUNT) {
      throw new BadRequestException(
        `questionCount must be <= ${MAX_QUESTION_COUNT}, got ${raw}`,
      );
    }
    return raw;
  }

  private normalizeQuestionTypes(raw?: string[]): string[] {
    if (!raw || raw.length === 0) {
      return ['choice', 'fill', 'judge']; // default: all types
    }
    const invalid = raw.filter((t) => !(VALID_QUESTION_TYPES as readonly string[]).includes(t));
    if (invalid.length > 0) {
      throw new BadRequestException(
        `Invalid question types: ${invalid.join(', ')}. ` +
        `Allowed: ${VALID_QUESTION_TYPES.join(', ')}`,
      );
    }
    return [...new Set(raw)]; // dedup
  }

  private normalizeDifficultyLevel(raw?: string): string {
    if (!raw) return 'medium';
    if (!(VALID_DIFFICULTY_LEVELS as readonly string[]).includes(raw)) {
      throw new BadRequestException(
        `Invalid difficultyLevel "${raw}". ` +
        `Allowed: ${VALID_DIFFICULTY_LEVELS.join(', ')}`,
      );
    }
    return raw;
  }
}
