"""
Tests for services/quality/script_detector.py
"""
import pytest

from services.quality import evaluate_chunk, evaluate_document, detect_arabic_variant


# ---------- Happy path: correct script ----------

class TestCorrectScript:
    def test_russian_correct(self):
        r = evaluate_chunk("Hello world", "Привет мир", "ru")
        assert r.passed is True
        assert "wrong_script" not in r.issues

    def test_chinese_simplified_correct(self):
        r = evaluate_chunk("Hello", "你好世界", "zh")
        assert r.passed is True

    def test_arabic_correct(self):
        r = evaluate_chunk("Hello", "مرحبا بالعالم", "ar")
        assert r.passed is True

    def test_persian_correct(self):
        # Persian has unique chars چ پ ژ گ
        r = evaluate_chunk("Hello", "سلام چطوری؟ من پژوهشگر هستم", "fa")
        assert r.passed is True

    def test_french_correct(self):
        r = evaluate_chunk("Hello world", "Bonjour le monde", "fr")
        assert r.passed is True

    def test_hebrew_correct(self):
        r = evaluate_chunk("Hello", "שלום עולם", "he")
        assert r.passed is True

    def test_korean_correct(self):
        r = evaluate_chunk("Hello", "안녕하세요 세계", "ko")
        assert r.passed is True

    def test_japanese_correct(self):
        # Japanese must contain hiragana or katakana to be classified as ja.
        r = evaluate_chunk("Hello", "こんにちは世界", "ja")
        assert r.passed is True

    def test_hindi_correct(self):
        r = evaluate_chunk("Hello", "नमस्ते दुनिया", "hi")
        assert r.passed is True

    def test_thai_correct(self):
        r = evaluate_chunk("Hello", "สวัสดีชาวโลก", "th")
        assert r.passed is True

    def test_greek_correct(self):
        r = evaluate_chunk("Hello", "Γεια σας κόσμε", "el")
        assert r.passed is True


# ---------- Wrong script: language confusion ----------

class TestWrongScript:
    def test_french_text_for_japanese_target(self):
        r = evaluate_chunk("Hello", "Bonjour le monde", "ja")
        assert r.passed is False
        assert "wrong_script" in r.issues

    def test_arabic_text_for_french_target(self):
        r = evaluate_chunk("Hello", "مرحبا بالعالم", "fr")
        assert r.passed is False
        assert "wrong_script" in r.issues

    def test_russian_text_for_english_target(self):
        r = evaluate_chunk("Hello", "Привет мир", "en")
        assert r.passed is False
        assert "wrong_script" in r.issues

    def test_chinese_text_for_korean_target(self):
        r = evaluate_chunk("Hello", "你好世界", "ko")
        # Hangul syllables are 0xAC00-0xD7AF — Chinese chars are not in that range.
        assert r.passed is False
        assert "wrong_script" in r.issues


# ---------- Persian / Arabic discrimination ----------

class TestArabicVariantDiscrimination:
    def test_persian_for_arabic_target_fails(self):
        # Persian text with چ پ ژ گ should fail when target is Arabic.
        r = evaluate_chunk("Hello", "سلام چطوری؟", "ar")
        assert r.passed is False
        assert "wrong_arabic_variant" in r.issues

    def test_arabic_for_persian_target_fails(self):
        # Pure Arabic text (no Persian-specific chars) when target is Persian.
        r = evaluate_chunk("Hello", "السلام عليكم", "fa")
        # No Persian-specific chars, no other Arabic-script variant chars
        # → should pass the variant check (because no discrimination signal).
        # This is a known limitation: pure Arabic indistinguishable from pure Persian.
        # We accept that the variant check only flags MISMATCHES with discriminating chars.
        assert "wrong_arabic_variant" not in r.issues

    def test_urdu_for_persian_target_fails(self):
        # Urdu with ٹ ڈ should fail when target is Persian.
        r = evaluate_chunk("Hello", "السلام ٹڈ", "fa")
        assert r.passed is False
        assert "wrong_arabic_variant" in r.issues

    def test_pashto_for_arabic_target_fails(self):
        # Pashto with ټډړ should fail when target is Arabic.
        r = evaluate_chunk("Hello", "السلام ټډړ", "ar")
        assert r.passed is False
        assert "wrong_arabic_variant" in r.issues


# ---------- Edge cases ----------

class TestEdgeCases:
    def test_empty_translation_passes(self):
        # Empty translations are skipped (the provider may have legitimately
        # produced nothing — e.g. an empty cell).
        r = evaluate_chunk("Hello", "", "fr")
        assert r.passed is True
        assert "empty_translation" in r.issues

    def test_whitespace_only_passes(self):
        r = evaluate_chunk("Hello", "   \n  ", "fr")
        assert r.passed is True
        assert "empty_translation" in r.issues

    def test_none_translation_passes(self):
        r = evaluate_chunk("Hello", None, "fr")
        assert r.passed is True

    def test_numbers_only_passes(self):
        r = evaluate_chunk("Price: 100", "100", "fr")
        assert r.passed is True

    def test_unknown_target_lang_falls_back(self):
        # Unknown lang → latin → no script check, just length/leak checks.
        r = evaluate_chunk("Hello", "Bonjour", "xx")
        assert r.passed is True

    def test_mixed_brands_latin_target(self):
        # "iPhone 15 Pro Max" is mostly Latin + digits — fine for fr.
        r = evaluate_chunk("Buy iPhone 15", "Acheter iPhone 15", "fr")
        assert r.passed is True


# ---------- Length sanity ----------

class TestLengthIssues:
    def test_huge_translation_flagged_via_repetition(self):
        # Source too short for length ratio, but the "x"*1000 repetition
        # is caught by the pattern/repetition check, not length.
        r = evaluate_chunk("Short", "x" * 1000, "fr")
        assert "repetition_hallucination" in r.issues

    def test_huge_translation_with_long_source_flagged(self):
        # Long enough source → length ratio kicks in → length_outlier.
        src = "A" * 200
        r = evaluate_chunk(src, "x" * 1000, "fr")
        assert "length_outlier" in r.issues

    def test_tiny_translation_flagged(self):
        r = evaluate_chunk("A" * 100, "ok", "fr")
        # ratio is 2/100 = 0.02, well below 0.15
        assert "truncation_suspect" in r.issues

    def test_normal_translation_no_length_issue(self):
        r = evaluate_chunk("Hello world, how are you?", "Bonjour le monde, comment allez-vous?", "fr")
        assert "length_outlier" not in r.issues
        assert "truncation_suspect" not in r.issues

    def test_short_source_skips_ratio(self):
        r = evaluate_chunk("Hi", "ok", "fr")
        # source too short for ratio check
        assert "length_outlier" not in r.issues
        assert "truncation_suspect" not in r.issues

    def test_numeric_source_skips_length_check(self):
        # Source is mostly digits — translation can also be short.
        r = evaluate_chunk("Price: 100", "100", "fr")
        assert "truncation_suspect" not in r.issues
        assert "length_outlier" not in r.issues


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

class TestPatternLeak:
    def test_prompt_leak_english_detected(self):
        r = evaluate_chunk("Hello", "Translation: Bonjour le monde", "fr")
        assert "prompt_leak" in r.issues

    def test_prompt_leak_french_detected(self):
        r = evaluate_chunk("Hello", "Voici la traduction : Bonjour", "fr")
        assert "prompt_leak" in r.issues

    def test_prompt_leak_chinese_detected(self):
        r = evaluate_chunk("Hello", "翻译：你好", "zh")
        assert "prompt_leak" in r.issues

    def test_repetition_hallucination_detected(self):
        r = evaluate_chunk("Hello", "the the the the the the", "fr")
        assert "repetition_hallucination" in r.issues

    def test_normal_text_no_leak(self):
        r = evaluate_chunk("Hello", "Bonjour le monde", "fr")
        assert "prompt_leak" not in r.issues
        assert "repetition_hallucination" not in r.issues


# ---------- Document-level aggregation ----------

class TestEvaluateDocument:
    def test_all_good(self):
        result = evaluate_document(
            ["Hello", "World", "Good morning"],
            ["Bonjour", "Monde", "Bonjour"],
            "fr",
        )
        assert result.passed is True
        assert result.failed_chunk_count == 0
        assert result.chunk_count == 3

    def test_some_failures(self):
        result = evaluate_document(
            ["Hello", "World", "Good morning"],
            ["Bonjour", "مرحبا", "Bonjour"],
            "fr",
        )
        assert result.passed is False
        assert result.failed_chunk_count == 1
        assert "wrong_script" in result.issues
        assert len(result.samples) == 1

    def test_empty_lists(self):
        result = evaluate_document([], [], "fr")
        assert result.passed is True
        assert result.chunk_count == 0
        assert result.score == 0.0

    def test_sample_size_caps_samples(self):
        # 10 failures, sample_size=3
        result = evaluate_document(
            ["hi"] * 10,
            ["مرحبا"] * 10,
            "fr",
            sample_size=3,
        )
        assert result.failed_chunk_count == 10
        assert len(result.samples) == 3

    def test_score_proportional(self):
        # 4 chunks, 1 fails → score should be ~0.75 (3/4 passed checks)
        result = evaluate_document(
            ["hi", "hi", "hi", "hi"],
            ["bonjour", "bonjour", "مرحبا", "bonjour"],
            "fr",
        )
        assert result.failed_chunk_count == 1
        assert result.score < 1.0
        assert result.score > 0.0


# ---------- detect_arabic_variant direct ----------

class TestDetectArabicVariantDirect:
    def test_empty_text(self):
        r = detect_arabic_variant("", "fa")
        assert r["verdict"] == "skip"

    def test_pure_persian_for_persian(self):
        r = detect_arabic_variant("سلام چطوری", "fa")
        assert r["verdict"] == "pass"

    def test_pure_persian_for_arabic(self):
        r = detect_arabic_variant("سلام چطوری", "ar")
        assert r["verdict"] == "fail"
        assert "fa" in r["detected_variants"]

    def test_pure_urdu_for_persian(self):
        r = detect_arabic_variant("السلام ٹڈ", "fa")
        assert r["verdict"] == "fail"
        assert "ur" in r["detected_variants"]

    def test_non_arabic_text(self):
        r = detect_arabic_variant("Bonjour le monde", "fa")
        # Not in Arabic script → skip
        assert r["verdict"] == "skip"
