# tests/unit/utils/test_i18n.py
"""
Unit tests for i18n utilities.

Tests cover:
- Language configuration
- Translation loading
- Translation lookup
- Accept-Language parsing
- Language resolution
- Jinja2 integration
"""

import pytest

from app.utils.i18n import (
    DEFAULT_LANGUAGE,
    LANGUAGE_FLAGS,
    LANGUAGE_NAMES,
    LANGUAGE_NAMES_EN,
    SUPPORTED_LANGUAGES,
    TranslationContext,
    clear_translation_cache,
    create_translation_context,
    get_jinja2_globals,
    get_language_choices,
    get_language_info,
    get_locales_path,
    get_nested_value,
    is_rtl_language,
    load_translations,
    parse_accept_language,
    resolve_store_dashboard_language,
    resolve_storefront_language,
    t,
    translate,
)


@pytest.mark.unit
@pytest.mark.utils
class TestLanguageConfiguration:
    """Test language configuration constants"""

    def test_supported_languages(self):
        """Test SUPPORTED_LANGUAGES contains expected languages"""
        assert "en" in SUPPORTED_LANGUAGES
        assert "fr" in SUPPORTED_LANGUAGES
        assert "de" in SUPPORTED_LANGUAGES
        assert "lb" in SUPPORTED_LANGUAGES

    def test_default_language_is_french(self):
        """Test default language is French (Luxembourg context)"""
        assert DEFAULT_LANGUAGE == "fr"

    def test_language_names_defined(self):
        """Test all languages have names defined"""
        for lang in SUPPORTED_LANGUAGES:
            assert lang in LANGUAGE_NAMES
            assert lang in LANGUAGE_NAMES_EN
            assert lang in LANGUAGE_FLAGS

    def test_get_locales_path_exists(self):
        """Test locales path is accessible"""
        path = get_locales_path()
        assert path is not None


@pytest.mark.unit
@pytest.mark.utils
class TestTranslationLoading:
    """Test translation loading functionality"""

    def test_load_translations_english(self):
        """Test loading English translations"""
        clear_translation_cache()
        translations = load_translations("en")
        assert isinstance(translations, dict)

    def test_load_translations_french(self):
        """Test loading French translations"""
        clear_translation_cache()
        translations = load_translations("fr")
        assert isinstance(translations, dict)

    def test_load_translations_unsupported(self):
        """Test loading unsupported language falls back to default"""
        clear_translation_cache()
        translations = load_translations("xx")  # Non-existent
        # Should fall back to French (default)
        assert isinstance(translations, dict)

    def test_clear_translation_cache(self):
        """Test clearing translation cache"""
        load_translations("en")
        clear_translation_cache()
        # Should not raise
        load_translations("en")


@pytest.mark.unit
@pytest.mark.utils
class TestNestedValue:
    """Test get_nested_value function"""

    def test_get_nested_value_simple(self):
        """Test getting simple key"""
        data = {"key": "value"}
        result = get_nested_value(data, "key")
        assert result == "value"

    def test_get_nested_value_nested(self):
        """Test getting nested key"""
        data = {"level1": {"level2": {"level3": "value"}}}
        result = get_nested_value(data, "level1.level2.level3")
        assert result == "value"

    def test_get_nested_value_missing(self):
        """Test getting missing key returns key path"""
        data = {"key": "value"}
        result = get_nested_value(data, "missing.key")
        assert result == "missing.key"

    def test_get_nested_value_with_default(self):
        """Test getting missing key with default"""
        data = {"key": "value"}
        result = get_nested_value(data, "missing.key", "default")
        assert result == "default"

    def test_get_nested_value_non_string(self):
        """Test getting non-string value returns default"""
        data = {"key": {"nested": "obj"}}
        result = get_nested_value(data, "key", "default")
        assert result == "default"


@pytest.mark.unit
@pytest.mark.utils
class TestTranslate:
    """Test translate function"""

    def test_translate_with_language(self):
        """Test translate with specified language"""
        result = translate("common.save", language="en")
        # Should return something (either translation or key)
        assert result is not None

    def test_translate_default_language(self):
        """Test translate uses default language when not specified"""
        result = translate("common.save")
        assert result is not None

    def test_translate_missing_key(self):
        """Test translate returns key when not found"""
        result = translate("nonexistent.key.path")
        assert result == "nonexistent.key.path"

    def test_translate_with_interpolation(self):
        """Test translate with variable interpolation"""
        # Create a translation that uses variables
        result = translate("test.key", language="en", name="John")
        # Should return something (translation with vars or key)
        assert result is not None

    def test_t_alias(self):
        """Test t() is alias for translate()"""
        result1 = translate("common.save", language="en")
        result2 = t("common.save", language="en")
        assert result1 == result2


@pytest.mark.unit
@pytest.mark.utils
class TestTranslationContext:
    """Test TranslationContext class"""

    def test_translation_context_init(self):
        """Test TranslationContext initialization"""
        ctx = TranslationContext("en")
        assert ctx.language == "en"

    def test_translation_context_default_language(self):
        """Test TranslationContext uses default when not specified"""
        ctx = TranslationContext()
        assert ctx.language == DEFAULT_LANGUAGE

    def test_translation_context_callable(self):
        """Test TranslationContext is callable"""
        ctx = TranslationContext("en")
        result = ctx("common.save")
        assert result is not None

    def test_translation_context_set_language(self):
        """Test set_language changes language"""
        ctx = TranslationContext("en")
        ctx.set_language("fr")
        assert ctx.language == "fr"

    def test_translation_context_set_unsupported(self):
        """Test set_language rejects unsupported language"""
        ctx = TranslationContext("en")
        ctx.set_language("xx")
        assert ctx.language == "en"  # Should not change


@pytest.mark.unit
@pytest.mark.utils
class TestJinja2Integration:
    """Test Jinja2 integration functions"""

    def test_create_translation_context(self):
        """Test create_translation_context factory"""
        ctx = create_translation_context("de")
        assert isinstance(ctx, TranslationContext)
        assert ctx.language == "de"

    def test_get_jinja2_globals(self):
        """Test get_jinja2_globals returns required globals"""
        globals = get_jinja2_globals("en")

        assert "_" in globals
        assert "t" in globals
        assert "SUPPORTED_LANGUAGES" in globals
        assert "DEFAULT_LANGUAGE" in globals
        assert "LANGUAGE_NAMES" in globals
        assert "LANGUAGE_FLAGS" in globals
        assert "current_language" in globals
        assert globals["current_language"] == "en"

    def test_get_jinja2_globals_default_language(self):
        """Test get_jinja2_globals uses default when not specified"""
        globals = get_jinja2_globals()
        assert globals["current_language"] == DEFAULT_LANGUAGE


@pytest.mark.unit
@pytest.mark.utils
class TestLanguageResolution:
    """Test language resolution functions"""

    def test_resolve_store_dashboard_user_preferred(self):
        """Test store dashboard prefers user's language"""
        result = resolve_store_dashboard_language("en", "fr")
        assert result == "en"

    def test_resolve_store_dashboard_store_fallback(self):
        """Test store dashboard falls back to store setting"""
        result = resolve_store_dashboard_language(None, "de")
        assert result == "de"

    def test_resolve_store_dashboard_default(self):
        """Test store dashboard uses default when nothing set"""
        result = resolve_store_dashboard_language(None, None)
        assert result == DEFAULT_LANGUAGE

    def test_resolve_storefront_customer_preferred(self):
        """Test storefront prefers customer's language"""
        result = resolve_storefront_language("de", "fr", "en", None)
        assert result == "de"

    def test_resolve_storefront_session(self):
        """Test storefront uses session language"""
        result = resolve_storefront_language(None, "de", "fr", None)
        assert result == "de"

    def test_resolve_storefront_store(self):
        """Test storefront uses store default"""
        result = resolve_storefront_language(None, None, "en", None)
        assert result == "en"

    def test_resolve_storefront_browser(self):
        """Test storefront uses browser language"""
        result = resolve_storefront_language(None, None, None, "de")
        assert result == "de"

    def test_resolve_storefront_enabled_filter(self):
        """Test storefront enabled_languages filters automatic fallback but not explicit choices"""
        # Explicit user choice (customer_preferred) should be respected
        # even if not in store's enabled list
        result = resolve_storefront_language("de", None, None, None, ["en", "fr"])
        assert result == "de"

        # But automatic fallback (browser, store default) should be filtered
        result = resolve_storefront_language(None, None, None, "de", ["en", "fr"])
        assert result in ["en", "fr", DEFAULT_LANGUAGE]


@pytest.mark.unit
@pytest.mark.utils
class TestAcceptLanguageParsing:
    """Test Accept-Language header parsing"""

    def test_parse_accept_language_simple(self):
        """Test parsing simple Accept-Language"""
        result = parse_accept_language("fr")
        assert result == "fr"

    def test_parse_accept_language_with_region(self):
        """Test parsing Accept-Language with region"""
        result = parse_accept_language("fr-FR")
        assert result == "fr"

    def test_parse_accept_language_multiple(self):
        """Test parsing multiple languages with quality"""
        result = parse_accept_language("de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7")
        assert result == "de"  # Highest quality supported

    def test_parse_accept_language_none(self):
        """Test parsing None returns None"""
        result = parse_accept_language(None)
        assert result is None

    def test_parse_accept_language_unsupported(self):
        """Test parsing unsupported language returns None"""
        result = parse_accept_language("zh-CN")
        assert result is None


@pytest.mark.unit
@pytest.mark.utils
class TestUtilityFunctions:
    """Test utility functions"""

    def test_get_language_choices(self):
        """Test get_language_choices returns tuples"""
        choices = get_language_choices()
        assert len(choices) == len(SUPPORTED_LANGUAGES)
        for code, name in choices:
            assert code in SUPPORTED_LANGUAGES
            assert name == LANGUAGE_NAMES[code]

    def test_get_language_info_supported(self):
        """Test get_language_info for supported language"""
        info = get_language_info("en")
        assert info["code"] == "en"
        assert info["name"] == "English"
        assert info["name_en"] == "English"
        assert "flag" in info

    def test_get_language_info_unsupported(self):
        """Test get_language_info for unsupported language"""
        info = get_language_info("xx")
        # Should fallback to default
        assert info["code"] == DEFAULT_LANGUAGE

    def test_is_rtl_language_false(self):
        """Test is_rtl_language returns False for LTR languages"""
        for lang in SUPPORTED_LANGUAGES:
            assert is_rtl_language(lang) is False

    def test_is_rtl_language_arabic(self):
        """Test is_rtl_language returns True for RTL languages"""
        assert is_rtl_language("ar") is True
        assert is_rtl_language("he") is True
