# #region LanguageDetectServiceTests [C:3] [TYPE Module] [SEMANTICS test, language, detection, heuristic]
# @BRIEF Unit tests for LanguageDetectService — local language detection via lingua.
# @RELATION BINDS_TO -> [LanguageDetectService]

import pytest

from src.plugins.translate._lang_detect import (
    _CODE_TO_LANG,
    _detector_cache,
    batch_detect,
    build_detector,
    detect_language,
    get_detector,
)


# #region test_detect_language_basic [C:1] [TYPE Function]
# @BRIEF Verify core language detection for common languages.
class TestDetectLanguage:
    """Core language detection tests."""

    def test_detect_english(self, en_detector):
        assert detect_language("Hello world, this is English", en_detector) == "en"
        assert detect_language("Good morning! How are you today?", en_detector) == "en"
        assert detect_language("This is a test comment from a user", en_detector) == "en"

    def test_detect_russian(self, ru_detector):
        assert detect_language("Привет мир, это русский", ru_detector) == "ru"
        assert detect_language("Доброе утро! Как дела?", ru_detector) == "ru"
        assert detect_language("Это тестовый комментарий пользователя", ru_detector) == "ru"

    def test_detect_short_texts(self, en_ru_detector):
        """Short texts should still be detected correctly.
        Note: very short single words like "Hello" can be ambiguous
        (lingua detects 'Hello' as Italian), so we use 2+ word phrases.
        """
        assert detect_language("Hello there", en_ru_detector) == "en"
        assert detect_language("Привет всем", en_ru_detector) == "ru"
        assert detect_language("Good morning", en_ru_detector) == "en"
        assert detect_language("Доброе утро", en_ru_detector) == "ru"

    def test_detect_multilingual(self, en_ru_detector):
        """Detector with both EN and RU should handle mixed content."""
        result_en = detect_language("Hello world", en_ru_detector)
        result_ru = detect_language("Привет мир", en_ru_detector)
        assert result_en == "en"
        assert result_ru == "ru"

    def test_detect_french(self, fr_detector):
        assert detect_language("Bonjour le monde", fr_detector) == "fr"
        assert detect_language("Comment allez-vous?", fr_detector) == "fr"
# #endregion test_detect_language_basic


# #region test_detect_language_edge_cases [C:1] [TYPE Function]
class TestDetectLanguageEdgeCases:
    """Edge cases: empty, numeric, special characters."""

    def test_empty_string(self, en_detector):
        assert detect_language("", en_detector) == "und"

    def test_whitespace_only(self, en_detector):
        assert detect_language("   ", en_detector) == "und"
        assert detect_language("\t\n", en_detector) == "und"

    def test_numeric_text(self, en_detector):
        assert detect_language("12345", en_detector) == "und"
        assert detect_language("42", en_detector) == "und"
        assert detect_language("3.14159", en_detector) == "und"

    def test_special_characters(self, en_detector):
        assert detect_language("!@#$%", en_detector) == "und"
        assert detect_language("--- ***", en_detector) == "und"

    def test_none_text(self, en_detector):
        """None input should be handled gracefully (coerced to 'und')."""
        assert detect_language(None, en_detector) == "und"
# #endregion test_detect_language_edge_cases


# #region test_batch_detect [C:1] [TYPE Function]
class TestBatchDetect:
    """Batch detection tests."""

    def test_batch_detect_mixed(self, en_ru_detector):
        texts = [
            "Hello world",
            "Привет мир",
            "Good morning",
            "",
            "Доброе утро",
            "12345",
        ]
        results = batch_detect(texts, detector=en_ru_detector)
        assert results == ["en", "ru", "en", "und", "ru", "und"]

    def test_batch_detect_empty_list(self, en_ru_detector):
        assert batch_detect([], detector=en_ru_detector) == []

    def test_batch_detect_all_same(self, ru_detector):
        texts = ["Привет", "Доброе утро", "Как дела?"]
        results = batch_detect(texts, detector=ru_detector)
        assert all(r == "ru" for r in results)

    def test_batch_detect_builds_detector(self):
        """Without pre-built detector, batch_detect builds one from target_languages."""
        texts = ["Hello world", "Привет мир"]
        results = batch_detect(texts, target_languages=["ru"])
        # "Hello world" should NOT be "ru", so should be "und" or "en"
        # "Привет мир" should be "ru"
        assert results[1] == "ru"
# #endregion test_batch_detect


# #region test_build_detector [C:1] [TYPE Function]
class TestBuildDetector:
    """Detector construction tests."""

    def test_build_with_target_languages(self):
        detector = build_detector(["ru", "de"])
        assert detect_language("Привет мир", detector) == "ru"
        assert detect_language("Guten Morgen", detector) == "de"

    def test_build_without_target_languages(self):
        """Should fall back to default set (common source languages)."""
        detector = build_detector()
        assert detect_language("Hello world", detector) == "en"

    def test_build_with_empty_list(self):
        detector = build_detector([])
        assert detect_language("Hello world", detector) == "en"

    def test_build_with_unknown_code(self):
        """Unknown codes should be silently ignored."""
        detector = build_detector(["zz", "en"])
        assert detect_language("Hello world", detector) == "en"
# #endregion test_build_detector


# #region test_get_detector_cache [C:1] [TYPE Function]
class TestGetDetectorCache:
    """Detector caching behavior."""

    def test_get_detector_returns_detector(self):
        detector = get_detector(["en"])
        assert hasattr(detector, "detect_language_of")

    def test_get_detector_caches_instance(self):
        _detector_cache.clear()
        d1 = get_detector(["en", "ru"])
        d2 = get_detector(["en", "ru"])
        assert d1 is d2  # Same instance from cache

    def test_get_detector_different_key(self):
        _detector_cache.clear()
        d1 = get_detector(["en"])
        d2 = get_detector(["ru"])
        assert d1 is not d2  # Different instances for different languages

    def test_get_detector_default_key(self):
        _detector_cache.clear()
        d1 = get_detector()
        d2 = get_detector(None)
        assert d1 is d2  # Same default instance
# #endregion test_get_detector_cache


# #region test_code_to_lang_mapping [C:1] [TYPE Function]
class TestCodeToLangMapping:
    """Verify the BCP-47 → lingua Language mapping."""

    def test_common_codes_present(self):
        for code in ("en", "ru", "fr", "de", "es", "zh", "ja", "ko", "ar", "pt", "it", "pl", "uk", "tr", "vi", "th", "nl", "sv"):
            assert code in _CODE_TO_LANG, f"Missing mapping for '{code}'"

    def test_mapping_count(self):
        """Should have at least 60 languages mapped from lingua's 87."""
        assert len(_CODE_TO_LANG) >= 60, f"Only {len(_CODE_TO_LANG)} languages mapped"

    def test_mapping_values(self):
        """All mapped values should be lingua Language enum members."""
        from lingua import Language
        for code, lang in _CODE_TO_LANG.items():
            assert isinstance(lang, Language), f"'{code}' maps to {type(lang).__name__}, not Language"
# #endregion test_code_to_lang_mapping


# #region fixtures [C:1] [TYPE Module]
@pytest.fixture
def en_detector():
    """Detector restricted to English."""
    return build_detector(["en"])


@pytest.fixture
def ru_detector():
    """Detector restricted to Russian."""
    return build_detector(["ru"])


@pytest.fixture
def en_ru_detector():
    """Detector for English and Russian."""
    return build_detector(["en", "ru"])


@pytest.fixture
def fr_detector():
    """Detector restricted to French."""
    return build_detector(["fr"])
# #endregion fixtures
# #endregion LanguageDetectServiceTests
