"""Tests for the playback-phase TTS-echo guard (#75780).

Contract:
  - `is_tts_echo` flags a barge-in transcript as a likely self-capture of
    Hermes' own TTS output when it is a close character-level match for the
    text that was just spoken, regardless of language/tokenization.
  - A genuine, unrelated user interjection captured during playback must
    NOT be flagged, even though it happens to share some words.
  - `HermesCLI._voice_submit_barge_utterance` uses this guard ONLY for
    playback-phase barge captures (generation-phase speech can't be TTS
    bleed, since nothing is playing) and drops the echoed transcript
    instead of queuing it as the next user turn.
"""

from tools.voice_mode import is_tts_echo


class TestIsTtsEcho:
    def test_near_verbatim_repeat_is_echo(self):
        spoken = (
            "맞아요. 사용자가 마이크를 끄는 게 아니라 앱이 제 음성은 "
            "에코 제거로 걸러내고 실제 사용자 음성만 끼어들기로 받아야 해요."
        )
        transcript = spoken
        assert is_tts_echo(transcript, spoken) is True

    def test_repeat_with_leading_stutter_is_echo(self):
        spoken = "네, 방금도 제 답변이 그대로 다시 입력됐어요."
        transcript = "네 방금 네 방금도 제 답변이 그대로 다시 입력됐어요."
        assert is_tts_echo(transcript, spoken) is True

    def test_unrelated_interjection_is_not_echo(self):
        spoken = "The weather today is sunny with a light breeze from the west."
        transcript = "actually can you also check my calendar for tomorrow"
        assert is_tts_echo(transcript, spoken) is False

    def test_short_unrelated_reply_is_not_echo(self):
        spoken = "I've finished summarizing the document you shared earlier."
        transcript = "stop"
        assert is_tts_echo(transcript, spoken) is False

    def test_short_fragment_of_longer_multi_sentence_reply_is_echo(self):
        # Playback-phase captures are cut immediately on trigger and only
        # span pre-roll + time-to-silence, so a real self-capture is
        # typically a short fragment of a much longer spoken reply, not a
        # near-verbatim repeat of the whole thing. A whole-string ratio
        # dilutes with the length mismatch and misses this case (#75780
        # review).
        spoken = (
            "Sure, here's a summary of what we found. The build failed "
            "because of a missing dependency in the lockfile. I've already "
            "gone ahead and regenerated it, and the tests are passing "
            "again locally. Let me know if you'd like me to open a PR for "
            "this or if you want to review the diff first before I do "
            "anything else."
        )
        transcript = "Sure, here's a summary of what we found."
        assert is_tts_echo(transcript, spoken) is True

    def test_short_fragment_from_middle_of_reply_is_echo(self):
        spoken = (
            "The deployment finished successfully. All three services "
            "came up healthy, and the smoke tests passed without any "
            "errors."
        )
        transcript = "the smoke tests passed without any errors"
        assert is_tts_echo(transcript, spoken) is True

    def test_short_genuine_acknowledgement_is_not_echo(self):
        # A one-word barge-in that happens to also appear as a word inside
        # a longer spoken reply must NOT be treated as a self-capture: the
        # fragment fallback's same-length window would otherwise match it
        # at ratio 1.0 and drop a real "yes" (#75792 review).
        spoken = "Yes, I can help with that -- let me pull up the details for you."
        transcript = "yes"
        assert is_tts_echo(transcript, spoken) is False

    def test_short_fragment_of_longer_reply_in_no_whitespace_language_is_echo(self):
        # The fragment fallback must work without relying on whitespace
        # word-splitting, since some languages (e.g. Chinese, Japanese)
        # don't delimit words with spaces (#75792 review).
        spoken = (
            "部署已经成功完成。所有三个服务都正常运行状态良好,冒烟测试也全部通过,没有发现任何错误。"
        )
        transcript = "所有三个服务都正常运行状态良好"
        assert is_tts_echo(transcript, spoken) is True

    def test_empty_inputs_are_not_echo(self):
        assert is_tts_echo("", "hello") is False
        assert is_tts_echo("hello", "") is False
        assert is_tts_echo("", "") is False

    def test_case_and_whitespace_insensitive(self):
        spoken = "Sure, I can help with that right away."
        transcript = "  SURE,   I can help with that   right away.  "
        assert is_tts_echo(transcript, spoken) is True

    def test_custom_threshold_is_honored(self):
        spoken = "This is a moderately similar sentence about testing."
        transcript = "This is a rather different sentence about coding."
        # Lenient threshold treats it as an echo, strict threshold does not.
        assert is_tts_echo(transcript, spoken, threshold=0.5) is True
        assert is_tts_echo(transcript, spoken, threshold=0.95) is False
