"""
日文修正器測試模組

測試日文拼音轉換、分詞與修正功能。
"""

from phonofix import JapaneseEngine
from phonofix.languages.japanese.phonetic_impl import JapanesePhoneticSystem
from phonofix.languages.japanese.tokenizer import JapaneseTokenizer


class TestJapaneseCorrector:
    def test_phonetic_conversion(self):
        """測試日文拼音轉換"""
        phonetic = JapanesePhoneticSystem()

        # 測試基本轉換
        # 注意：此專案在 phonetic domain 採用「連續字串」作為比對維度（對齊中文拼音串的設計），因此不保留空白分隔
        assert phonetic.to_phonetic("東京都") == "tokyoto"

        # 注意: Cutlet 預設使用赫本式拼音，但對於 "は" (ha) 作為助詞時，
        # 如果分詞器沒有正確標記為助詞，可能會轉為 "ha"。
        # 這裡 Cutlet 轉為 "konnichiha"，我們暫時接受此結果。
        # 理想情況下應為 "konnichiwa"。
        assert phonetic.to_phonetic("こんにちは") in {"konnichiha", "konnichiwa"}

        assert phonetic.to_phonetic("アスピリン") == "asupirin"

        # 測試混合
        # 注意:
        # 1. "私" 可能被讀作 "watakushi" (較正式) 或 "watashi"
        # 2. "カツカレー" 可能被分詞為 "katsu" + "karee"
        # 這裡根據實際 Cutlet/UniDic 輸出調整預期結果
        actual = phonetic.to_phonetic("私はカツカレーが好きです")
        assert actual in [
            "watashiwakatsukareegasukidesu",
            "watakushiwakatsukareegasukidesu",
        ]

    def test_tokenization(self):
        """測試日文分詞"""
        tokenizer = JapaneseTokenizer()

        text = "私はカツカレーが好きです"
        tokens = tokenizer.tokenize(text)

        # 預期分詞結果 (依賴 MeCab/UniDic，可能會有細微差異)
        # 私 / は / カツ / カレー / が / 好き / です
        # 注意: "カツカレー" 可能被切分為 "カツ" 和 "カレー"

        # 檢查關鍵詞是否被正確切分
        assert "私" in tokens
        assert "好き" in tokens
        # 檢查 "カツ" 和 "カレー" 是否存在 (分開或合併皆可接受)
        assert ("カツカレー" in tokens) or ("カツ" in tokens and "カレー" in tokens)

    def test_correction_basic(self):
        """測試基本日文修正"""
        dictionary = {
            "アスピリン": ["asupirin"],
            "ロキソニン": ["rokisonin"],
            "胃カメラ": ["ikamera"]
        }
        engine = JapaneseEngine()
        corrector = engine.create_corrector(dictionary)

        # 測試完全匹配 (拼音相同)
        assert corrector.correct("頭が痛いのでasupirinを飲みました") == "頭が痛いのでアスピリンを飲みました"

        # 測試模糊匹配 (容許些微差異)
        # "rokisonin" vs "rokisonen" (i -> e)
        assert corrector.correct("痛み止めにrokisonenを使います") == "痛み止めにロキソニンを使います"

    def test_protected_terms_and_event(self):
        """測試 protected_terms 與 on_event（日文也應一致）"""
        events = []

        def on_event(e):
            events.append(e)

        engine = JapaneseEngine()
        corrector = engine.create_corrector(
            {"アスピリン": ["asupirin"]},
            protected_terms=["asupirin"],
            on_event=on_event,
        )

        # 因為 asupirin 被保護，不應替換，也不應發事件
        assert corrector.correct("asupirin") == "asupirin"
        assert events == []

    def test_protected_terms_overlap_span_skips_replacement(self):
        """protected_terms 只要與候選片段重疊，就必須跳過（日文）"""
        events = []

        def on_event(e):
            events.append(e)

        engine = JapaneseEngine()
        corrector = engine.create_corrector(
            {"アスピリン": ["asupirin"]},
            protected_terms=["asup"],
            on_event=on_event,
        )

        assert corrector.correct("asupirin") == "asupirin"
        assert events == []

    def test_silent_disables_event(self):
        """測試 silent=True 不應輸出 log，但事件回呼仍可用（可觀測性）"""
        events = []

        def on_event(e):
            events.append(e)

        engine = JapaneseEngine()
        corrector = engine.create_corrector(
            {"アスピリン": ["asupirin"]},
            on_event=on_event,
        )

        assert corrector.correct("asupirin", silent=True) == "アスピリン"
        assert any(ev.get("type") == "replacement" and ev.get("replacement") == "アスピリン" for ev in events)

    def test_keywords_and_exclude_when(self):
        """測試 keywords/exclude_when 過濾規則（exclude_when 優先）"""
        engine = JapaneseEngine()
        corrector = engine.create_corrector({
            "アスピリン": {
                "aliases": ["asupirin"],
                "keywords": ["頭"],
                "exclude_when": ["胃"],
            }
        })

        assert corrector.correct("頭が痛いのでasupirin") == "頭が痛いのでアスピリン"
        assert corrector.correct("胃が痛いのでasupirin") == "胃が痛いのでasupirin"
        assert corrector.correct("asupirin") == "asupirin"

    def test_exact_match_prefers_longer_alias_on_tie(self):
        """
        防回歸：當同一位置可命中多個 exact alias 且分數相同時，應優先使用更長的匹配。

        典型案例：
        - asupirin 命中在 asupirinn 內，若先替換短的會留下尾巴 'n'
        """
        engine = JapaneseEngine(enable_surface_variants=False)
        corrector = engine.create_corrector({"アスピリン": ["asupirin", "asupirinn"]})
        assert corrector.correct("頭痛にasupirinn") == "頭痛にアスピリン"

    def test_exact_match_does_not_match_inside_ascii_word(self):
        """
        防回歸：短 alias 不應命中在更長的 ASCII 字串內（例如 ai in kaihatsu）。
        """
        engine = JapaneseEngine(enable_surface_variants=False)
        corrector = engine.create_corrector(
            {
                "人工知能": ["ai"],
                "開発": ["kaihatsu"],
                "ロボット": ["robotto"],
            }
        )
        text = "多くの企業が新しいrobottoのkaihatsuに取り組んでいる"
        assert corrector.correct(text) == "多くの企業が新しいロボットの開発に取り組んでいる"
