from __future__ import annotations

import asyncio
import json

import pytest

from app.ws_gateway.session_start import (
    build_remote_agent_context,
    parse_start_payload,
    parse_transferencia_session_id_payload,
    recv_start_message,
)


def test_parse_start_payload_builds_context_from_data() -> None:
    ctx = parse_start_payload(
        {
            "type": "start",
            "data": {
                "agent": "oferta",
                "ani": "5511999999999",
                "gsm": "5511999999999",
                "session_id": "550e8400-e29b-41d4-a716-446655440000",
                "routerCallKeyDay": "20260409",
                "routerCallKey": "RCK-001",
                "callIdGed": "GED-001",
                "protocolo": "PRT-001",
            },
            "audioFormat": {"encoding": "linear16"},
            "callConfig": {"agentBackend": "remote_ws"},
        }
    )

    assert ctx.data["agent"] == "oferta"
    assert ctx.data["session_id"] == "550e8400-e29b-41d4-a716-446655440000"
    assert ctx.data["protocolo"] == "PRT-001"
    assert ctx.agent_data == {}
    assert ctx.audio_format == {"encoding": "linear16"}
    assert ctx.call_config == {"agentBackend": "remote_ws"}
    assert ctx.session_data["msisdn"] == "5511999999999"
    assert ctx.session_data["audioFormat"] == {"encoding": "linear16"}
    assert ctx.agent_starts_conversation is True
    assert ctx.intro == ""
    assert ctx.nudge == "Alô, você ainda está aí?"


def test_parse_start_payload_lets_conta_agent_own_first_message() -> None:
    ctx = parse_start_payload(
        {
            "type": "start",
            "data": {
                "agent": "conta",
                "ani": "5511999999999",
                "gsm": "5511999999999",
                "session_id": "550e8400-e29b-41d4-a716-446655440001",
                "routerCallKeyDay": "20260409",
                "routerCallKey": "RCK-001",
                "callIdGed": "GED-001",
                "agentData": {
                    "idFatura": "fat-123",
                },
            },
        }
    )

    assert ctx.agent_starts_conversation is True
    assert ctx.intro == ""
    assert ctx.nudge == "Alô, você ainda está aí?"


def test_parse_start_payload_rejects_non_start_message() -> None:
    with pytest.raises(RuntimeError):
        parse_start_payload({"type": "ping"})


def test_parse_start_payload_rejects_invalid_fake_agent_responses() -> None:
    with pytest.raises(RuntimeError, match="callConfig invalido"):
        parse_start_payload(
            {
                "type": "start",
                "data": {
                    "agent": "conta",
                    "ani": "5511999999999",
                    "gsm": "5511999999999",
                    "routerCallKeyDay": "20260819",
                    "routerCallKey": "RCK-1",
                    "callIdGed": "GED-1",
                    "session_id": "session-1",
                    "agentData": {"idFatura": "FAT-1"},
                },
                "callConfig": {
                    "agentBackend": "remote_ws_fake",
                    "agentFake": {"responses": "resposta curta;outra curta"},
                },
            }
        )


def test_parse_start_payload_requires_id_fatura_for_conta() -> None:
    with pytest.raises(RuntimeError, match="idFatura"):
        parse_start_payload(
            {
                "type": "start",
                "data": {
                    "agent": "conta",
                    "ani": "5511999999999",
                    "gsm": "5511999999999",
                    "session_id": "550e8400-e29b-41d4-a716-446655440002",
                    "routerCallKeyDay": "20260409",
                    "routerCallKey": "RCK-001",
                    "callIdGed": "GED-001",
                },
            }
        )


def test_parse_start_payload_requires_protocolo_for_oferta() -> None:
    with pytest.raises(RuntimeError, match="protocolo"):
        parse_start_payload(
            {
                "type": "start",
                "data": {
                    "agent": "oferta",
                    "ani": "5511999999999",
                    "gsm": "5511999999999",
                    "session_id": "550e8400-e29b-41d4-a716-446655440003",
                    "routerCallKeyDay": "20260409",
                    "routerCallKey": "RCK-001",
                    "callIdGed": "GED-001",
                },
            }
        )


def test_parse_start_payload_requires_session_id() -> None:
    with pytest.raises(RuntimeError, match="session_id"):
        parse_start_payload(
            {
                "type": "start",
                "data": {
                    "agent": "oferta",
                    "ani": "5511999999999",
                    "gsm": "5511999999999",
                    "routerCallKeyDay": "20260409",
                    "routerCallKey": "RCK-001",
                    "callIdGed": "GED-001",
                    "protocolo": "PRT-001",
                },
            }
        )


def test_parse_start_payload_accepts_camel_session_id_and_normalizes() -> None:
    ctx = parse_start_payload(
        {
            "type": "start",
            "data": {
                "agent": "oferta",
                "ani": "5511999999999",
                "gsm": "5511999999999",
                "sessionId": "550e8400-e29b-41d4-a716-446655440004",
                "routerCallKeyDay": "20260409",
                "routerCallKey": "RCK-001",
                "callIdGed": "GED-001",
                "protocolo": "PRT-001",
            },
        }
    )

    assert ctx.data["session_id"] == "550e8400-e29b-41d4-a716-446655440004"


def test_parse_transferencia_session_id_payload_extracts_session_id() -> None:
    session_id = parse_transferencia_session_id_payload(
        {
            "type": "transferencia_session_id",
            "data": {"session_id": "550e8400-e29b-41d4-a716-446655440005"},
        }
    )

    assert session_id == "550e8400-e29b-41d4-a716-446655440005"


def test_recv_start_message_uses_transferencia_session_id_before_start() -> None:
    class FakeWebSocket:
        def __init__(self) -> None:
            self.messages = [
                {
                    "type": "transferencia_session_id",
                    "data": {"session_id": "550e8400-e29b-41d4-a716-446655440006"},
                },
                {
                    "type": "start",
                    "data": {
                        "agent": "oferta",
                        "ani": "5511999999999",
                        "gsm": "5511999999999",
                        "routerCallKeyDay": "20260409",
                        "routerCallKey": "RCK-001",
                        "callIdGed": "GED-001",
                        "protocolo": "PRT-001",
                    },
                },
            ]

        async def receive_text(self) -> str:
            return json.dumps(self.messages.pop(0))

    ctx = asyncio.run(recv_start_message(FakeWebSocket()))

    assert ctx.data["session_id"] == "550e8400-e29b-41d4-a716-446655440006"


def test_build_remote_agent_context_uses_camel_case_payload() -> None:
    context = build_remote_agent_context(
        data={
            "agent": "contas",
            "ani": "5511888888888",
            "gsm": "5511777777777",
            "routerCallKeyDay": "20260406",
            "routerCallKey": "RCK-123",
            "callIdGed": "GED-777",
        },
        agent_data={"idFatura": "fat-123"},
    )

    assert context == {
        "agent": "conta",
        "RouterCallKeyDay": "20260406",
        "RouterCallKey": "RCK-123",
        "ANI": "5511888888888",
        "GSM": "5511777777777",
        "msisdn": "5511777777777",
        "callIdGed": "GED-777",
        "ID_FATURA": "fat-123",
        "current_invoice_number": "fat-123",
    }


def test_build_remote_agent_context_for_conta_forwards_protocol_id() -> None:
    context = build_remote_agent_context(
        data={
            "agent": "conta",
            "ani": "5511888888888",
            "gsm": "5511777777777",
            "routerCallKeyDay": "20260406",
            "routerCallKey": "RCK-123",
            "callIdGed": "GED-777",
            "protocol_id": "PRT-777",
        },
        agent_data={"idFatura": "fat-123"},
    )

    assert context["protocol_id"] == "PRT-777"
    assert context["protocolo"] == "PRT-777"
    assert context["protocolNumber"] == "PRT-777"


def test_build_remote_agent_context_for_oferta_uses_protocolo_and_never_fatura() -> None:
    context = build_remote_agent_context(
        data={
            "agent": "ofertas",
            "ani": "5511888888888",
            "gsm": "5511777777777",
            "routerCallKeyDay": "20260406",
            "routerCallKey": "RCK-123",
            "callIdGed": "GED-777",
            "protocolo": "PRT-777",
            "assetId": "asset-777",
            "channelId": "ura",
        },
        agent_data={"idFatura": "fat-should-not-leak"},
    )

    assert context == {
        "agent": "oferta",
        "RouterCallKeyDay": "20260406",
        "RouterCallKey": "RCK-123",
        "ANI": "5511888888888",
        "GSM": "5511777777777",
        "msisdn": "5511777777777",
        "callIdGed": "GED-777",
        "protocolo": "PRT-777",
        "protocolNumber": "PRT-777",
        "channelId": "ura",
        "assetId": "asset-777",
    }


def test_build_remote_agent_context_only_forwards_explicit_session_id() -> None:
    without_session = build_remote_agent_context(
        data={
            "agent": "oferta",
            "ani": "5511888888888",
            "gsm": "5511777777777",
            "routerCallKeyDay": "20260406",
            "routerCallKey": "RCK-123",
            "callIdGed": "GED-777",
            "protocolo": "PRT-777",
        },
        agent_data={},
    )
    with_session = build_remote_agent_context(
        data={
            **without_session,
            "agent": "oferta",
            "routerCallKeyDay": "20260406",
            "routerCallKey": "RCK-123",
            "ani": "5511888888888",
            "gsm": "5511777777777",
            "sessionId": "sess-777",
        },
        agent_data={},
    )

    assert "sessionId" not in without_session
    assert "session_id" not in without_session
    assert with_session["sessionId"] == "sess-777"
    assert with_session["session_id"] == "sess-777"


def test_build_remote_agent_context_forwards_explicit_message_id() -> None:
    context = build_remote_agent_context(
        data={
            "agent": "conta",
            "ani": "5511888888888",
            "gsm": "5511777777777",
            "routerCallKeyDay": "20260406",
            "routerCallKey": "RCK-123",
            "callIdGed": "GED-777",
            "messageId": "msg-777",
        },
        agent_data={},
    )

    assert context["message_id"] == "msg-777"


def test_parse_start_payload_does_not_enable_debug_events_from_client_flag_alone() -> None:
    ctx = parse_start_payload(
        {
            "type": "start",
            "debug": {"events": True},
            "data": {
                "agent": "conta",
                "ani": "5511999999999",
                "gsm": "5511999999999",
                "session_id": "load-debug-1",
                "routerCallKeyDay": "20260817",
                "routerCallKey": "LOAD-1",
                "callIdGed": "GED-LOAD-1",
                "agentData": {"idFatura": "fat-load-1"},
            },
        }
    )

    assert ctx.stress_test is False
    assert ctx.debug_events_enabled is False


def test_parse_start_payload_enables_debug_events_for_scripted_fake_agent() -> None:
    response = "Esta resposta simulada possui tamanho suficiente para validar o contrato."
    ctx = parse_start_payload(
        {
            "type": "start",
            "data": {
                "agent": "conta",
                "ani": "5511999999999",
                "gsm": "5511999999999",
                "session_id": "stress-debug-1",
                "routerCallKeyDay": "20260817",
                "routerCallKey": "STRESS-1",
                "callIdGed": "GED-STRESS-1",
                "agentData": {"idFatura": "fat-stress-1"},
            },
            "callConfig": {
                "agentBackend": "remote_ws_fake",
                "agentFake": {"responses": f"{response};{response}"},
            },
        }
    )

    assert ctx.stress_test is True
    assert ctx.debug_events_enabled is True
