from __future__ import annotations

import logging
from types import SimpleNamespace

from livekit.agents import vad as agents_vad

from app.livekit.main import FlowLoggingVADStream


def _event(
    event_type: agents_vad.VADEventType,
    *,
    speech_s: float,
    raw_speech_s: float | None = None,
    silence_s: float = 0.0,
    speaking: bool = False,
) -> SimpleNamespace:
    return SimpleNamespace(
        type=event_type,
        speech_duration=speech_s,
        raw_accumulated_speech=speech_s if raw_speech_s is None else raw_speech_s,
        silence_duration=silence_s,
        raw_accumulated_silence=silence_s,
        probability=0.0,
        speaking=speaking,
    )


def _stream(on_speech_end) -> FlowLoggingVADStream:
    return FlowLoggingVADStream(
        SimpleNamespace(),
        stream_id=0,
        should_log=True,
        release_logging_stream=lambda _stream_id: None,
        call_logger=logging.getLogger(__name__),
        min_interrupt_s=0.5,
        vad_config={
            "min_speech_duration": 0.15,
            "min_silence_duration": 1.0,
            "activation_threshold": 0.3,
            "deactivation_threshold": 0.15,
        },
        log_decisions=False,
        log_activity=False,
        activity_min_prob=0.0,
        on_speech_end=on_speech_end,
    )


def test_speech_end_uses_final_duration_without_terminal_vad_silence() -> None:
    durations_ms: list[int] = []
    stream = _stream(durations_ms.append)

    stream._log_vad_decision(
        _event(agents_vad.VADEventType.START_OF_SPEECH, speech_s=0.15)
    )
    # While Silero waits for the endpoint, INFERENCE_DONE keeps increasing
    # speech_duration with the terminal silence.
    stream._log_vad_decision(
        _event(
            agents_vad.VADEventType.INFERENCE_DONE,
            speech_s=1.2,
            raw_speech_s=0.2,
            silence_s=1.0,
        )
    )
    # END_OF_SPEECH reports the corrected duration after removing that silence.
    stream._log_vad_decision(
        _event(
            agents_vad.VADEventType.END_OF_SPEECH,
            speech_s=0.2,
            raw_speech_s=0.2,
            silence_s=1.0,
        )
    )

    assert durations_ms == [200]


def test_short_audio_stays_short_after_a_previous_long_round() -> None:
    durations_ms: list[int] = []
    stream = _stream(durations_ms.append)

    stream._log_vad_decision(
        _event(agents_vad.VADEventType.START_OF_SPEECH, speech_s=0.15)
    )
    stream._log_vad_decision(
        _event(
            agents_vad.VADEventType.INFERENCE_DONE,
            speech_s=2.2,
            raw_speech_s=1.2,
            silence_s=1.0,
        )
    )
    stream._log_vad_decision(
        _event(
            agents_vad.VADEventType.END_OF_SPEECH,
            speech_s=1.2,
            raw_speech_s=1.2,
            silence_s=1.0,
        )
    )

    stream._log_vad_decision(
        _event(agents_vad.VADEventType.START_OF_SPEECH, speech_s=0.15)
    )
    stream._log_vad_decision(
        _event(
            agents_vad.VADEventType.INFERENCE_DONE,
            speech_s=1.2,
            raw_speech_s=0.2,
            silence_s=1.0,
        )
    )
    stream._log_vad_decision(
        _event(
            agents_vad.VADEventType.END_OF_SPEECH,
            speech_s=0.2,
            raw_speech_s=0.2,
            silence_s=1.0,
        )
    )

    assert durations_ms == [1200, 200]
