import { Injectable, Logger } from '@nestjs/common';
import type { QuizGenerationResult } from '../ai/prompts/schemas/quiz-generation.schema';
import { BusinessValidationError, ReferenceValidationError } from './active-recall-validator';
import { VALID_QUESTION_TYPES, isValidJudgeValue } from '../quiz/quiz-type.contract';

export { BusinessValidationError, ReferenceValidationError };

/**
 * M-AI-07-03 / M-QUIZ-01-04: Quiz Generation Business Validator
 *
 * 验证 AI 输出符合业务约束（使用共享契约 quiz-type.contract）。
 */
const CODE_BLOCK_PATTERN = /```(?:json|javascript|js|python)?[\s\S]*?```/;
const MODEL_INSTRUCTION_PATTERNS = [
  /^here\s+(is|are)\s+(the|your)\s/i,
  /^(certainly|sure|of course)[,;!\s]/i,
  /^请根据/,
  /^以下是/,
];

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

  validate(output: QuizGenerationResult): void {
    const violations: string[] = [];

    // quizTitle
    if (!output.quizTitle || typeof output.quizTitle !== 'string' || output.quizTitle.trim().length === 0) {
      violations.push('quizTitle is required and must be non-empty');
    } else if (output.quizTitle.length > 255) {
      violations.push(`quizTitle length ${output.quizTitle.length} exceeds 255`);
    }

    // questions array
    if (!Array.isArray(output.questions)) {
      violations.push('questions must be an array');
      throw new BusinessValidationError('Business validation failed', violations);
    }
    if (output.questions.length === 0) {
      violations.push('questions must not be empty');
    }
    if (output.questions.length > 50) {
      violations.push(`questions count ${output.questions.length} exceeds max 50`);
    }

    const seenStems = new Set<string>();

    for (let i = 0; i < output.questions.length; i++) {
      const q = output.questions[i];

      // type（M-QUIZ-01-04: 使用共享契约枚举）
      if (!q.type || !VALID_QUESTION_TYPES.includes(q.type as any)) {
        violations.push(`questions[${i}].type "${q.type}" invalid, must be: ${VALID_QUESTION_TYPES.join(', ')}`);
      }

      // stem
      if (!q.stem || typeof q.stem !== 'string' || q.stem.trim().length === 0) {
        violations.push(`questions[${i}].stem is required`);
      } else if (q.stem.length > 2000) {
        violations.push(`questions[${i}].stem length ${q.stem.length} exceeds 2000`);
      }

      // answer
      if (!q.answer || typeof q.answer !== 'string' || q.answer.trim().length === 0) {
        violations.push(`questions[${i}].answer is required`);
      }

      // explanation
      if (!q.explanation || typeof q.explanation !== 'string' || q.explanation.trim().length === 0) {
        violations.push(`questions[${i}].explanation is required`);
      }

      // per-type validation
      if (q.type === 'choice') {
        if (!Array.isArray(q.options) || q.options.length < 2) {
          violations.push(`questions[${i}] choice requires >= 2 options`);
        } else {
          // answer must be a valid option index
          const idx = parseInt(q.answer, 10);
          if (isNaN(idx) || idx < 0 || idx >= q.options.length) {
            violations.push(`questions[${i}] answer "${q.answer}" is not a valid option index (0-${q.options.length - 1})`);
          }
          // duplicate options
          const uniqueOpts = new Set(q.options);
          if (uniqueOpts.size !== q.options.length) {
            violations.push(`questions[${i}] has duplicate options`);
          }
        }
      }

      if (q.type === 'judge') {
        // M-QUIZ-01-04: 使用共享契约校验 judge 值
        if (!isValidJudgeValue(q.answer)) {
          violations.push(`questions[${i}] judge answer must be "true" or "false" (string), got "${q.answer}" (${typeof q.answer})`);
        }
      }

      // duplicate stem
      if (q.stem) {
        const dedupeKey = q.stem.trim().toLowerCase();
        if (seenStems.has(dedupeKey)) {
          violations.push(`questions[${i}] stem is duplicate`);
        }
        seenStems.add(dedupeKey);
      }

      // model artifacts
      this.checkModelArtifacts(q.stem, `questions[${i}].stem`, violations);
      this.checkModelArtifacts(q.explanation, `questions[${i}].explanation`, violations);
    }

    if (violations.length > 0) {
      this.logger.warn(`Quiz validation failed: ${violations.length} violation(s): ${violations.join('; ')}`);
      throw new BusinessValidationError(`Business validation failed: ${violations.length} violation(s)`, violations);
    }

    this.logger.log(`Quiz validation passed: ${output.questions.length} questions`);
  }

  private checkModelArtifacts(text: string | undefined, fieldName: string, violations: string[]): void {
    if (!text) return;
    if (CODE_BLOCK_PATTERN.test(text)) {
      violations.push(`${fieldName} contains code block`);
    }
    for (const pattern of MODEL_INSTRUCTION_PATTERNS) {
      if (pattern.test(text.trim())) {
        violations.push(`${fieldName} contains model instruction`);
        break;
      }
    }
  }
}
