/**
 * Tests for src/utils/scriptDetector.ts
 *
 * Run with:
 *   npx tsx --test tests/utils/scriptDetector.test.ts
 *
 * Or in package.json scripts:
 *   "test:quality": "tsx --test tests/utils/scriptDetector.test.ts"
 */
import { describe, it } from 'node:test';
import { strict as assert } from 'node:assert';

import {
  getScript,
  isArabicScriptLang,
  evaluateChunk,
  evaluateDocument,
  detectArabicVariant,
  lengthCheck,
  patternCheck,
} from '../../src/utils/scriptDetector.ts';

describe('getScript', () => {
  it('maps cyrillic languages', () => {
    assert.equal(getScript('ru'), 'cyrillic');
    assert.equal(getScript('uk'), 'cyrillic');
    assert.equal(getScript('be'), 'cyrillic');
  });

  it('maps latin languages', () => {
    assert.equal(getScript('en'), 'latin');
    assert.equal(getScript('fr'), 'latin');
    assert.equal(getScript('de'), 'latin');
    assert.equal(getScript('vi'), 'latin');
  });

  it('maps CJK languages', () => {
    assert.equal(getScript('zh'), 'cjk');
    assert.equal(getScript('zh-cn'), 'cjk');
  });

  it('maps Japanese to hiragana_katakana', () => {
    assert.equal(getScript('ja'), 'hiragana_katakana');
  });

  it('maps Korean to hangul', () => {
    assert.equal(getScript('ko'), 'hangul');
  });

  it('maps Yiddish to hebrew (not arabic)', () => {
    assert.equal(getScript('yi'), 'hebrew');
  });

  it('maps Maldivian to thaana (not arabic)', () => {
    assert.equal(getScript('dv'), 'thaana');
  });

  it('falls back to latin for unknown', () => {
    assert.equal(getScript('xx'), 'latin');
    assert.equal(getScript(''), 'latin');
    assert.equal(getScript(null), 'latin');
  });

  it('is case-insensitive', () => {
    assert.equal(getScript('FR'), 'latin');
    assert.equal(getScript('ZH-CN'), 'cjk');
  });
});

describe('isArabicScriptLang', () => {
  it('returns true for Arabic-script langs', () => {
    assert.equal(isArabicScriptLang('ar'), true);
    assert.equal(isArabicScriptLang('fa'), true);
    assert.equal(isArabicScriptLang('ur'), true);
    assert.equal(isArabicScriptLang('ckb'), true);
  });

  it('returns false for non-Arabic langs', () => {
    assert.equal(isArabicScriptLang('fr'), false);
    assert.equal(isArabicScriptLang('he'), false);
    assert.equal(isArabicScriptLang('en'), false);
  });
});

// ---------- Happy path ----------

describe('evaluateChunk — correct script', () => {
  it('russian', () => {
    const r = evaluateChunk('Hello world', 'Привет мир', 'ru');
    assert.equal(r.passed, true);
    assert.ok(!r.issues.includes('wrong_script'));
  });

  it('chinese', () => {
    const r = evaluateChunk('Hello', '你好世界', 'zh');
    assert.equal(r.passed, true);
  });

  it('arabic', () => {
    const r = evaluateChunk('Hello', 'مرحبا بالعالم', 'ar');
    assert.equal(r.passed, true);
  });

  it('persian (with unique chars)', () => {
    const r = evaluateChunk('Hello', 'سلام چطوری؟ من پژوهشگر هستم', 'fa');
    assert.equal(r.passed, true);
  });

  it('french', () => {
    const r = evaluateChunk('Hello', 'Bonjour le monde', 'fr');
    assert.equal(r.passed, true);
  });

  it('hebrew', () => {
    const r = evaluateChunk('Hello', 'שלום עולם', 'he');
    assert.equal(r.passed, true);
  });

  it('korean', () => {
    const r = evaluateChunk('Hello', '안녕하세요 세계', 'ko');
    assert.equal(r.passed, true);
  });

  it('japanese', () => {
    const r = evaluateChunk('Hello', 'こんにちは世界', 'ja');
    assert.equal(r.passed, true);
  });

  it('hindi', () => {
    const r = evaluateChunk('Hello', 'नमस्ते दुनिया', 'hi');
    assert.equal(r.passed, true);
  });

  it('thai', () => {
    const r = evaluateChunk('Hello', 'สวัสดีชาวโลก', 'th');
    assert.equal(r.passed, true);
  });

  it('greek', () => {
    const r = evaluateChunk('Hello', 'Γεια σας κόσμε', 'el');
    assert.equal(r.passed, true);
  });
});

// ---------- Wrong script detection ----------

describe('evaluateChunk — wrong script', () => {
  it('french text for japanese target', () => {
    const r = evaluateChunk('Hello', 'Bonjour le monde', 'ja');
    assert.equal(r.passed, false);
    assert.ok(r.issues.includes('wrong_script'));
  });

  it('arabic text for french target (language confusion)', () => {
    const r = evaluateChunk('Hello', 'مرحبا بالعالم', 'fr');
    assert.equal(r.passed, false);
    assert.ok(r.issues.includes('wrong_script'));
  });

  it('russian text for english target', () => {
    const r = evaluateChunk('Hello', 'Привет мир', 'en');
    assert.equal(r.passed, false);
    assert.ok(r.issues.includes('wrong_script'));
  });

  it('chinese text for korean target', () => {
    const r = evaluateChunk('Hello', '你好世界', 'ko');
    assert.equal(r.passed, false);
    assert.ok(r.issues.includes('wrong_script'));
  });
});

// ---------- Arabic variant discrimination ----------

describe('evaluateChunk — Arabic variant discrimination', () => {
  it('persian text for arabic target fails', () => {
    const r = evaluateChunk('Hello', 'سلام چطوری؟', 'ar');
    assert.equal(r.passed, false);
    assert.ok(r.issues.includes('wrong_arabic_variant'));
  });

  it('urdu text for persian target fails', () => {
    const r = evaluateChunk('Hello', 'السلام ٹڈ', 'fa');
    assert.equal(r.passed, false);
    assert.ok(r.issues.includes('wrong_arabic_variant'));
  });

  it('pashto text for arabic target fails', () => {
    const r = evaluateChunk('Hello', 'السلام ټډړ', 'ar');
    assert.equal(r.passed, false);
    assert.ok(r.issues.includes('wrong_arabic_variant'));
  });
});

// ---------- Edge cases ----------

describe('evaluateChunk — edge cases', () => {
  it('empty translation passes (skipped)', () => {
    const r = evaluateChunk('Hello', '', 'fr');
    assert.equal(r.passed, true);
    assert.ok(r.issues.includes('empty_translation'));
  });

  it('whitespace-only translation passes', () => {
    const r = evaluateChunk('Hello', '   \n  ', 'fr');
    assert.equal(r.passed, true);
  });

  it('null translation passes', () => {
    const r = evaluateChunk('Hello', null, 'fr');
    assert.equal(r.passed, true);
  });

  it('undefined translation passes', () => {
    const r = evaluateChunk('Hello', undefined, 'fr');
    assert.equal(r.passed, true);
  });

  it('numbers only pass for fr target', () => {
    const r = evaluateChunk('Price: 100', '100', 'fr');
    assert.equal(r.passed, true);
  });

  it('unknown target lang falls back to latin', () => {
    const r = evaluateChunk('Hello', 'Bonjour', 'xx');
    assert.equal(r.passed, true);
  });
});

// ---------- Length ----------

describe('evaluateChunk — length', () => {
  it('huge translation flagged via repetition (source too short)', () => {
    const r = evaluateChunk('Short', 'x'.repeat(1000), 'fr');
    assert.ok(r.issues.includes('repetition_hallucination'));
  });

  it('huge translation with long source flagged as length', () => {
    const src = 'A'.repeat(200);
    const r = evaluateChunk(src, 'x'.repeat(1000), 'fr');
    assert.ok(r.issues.includes('length_outlier'));
  });

  it('tiny translation flagged', () => {
    const r = evaluateChunk('A'.repeat(100), 'ok', 'fr');
    assert.ok(r.issues.includes('truncation_suspect'));
  });

  it('numeric source skips length check', () => {
    const r = evaluateChunk('Price: 100', '100', 'fr');
    assert.ok(!r.issues.includes('truncation_suspect'));
    assert.ok(!r.issues.includes('length_outlier'));
  });
});

// ---------- Pattern leak / repetition ----------

describe('evaluateChunk — pattern leak', () => {
  it('english prompt leak', () => {
    const r = evaluateChunk('Hello', 'Translation: Bonjour le monde', 'fr');
    assert.ok(r.issues.includes('prompt_leak'));
  });

  it('french prompt leak', () => {
    const r = evaluateChunk('Hello', 'Voici la traduction : Bonjour', 'fr');
    assert.ok(r.issues.includes('prompt_leak'));
  });

  it('chinese prompt leak', () => {
    const r = evaluateChunk('Hello', '翻译：你好', 'zh');
    assert.ok(r.issues.includes('prompt_leak'));
  });

  it('token repetition detected', () => {
    const r = evaluateChunk('Hello', 'the the the the the the the', 'fr');
    assert.ok(r.issues.includes('repetition_hallucination'));
  });

  it('char repetition detected', () => {
    const r = evaluateChunk('Hello', 'x'.repeat(30), 'fr');
    assert.ok(r.issues.includes('repetition_hallucination'));
  });

  it('normal text has no leak', () => {
    const r = evaluateChunk('Hello', 'Bonjour le monde', 'fr');
    assert.ok(!r.issues.includes('prompt_leak'));
    assert.ok(!r.issues.includes('repetition_hallucination'));
  });
});

// ---------- Document-level ----------

describe('evaluateDocument', () => {
  it('all good', () => {
    const result = evaluateDocument(
      ['Hello', 'World', 'Good morning'],
      ['Bonjour', 'Monde', 'Bonjour'],
      'fr',
    );
    assert.equal(result.passed, true);
    assert.equal(result.chunkCount, 3);
    assert.equal(result.failedChunkCount, 0);
  });

  it('some failures', () => {
    const result = evaluateDocument(
      ['Hello', 'World', 'Good morning'],
      ['Bonjour', 'مرحبا', 'Bonjour'],
      'fr',
    );
    assert.equal(result.passed, false);
    assert.equal(result.failedChunkCount, 1);
    assert.ok('wrong_script' in result.issues);
    assert.equal(result.samples.length, 1);
  });

  it('empty lists', () => {
    const result = evaluateDocument([], [], 'fr');
    assert.equal(result.passed, true);
    assert.equal(result.chunkCount, 0);
  });

  it('sample size caps samples', () => {
    const result = evaluateDocument(
      Array(10).fill('hi'),
      Array(10).fill('مرحبا'),
      'fr',
      3,
    );
    assert.equal(result.failedChunkCount, 10);
    assert.equal(result.samples.length, 3);
  });
});

// ---------- detectArabicVariant direct ----------

describe('detectArabicVariant', () => {
  it('persian for persian passes', () => {
    const r = detectArabicVariant('سلام چطوری', 'fa');
    assert.equal(r.verdict, 'pass');
  });

  it('persian for arabic fails', () => {
    const r = detectArabicVariant('سلام چطوری', 'ar');
    assert.equal(r.verdict, 'fail');
    assert.ok(r.detectedVariants.includes('fa'));
  });

  it('urdu for persian fails', () => {
    const r = detectArabicVariant('السلام ٹڈ', 'fa');
    assert.equal(r.verdict, 'fail');
    assert.ok(r.detectedVariants.includes('ur'));
  });

  it('non-arabic text skipped', () => {
    const r = detectArabicVariant('Bonjour le monde', 'fa');
    assert.equal(r.verdict, 'skip');
  });
});

// ---------- lengthCheck direct ----------

describe('lengthCheck', () => {
  it('normal length returns ratio', () => {
    const r = lengthCheck(
      'Hello world this is a longer source string',
      'Bonjour le monde ceci est une traduction francaise',
    );
    assert.equal(r.issue, null);
    assert.ok(r.ratio !== null);
  });

  it('huge translation', () => {
    const r = lengthCheck('A'.repeat(200), 'x'.repeat(1000));
    assert.equal(r.issue, 'length_outlier');
  });

  it('tiny translation', () => {
    const r = lengthCheck('A'.repeat(100), 'ok');
    assert.equal(r.issue, 'truncation_suspect');
  });

  it('empty translation flagged', () => {
    const r = lengthCheck('Hello world this is a test', '');
    assert.equal(r.issue, 'truncation_suspect');
  });
});

// ---------- patternCheck direct ----------

describe('patternCheck', () => {
  it('no leak on normal text', () => {
    const r = patternCheck('Bonjour le monde');
    assert.equal(r.issue, null);
  });

  it('english leak', () => {
    const r = patternCheck('Translation: Bonjour');
    assert.equal(r.issue, 'prompt_leak');
  });

  it('french leak', () => {
    const r = patternCheck('Voici la traduction : Bonjour');
    assert.equal(r.issue, 'prompt_leak');
  });

  it('chinese leak', () => {
    const r = patternCheck('翻译：你好');
    assert.equal(r.issue, 'prompt_leak');
  });

  it('token repetition', () => {
    const r = patternCheck('the the the the the the the');
    assert.equal(r.issue, 'repetition_hallucination');
  });

  it('char repetition', () => {
    const r = patternCheck('x'.repeat(30));
    assert.equal(r.issue, 'repetition_hallucination');
  });
});
