import { isValidQuestionType, assertValidQuestionType, normalizeFillAnswer, gradeFillAnswer, gradeChoiceAnswer, gradeJudgeAnswer, gradeAnswer, VALID_QUESTION_TYPES } from './quiz-type.contract';

describe('Quiz 题型跨端契约 (quiz-type.contract)', () => {
  // ═══════════════════════════════════════════════
  // isValidQuestionType
  // ═══════════════════════════════════════════════

  describe('isValidQuestionType', () => {
    it.each(VALID_QUESTION_TYPES)('接受合法 type: %s', (t) => {
      expect(isValidQuestionType(t)).toBe(true);
    });

    it.each(['multi_choice', 'short_answer', '', null, undefined, 123])('拒绝非法 type: %s', (t) => {
      expect(isValidQuestionType(t)).toBe(false);
    });
  });

  describe('assertValidQuestionType', () => {
    it('合法 type 返回原值', () => {
      expect(assertValidQuestionType('choice')).toBe('choice');
    });

    it('非法 type 抛出错误', () => {
      expect(() => assertValidQuestionType('essay')).toThrow('Invalid question type');
    });
  });

  // ═══════════════════════════════════════════════
  // normalizeFillAnswer
  // ═══════════════════════════════════════════════

  describe('normalizeFillAnswer', () => {
    it('去除首尾空白', () => {
      expect(normalizeFillAnswer('  氧气  ')).toBe('氧气');
    });

    it('连续空白归一化为单个空格', () => {
      expect(normalizeFillAnswer('光合  作用')).toBe('光合 作用');
    });

    it('全角空格归一化', () => {
      expect(normalizeFillAnswer('光合　作用')).toBe('光合 作用');
    });

    it('Unicode NFC 规范化', () => {
      // é 的分解形式 (U+0065 U+0301) vs 组合形式 (U+00E9)
      const decomposed = 'étudier';
      const composed = 'étudier';
      expect(normalizeFillAnswer(decomposed)).toBe(normalizeFillAnswer(composed));
    });

    it('中文标点转英文标点（含标点邻接空格归一化）', () => {
      expect(normalizeFillAnswer('A，B、C；D：E（F）G')).toBe('a,b,c;d:e(f)g');
    });

    it('大小写 insensitive', () => {
      expect(normalizeFillAnswer('Oxygen')).toBe('oxygen');
    });

    it('复合场景：空白+标点+大小写', () => {
      const raw = '  光合作用,  Water  ；氧气  ';
      expect(normalizeFillAnswer(raw)).toBe('光合作用,water;氧气');
    });
  });

  // ═══════════════════════════════════════════════
  // gradeFillAnswer
  // ═══════════════════════════════════════════════

  describe('gradeFillAnswer', () => {
    it('精确匹配判对', () => {
      const r = gradeFillAnswer('氧气', '氧气');
      expect(r.isCorrect).toBe(true);
    });

    it('大小写差异判对', () => {
      const r = gradeFillAnswer('water', 'Water');
      expect(r.isCorrect).toBe(true);
    });

    it('空白差异判对', () => {
      const r = gradeFillAnswer('  氧气  ', '氧气');
      expect(r.isCorrect).toBe(true);
    });

    it('中文标点与英文标点差异判对', () => {
      const r = gradeFillAnswer('A，B', 'A,B');
      expect(r.isCorrect).toBe(true);
    });

    it('语义不同判错', () => {
      const r = gradeFillAnswer('氮气', '氧气');
      expect(r.isCorrect).toBe(false);
    });

    it('返回规范化后的值', () => {
      const r = gradeFillAnswer('  Water  ', 'water');
      expect(r.normalizedUser).toBe('water');
      expect(r.normalizedCorrect).toBe('water');
    });
  });

  // ═══════════════════════════════════════════════
  // gradeChoiceAnswer / gradeJudgeAnswer
  // ═══════════════════════════════════════════════

  describe('gradeChoiceAnswer', () => {
    it('精确匹配', () => {
      expect(gradeChoiceAnswer('0', '0')).toBe(true);
      expect(gradeChoiceAnswer('0', '1')).toBe(false);
    });
  });

  describe('gradeJudgeAnswer', () => {
    it('"true"/"false" 精确匹配', () => {
      expect(gradeJudgeAnswer('true', 'true')).toBe(true);
      expect(gradeJudgeAnswer('true', 'false')).toBe(false);
    });
  });

  // ═══════════════════════════════════════════════
  // gradeAnswer 通用入口
  // ═══════════════════════════════════════════════

  describe('gradeAnswer', () => {
    it('choice 分发', () => {
      expect(gradeAnswer('choice', '0', '0')).toBe(true);
    });

    it('fill 分发（规范化）', () => {
      expect(gradeAnswer('fill', '  Oxygen  ', 'oxygen')).toBe(true);
    });

    it('judge 分发', () => {
      expect(gradeAnswer('judge', 'true', 'true')).toBe(true);
    });

    it('非法 type → 抛出错误 (fail-closed)', () => {
      expect(() => gradeAnswer('essay' as any, '', '')).toThrow('Unknown question type for grading');
    });
  });
});
