import { Injectable, Logger } from '@nestjs/common';
import type { ReviewCardGenerationResult } from '../ai/prompts/schemas/review-card-generation.schema';
import {
  BusinessValidationError,
  ReferenceValidationError,
} from './active-recall-validator';

export { BusinessValidationError, ReferenceValidationError };

/**
 * M-AI-06-04: ReviewCard Generation Business Validator
 *
 * 验证 AI 输出符合业务约束（契约 §5.2 验证规则表）。
 *
 * 检查项：
 * - cards 数组非空，数量在 [1, 20]
 * - totalCount 与 cards.length 一致
 * - frontText 非空，长度 ≤ 500
 * - backText 非空，长度 ≤ 1000
 * - difficulty 合法枚举 {easy, medium, hard}
 * - tags 数组每项 ≤ 50 字符，最多 5 个
 * - 禁止空卡片（frontText + backText 都为空）
 * - 禁止完全重复卡片（相同 frontText + backText）
 * - 禁止模型指令或 Markdown 代码块
 * - 禁止超大文本（frontText > 2000 或 backText > 5000）
 */

const VALID_DIFFICULTIES = ['easy', 'medium', 'hard'] as const;

/** 检测 markdown 代码块包装 */
const CODE_BLOCK_PATTERN = /```(?:json|javascript|js|python)?[\s\S]*?```/;
/** 检测模型指令痕迹 */
const MODEL_INSTRUCTION_PATTERNS = [
  /^here\s+(is|are)\s+(the|your)\s/i,
  /^the\s+(following|cards|review)\s/i,
  /^(certainly|sure|of course)[,;!\s]/i,
  /^(I|we)\s+(hope|trust|believe|think)\s/i,
  /^请分析/,
  /^根据上文/,
];

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

  /**
   * 验证业务规则。
   *
   * @param output - AiGatewayService 解析后的输出（已通过 Zod schema.parse）
   * @throws BusinessValidationError 业务规则违反
   */
  validate(output: ReviewCardGenerationResult): void {
    const violations: string[] = [];

    // ── cards 数组 ──
    if (!Array.isArray(output.cards)) {
      violations.push('cards must be an array');
      throw new BusinessValidationError('Business validation failed', violations);
    }

    if (output.cards.length === 0) {
      violations.push('cards must not be empty');
    }
    if (output.cards.length > 20) {
      violations.push(`cards count ${output.cards.length} exceeds max 20`);
    }

    // ── totalCount 一致性 ──
    if (typeof output.totalCount !== 'number' || !Number.isInteger(output.totalCount)) {
      violations.push('totalCount must be an integer');
    } else if (output.totalCount !== output.cards.length) {
      violations.push(
        `totalCount ${output.totalCount} does not match cards.length ${output.cards.length}`,
      );
    }

    // ── 逐卡片验证 ──
    const seenCards = new Set<string>();

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

      // frontText 非空
      if (!card.frontText || typeof card.frontText !== 'string' || card.frontText.trim().length === 0) {
        violations.push(`cards[${i}].frontText is required and must be non-empty`);
      } else {
        // frontText 长度
        if (card.frontText.length > 500) {
          violations.push(`cards[${i}].frontText length ${card.frontText.length} exceeds 500`);
        }
        // 禁止超大文本（Zod max 后二次检查）
        if (card.frontText.length > 2000) {
          violations.push(`cards[${i}].frontText excessively large (${card.frontText.length} chars)`);
        }
      }

      // backText 非空
      if (!card.backText || typeof card.backText !== 'string' || card.backText.trim().length === 0) {
        violations.push(`cards[${i}].backText is required and must be non-empty`);
      } else {
        // backText 长度
        if (card.backText.length > 1000) {
          violations.push(`cards[${i}].backText length ${card.backText.length} exceeds 1000`);
        }
        // 禁止超大文本
        if (card.backText.length > 5000) {
          violations.push(`cards[${i}].backText excessively large (${card.backText.length} chars)`);
        }
      }

      // 禁止空卡片
      const frontEmpty = !card.frontText || card.frontText.trim().length === 0;
      const backEmpty = !card.backText || card.backText.trim().length === 0;
      if (frontEmpty && backEmpty) {
        violations.push(`cards[${i}] is empty (both frontText and backText are blank)`);
      }

      // difficulty 枚举
      if (card.difficulty && !VALID_DIFFICULTIES.includes(card.difficulty as any)) {
        violations.push(
          `cards[${i}].difficulty "${card.difficulty}" invalid, must be one of: ${VALID_DIFFICULTIES.join(', ')}`,
        );
      }

      // tags 验证
      if (card.tags && Array.isArray(card.tags)) {
        if (card.tags.length > 5) {
          violations.push(`cards[${i}].tags count ${card.tags.length} exceeds max 5`);
        }
        for (let j = 0; j < card.tags.length; j++) {
          if (typeof card.tags[j] !== 'string') {
            violations.push(`cards[${i}].tags[${j}] must be a string`);
          } else if (card.tags[j].length > 50) {
            violations.push(`cards[${i}].tags[${j}] length ${card.tags[j].length} exceeds 50`);
          }
        }
      }

      // 禁止完全重复卡片
      const dedupeKey = `${card.frontText || ''}::${card.backText || ''}`;
      if (dedupeKey !== '::') {
        if (seenCards.has(dedupeKey)) {
          violations.push(`cards[${i}] is a duplicate of another card`);
        }
        seenCards.add(dedupeKey);
      }

      // 禁止模型指令或 Markdown 代码块
      this.checkModelArtifacts(card.frontText, `cards[${i}].frontText`, violations);
      this.checkModelArtifacts(card.backText, `cards[${i}].backText`, violations);
    }

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

    this.logger.log(
      `ReviewCard business validation passed: ${output.cards.length} cards, ` +
      `difficulties=${output.cards.map(c => c.difficulty || 'medium').join(',')}`,
    );
  }

  // ── Private Helpers ──

  private checkModelArtifacts(
    text: string | undefined,
    fieldName: string,
    violations: string[],
  ): void {
    if (!text || typeof text !== 'string') return;

    if (CODE_BLOCK_PATTERN.test(text)) {
      violations.push(`${fieldName} contains code block — likely raw model output`);
    }
    for (const pattern of MODEL_INSTRUCTION_PATTERNS) {
      if (pattern.test(text.trim())) {
        violations.push(
          `${fieldName} contains model instruction pattern: "${text.substring(0, 60)}..."`,
        );
        break;
      }
    }
  }
}
