from __future__ import annotations

import unittest

from app.livekit.call_config import (
    normalize_call_config,
    resolve_fake_agent_overrides,
    resolve_agent_backend_name,
    resolve_stt_overrides,
    resolve_tts_overrides,
    resolve_vad_logging_overrides,
    resolve_vad_overrides,
    resolve_ws_overrides,
)
from app.ws_gateway.call_config import build_call_config


class CallConfigTests(unittest.TestCase):
    def test_build_and_normalize_call_config(self) -> None:
        payload = {
            "agentBackend": "remote_ws_fake",
            "stt": {
                "provider": "internal_http",
                "language": "pt-BR",
                "configOverride": "{\"processor\":{\"strategy\":\"faster_default\"}}",
                "minProbSingleWord": "0.15",
                "disableVosk": True,
            },
            "tts": {
                "provider": "elevenlabs",
                "voiceId": "voice-123",
                "modelId": "model-456",
            },
            "vad": {
                "minSpeechDuration": 0.04,
                "activationThreshold": 0.18,
                "deactivationThreshold": 0.10,
                "minSilenceDuration": 0.7,
                "prefixPaddingDuration": 0.75,
                "preBackendWaitNoticeFastOnVadPause": True,
                "deferredInterruptionMinAudioMs": 1350,
                "deferredInterruptionEnabled": False,
            },
            "vadLogging": {
                "logDecisions": True,
                "logActivity": False,
                "activityMinProbability": 0.03,
            },
            "ws": {
                "outputGain": 1.2,
                "audioInputBacklogShedEnabled": True,
                "audioInputBacklogShedThresholdMs": 700,
                "audioInputBacklogShedKeepMs": 250,
                "audioInputLatencyMetricsEnabled": True,
                "audioInputLatencyAlertMs": 900,
                "audioInputLatencyLogIntervalS": 10.5,
                "livekitAudioSourceQueueSizeMs": 400,
                "livekitAudioSourceClearOnShed": False,
                "audioInputBacklogEnergyShedEnabled": True,
                "audioInputBacklogEnergyShedMaxExcessMs": 600,
                "audioInputBacklogSilenceDbfs": -60,
            },
            "agentFake": {
                "delayMs": 2500,
                "responses": (
                    "Esta e a primeira resposta simulada com tamanho intermediario;"
                    "Esta e a resposta final simulada encerrando o atendimento"
                ),
            },
        }

        built = build_call_config(payload)
        normalized = normalize_call_config(payload)

        self.assertEqual(built, normalized)
        self.assertEqual(resolve_agent_backend_name(payload, "remote_ws"), "remote_ws_fake")
        self.assertEqual(resolve_stt_overrides(payload)["language"], "pt-BR")
        self.assertEqual(resolve_stt_overrides(payload)["disable_vosk"], "True")
        self.assertEqual(
            resolve_stt_overrides(payload)["config_override"],
            "{\"processor\":{\"strategy\":\"faster_default\"}}",
        )
        self.assertEqual(resolve_tts_overrides(payload)["voice_id"], "voice-123")
        self.assertEqual(resolve_vad_overrides(payload)["min_speech_duration"], "0.04")
        self.assertEqual(resolve_vad_overrides(payload)["activation_threshold"], "0.18")
        self.assertEqual(
            resolve_vad_overrides(payload)["pre_backend_wait_notice_fast_on_vad_pause"],
            "True",
        )
        self.assertEqual(
            resolve_vad_overrides(payload)["deferred_interruption_min_audio_ms"],
            "1350",
        )
        self.assertEqual(
            resolve_vad_overrides(payload)["deferred_interruption_enabled"],
            "False",
        )
        self.assertEqual(resolve_vad_logging_overrides(payload)["log_decisions"], "True")
        self.assertEqual(resolve_vad_logging_overrides(payload)["log_activity"], "False")
        self.assertEqual(resolve_ws_overrides(payload)["output_gain"], "1.2")
        self.assertEqual(resolve_ws_overrides(payload)["audio_in_backlog_shed_enabled"], "True")
        self.assertEqual(resolve_ws_overrides(payload)["audio_in_backlog_shed_threshold_ms"], "700")
        self.assertEqual(resolve_ws_overrides(payload)["audio_in_backlog_shed_keep_ms"], "250")
        self.assertEqual(resolve_ws_overrides(payload)["audio_in_latency_metrics_enabled"], "True")
        self.assertEqual(resolve_ws_overrides(payload)["audio_in_latency_alert_ms"], "900")
        self.assertEqual(resolve_ws_overrides(payload)["audio_in_latency_log_interval_s"], "10.5")
        self.assertEqual(resolve_ws_overrides(payload)["livekit_audio_source_queue_size_ms"], "400")
        self.assertEqual(resolve_ws_overrides(payload)["livekit_audio_source_clear_on_shed"], "False")
        self.assertEqual(resolve_ws_overrides(payload)["audio_in_backlog_energy_shed_enabled"], "True")
        self.assertEqual(resolve_ws_overrides(payload)["audio_in_backlog_energy_shed_max_excess_ms"], "600")
        self.assertEqual(resolve_ws_overrides(payload)["audio_in_backlog_silence_dbfs"], "-60")
        fake = resolve_fake_agent_overrides(payload)
        self.assertEqual(fake["delay_ms"], 2500)
        self.assertEqual(len(fake["responses"]), 2)

    def test_empty_payload_falls_back_to_defaults(self) -> None:
        payload = {}

        self.assertEqual(
            build_call_config(payload),
            {
                "agent_backend": "",
                "stt": {
                    "provider": "",
                    "language": "",
                    "api_key": "",
                    "initial_prompt": "",
                    "config_override": "",
                    "min_prob_single_word": "",
                    "disable_vosk": "",
                },
                "tts": {
                    "provider": "",
                    "voice_id": "",
                    "model_id": "",
                    "language": "",
                },
                "vad": {
                    "min_speech_duration": "",
                    "activation_threshold": "",
                    "deactivation_threshold": "",
                    "min_silence_duration": "",
                    "prefix_padding_duration": "",
                    "pre_backend_wait_notice_fast_on_vad_pause": "",
                    "deferred_interruption_min_audio_ms": "",
                    "deferred_interruption_enabled": "",
                },
                "vad_logging": {
                    "log_decisions": "",
                    "log_activity": "",
                    "activity_min_probability": "",
                },
                "ws": {
                    "output_gain": "",
                    "audio_in_backlog_shed_enabled": "",
                    "audio_in_backlog_shed_threshold_ms": "",
                    "audio_in_backlog_shed_keep_ms": "",
                    "audio_in_latency_metrics_enabled": "",
                    "audio_in_latency_alert_ms": "",
                    "audio_in_latency_log_interval_s": "",
                    "livekit_audio_source_queue_size_ms": "",
                    "livekit_audio_source_clear_on_shed": "",
                    "audio_in_backlog_energy_shed_enabled": "",
                    "audio_in_backlog_energy_shed_max_excess_ms": "",
                    "audio_in_backlog_silence_dbfs": "",
                },
                "agent_fake": {"delay_ms": "", "responses": ""},
            },
        )
        self.assertEqual(resolve_agent_backend_name(payload, "remote_ws"), "remote_ws")

    def test_fake_responses_are_trimmed_and_default_delay_is_applied(self) -> None:
        payload = {
            "agentBackend": "remote_ws_fake",
            "agentFake": {
                "responses": (
                    "  Primeira resposta simulada com comprimento intermediario  ;"
                    " Segunda resposta simulada encerrando corretamente a chamada "
                )
            },
        }

        fake = resolve_fake_agent_overrides(payload)

        self.assertEqual(fake["delay_ms"], 2500)
        self.assertEqual(
            fake["responses"],
            [
                "Primeira resposta simulada com comprimento intermediario",
                "Segunda resposta simulada encerrando corretamente a chamada",
            ],
        )

    def test_fake_responses_reject_invalid_contract(self) -> None:
        valid = "Esta resposta simulada possui tamanho intermediario adequado"
        invalid_cases = [
            {"agentBackend": "remote_ws", "agentFake": {"responses": f"{valid};{valid}"}},
            {"agentBackend": "remote_ws_fake", "agentFake": {"responses": valid}},
            {"agentBackend": "remote_ws_fake", "agentFake": {"responses": f"{valid};;{valid}"}},
            {"agentBackend": "remote_ws_fake", "agentFake": {"responses": f"curta;{valid}"}},
            {
                "agentBackend": "remote_ws_fake",
                "agentFake": {"responses": f"{valid};{valid}", "delayMs": 180001},
            },
        ]

        for payload in invalid_cases:
            with self.subTest(payload=payload), self.assertRaises(ValueError):
                resolve_fake_agent_overrides(payload)

    def test_disable_vosk_and_fake_backend_are_normalized_per_call(self) -> None:
        payload = {
            "agentBackend": "remote_ws_fake",
            "stt": {"provider": "internal_http", "disableVosk": True},
        }

        self.assertEqual(resolve_agent_backend_name(payload, "remote_ws"), "remote_ws_fake")
        self.assertEqual(resolve_stt_overrides(payload)["disable_vosk"], "True")

    def test_fast_vad_pause_override_lives_with_vad_overrides(self) -> None:
        payload = {"vad": {"preBackendWaitNoticeFastOnVadPause": True}}

        self.assertEqual(
            resolve_vad_overrides(payload)["pre_backend_wait_notice_fast_on_vad_pause"],
            "True",
        )

    def test_deferred_interruption_min_audio_override_lives_with_vad_overrides(self) -> None:
        payload = {"vad": {"deferredInterruptionMinAudioMs": 1250}}

        self.assertEqual(
            resolve_vad_overrides(payload)["deferred_interruption_min_audio_ms"],
            "1250",
        )

    def test_deferred_interruption_min_audio_accepts_root_env_style_key(self) -> None:
        payload = {"DEFERRED_INTERRUPTION_MIN_AUDIO_MS": 1400}

        self.assertEqual(
            resolve_vad_overrides(payload)["deferred_interruption_min_audio_ms"],
            "1400",
        )

    def test_deferred_interruption_enabled_accepts_root_env_style_key(self) -> None:
        payload = {"DEFERRED_INTERRUPTION_ENABLED": False}

        self.assertEqual(
            resolve_vad_overrides(payload)["deferred_interruption_enabled"],
            "False",
        )


if __name__ == "__main__":
    unittest.main()
