/**
 * M-QUIZ-01-04: Quiz 题型跨端契约
 *
 * 冻结三种题型的格式、判分规则和展示规则。
 * 所有模块（Validator、Projector、Submit、iOS）共享同一类型定义。
 */

// ═══════════════════════════════════════════════
// TypeScript Union / Enum
// ═══════════════════════════════════════════════

export const VALID_QUESTION_TYPES = ['choice', 'fill', 'judge'] as const;
export type QuestionType = (typeof VALID_QUESTION_TYPES)[number];

export const VALID_QUESTION_TYPES_SET: ReadonlySet<string> = new Set(VALID_QUESTION_TYPES);

// ═══════════════════════════════════════════════
// Validator allowlist
// ═══════════════════════════════════════════════

/** 校验 type 是否合法，非法时返回 false（fail-closed） */
export function isValidQuestionType(type: unknown): type is QuestionType {
  return typeof type === 'string' && VALID_QUESTION_TYPES_SET.has(type);
}

/** assert 版本：非法 type 抛出错误 */
export function assertValidQuestionType(type: unknown): QuestionType {
  if (!isValidQuestionType(type)) {
    throw new Error(
      `Invalid question type "${String(type)}", must be one of: ${VALID_QUESTION_TYPES.join(', ')}`,
    );
  }
  return type;
}

// ═══════════════════════════════════════════════
// Judge 格式
// ═══════════════════════════════════════════════

/** judge 题型合法答案值（传输和存储统一为字符串） */
export const VALID_JUDGE_VALUES = ['true', 'false'] as const;
export type JudgeValue = (typeof VALID_JUDGE_VALUES)[number];

export function isValidJudgeValue(value: unknown): value is JudgeValue {
  return typeof value === 'string' && (value === 'true' || value === 'false');
}

// ═══════════════════════════════════════════════
// Fill 判分规范化
// ═══════════════════════════════════════════════

/**
 * 规范化填空答案用于判分比较。
 *
 * 规则（有明确顺序）：
 * 1. trim 首尾空白
 * 2. 连续空白字符（空格、tab、全角空格）归一化为单个半角空格
 * 3. Unicode NFC 规范化
 * 4. 中文标点 → 英文标点（仅在非语义敏感的上下文中）
 * 5. 大小写 insensitive（ASCII 范围）
 *
 * 不使用：语义相似度、AI 判分、编辑距离。
 */
export function normalizeFillAnswer(raw: string): string {
  let normalized = raw.trim();

  // 连续空白归一化（含全角空格 U+3000）
  normalized = normalized.replace(/[\s　]+/g, ' ');

  // Unicode NFC 规范化
  normalized = normalized.normalize('NFC');

  // 中文标点 → 英文标点（答题场景不区分标点语言）
  normalized = normalized
    .replace(/，/g, ',')   // ，
    .replace(/、/g, ',')   // 、
    .replace(/；/g, ';')   // ；
    .replace(/：/g, ':')   // ：
    .replace(/（/g, '(')   // （
    .replace(/）/g, ')')   // ）
    .replace(/‘/g, "'")   // '
    .replace(/’/g, "'")   // '
    .replace(/“/g, '"')   // "
    .replace(/”/g, '"');  // "

  // 标点相邻空格归一化：移除标点前后空白（填空答案不因标点空格差异判错）
  normalized = normalized.replace(/\s*([,;:()])\s*/g, '$1');

  // ASCII 范围大小写 insensitive
  normalized = normalized.toLowerCase();

  return normalized;
}

/**
 * fill 题型判分：比较用户答案与正确答案的规范化形式。
 * 返回 { isCorrect, normalizedUserAnswer, normalizedCorrectAnswer }
 */
export function gradeFillAnswer(
  userAnswer: string,
  correctAnswer: string,
): { isCorrect: boolean; normalizedUser: string; normalizedCorrect: string } {
  const normalizedUser = normalizeFillAnswer(userAnswer);
  const normalizedCorrect = normalizeFillAnswer(correctAnswer);
  return {
    isCorrect: normalizedUser === normalizedCorrect,
    normalizedUser,
    normalizedCorrect,
  };
}

// ═══════════════════════════════════════════════
// Choice 判分
// ═══════════════════════════════════════════════

/** choice 判分：用户答案 === 正确答案（索引字符串精确匹配） */
export function gradeChoiceAnswer(userAnswer: string, correctAnswer: string): boolean {
  return userAnswer === correctAnswer;
}

// ═══════════════════════════════════════════════
// Judge 判分
// ═══════════════════════════════════════════════

/** judge 判分：用户答案 === 正确答案（"true"/"false" 精确匹配） */
export function gradeJudgeAnswer(userAnswer: string, correctAnswer: string): boolean {
  return userAnswer === correctAnswer;
}

// ═══════════════════════════════════════════════
// 通用判分入口
// ═══════════════════════════════════════════════

/**
 * 根据题型分发判分逻辑。
 * 非法 type → fail-closed（抛出错误）。
 */
export function gradeAnswer(
  type: QuestionType,
  userAnswer: string,
  correctAnswer: string,
): boolean {
  switch (type) {
    case 'choice':
      return gradeChoiceAnswer(userAnswer, correctAnswer);
    case 'fill':
      return gradeFillAnswer(userAnswer, correctAnswer).isCorrect;
    case 'judge':
      return gradeJudgeAnswer(userAnswer, correctAnswer);
    default:
      // fail-closed：永不静默降级
      throw new Error(`Unknown question type for grading: ${type}`);
  }
}
