from __future__ import annotations

import asyncio
from types import SimpleNamespace
from typing import Any

from contas_mcp.servers.contas_mcp_server import main as mcp_main


def _result(*, status: str = "COMPLETED", output: dict[str, Any] | None = None,
            error: str | None = None, trace: list[dict[str, Any]] | None = None,
            error_details: dict[str, Any] | None = None) -> SimpleNamespace:
    return SimpleNamespace(
        execution_id="exec-test",
        status=status,
        output=output or {},
        error=error,
        trace=trace or [],
        state={},
        error_details=error_details,
        model_dump=lambda: {
            "execution_id": "exec-test",
            "status": status,
            "output": output or {},
            "error": error,
            "trace": trace or [],
            "state": {},
            "error_details": error_details,
        },
    )


class _Runtime:
    def __init__(self, *results: Any):
        self.results = list(results)
        self.calls: list[tuple[str, dict[str, Any], str | None]] = []

    async def arun(self, name: str, payload: dict[str, Any], execution_id: str | None = None):
        self.calls.append((name, dict(payload), execution_id))
        if not self.results:
            raise AssertionError("resultado não configurado")
        return self.results.pop(0)


def test_contestacao_wrapper_preserva_tipo_atendimento_e_contexto_do_turno(monkeypatch):
    runtime = _Runtime(_result(output={"abrir_contestacao_cliente": {"success": True}}))
    monkeypatch.setattr(mcp_main, "get_workflow_runtime", lambda: runtime)
    args = {
        "msisdn": "15981110720",
        "subject": "VAS",
        "valor": "10",
        "message_id": "msg-123",
        "customer_message": "Sim",
        "session_created_at": "2026-05-05T12:00:00+00:00",
        "ura_call_id": "CALL-123",
        "channel_id": "URA",
        "ani": "11900000000",
        "invoice_id": "3000132705",
    }
    result = asyncio.run(mcp_main._run_workflow("contestacao_tool", args))
    assert result["status"] == "COMPLETED"
    _, payload, _ = runtime.calls[0]
    assert payload["tipo_atendimento"] == "contestacao"
    for key in ("message_id", "customer_message", "session_created_at", "ura_call_id", "channel_id", "ani", "invoice_id"):
        assert payload[key] == args[key]


def test_contestacao_wrapper_mapeia_falha_tecnica_como_sistemica(monkeypatch):
    runtime = _Runtime(_result(status="FAILED", error="gateway indisponivel"))
    monkeypatch.setattr(mcp_main, "get_workflow_runtime", lambda: runtime)
    result = asyncio.run(mcp_main._run_workflow("contestacao_tool", {"msisdn": "62999999999", "subject": "VAS"}))
    assert result["success"] is False
    assert result["erro_sistemico"] is True
    assert result["erro"] == "erro_falha_sistema"
    assert "contestation_error_description" not in result


def test_contestacao_wrapper_preserva_erro_de_negocio_e_protocolo(monkeypatch):
    msg = "Impossível prosseguir, pois não existe os itens informados na fatura questionada."
    runtime = _Runtime(_result(
        status="FAILED",
        error=msg,
        output={"registrar_protocolo": {"protocolo_id": "PRT-123"}},
        error_details={"body": {"description": "Itens já contestados", "provider": {"errorMessage": msg}}},
    ))
    monkeypatch.setattr(mcp_main, "get_workflow_runtime", lambda: runtime)
    result = asyncio.run(mcp_main._run_workflow("contestacao_tool", {"msisdn": "62999999999", "subject": "VAS"}))
    assert result["success"] is False
    assert result["erro_sistemico"] is False
    assert result["contestation_error_description"] == msg
    assert result["mensagem"] == msg
    assert result["contestacao_protocol"] == "PRT-123"


def test_invoice_explanation_branch_nao_sem_vas_recomenda_nao_resolvido():
    result = mcp_main._result_payload(
        _result(trace=[{"node": "finalizar_nao_resolvido"}]),
        workflow_name="invoice_explanation",
    )
    assert result["recomenda_finalizacao"] is True
    assert result["status_finalizacao_sugerido"] == "nao_resolvido"


def test_invoice_explanation_branch_devolver_orquestrador_nao_auto_finaliza():
    result = mcp_main._result_payload(
        _result(trace=[{"node": "devolver_orquestrador"}]),
        workflow_name="invoice_explanation",
    )
    assert "recomenda_finalizacao" not in result
    assert "status_finalizacao_sugerido" not in result


def test_pro_rata_branch_devolver_orquestrador_nao_auto_finaliza():
    result = mcp_main._result_payload(
        _result(trace=[{"node": "devolver_orquestrador"}]),
        workflow_name="pro_rata",
    )
    assert "recomenda_finalizacao" not in result
    assert "status_finalizacao_sugerido" not in result


def test_pro_rata_aceite_e_nao_controle_recomendam_resolvido():
    for node in ("registrar_aceitou", "registrar_nao_controle"):
        result = mcp_main._result_payload(_result(trace=[{"node": node}]), workflow_name="pro_rata")
        assert result["recomenda_finalizacao"] is True
        assert result["status_finalizacao_sugerido"] == "resolvido"


def test_vas_estrategico_workflow_payload_preserva_tres_itens_e_action_agrupa_duas_linhas():
    from app.domain.contas.workflow_actions import build_contas_workflow_actions

    class Service:
        pass

    action = build_contas_workflow_actions(Service()).get("preparar_vas_estrategico")
    items = [
        {"type": "bundle", "msisdn": "5511999999999", "name": "Netflix"},
        {"type": "estrategico", "msisdn": "5511999999999", "name": "Aya E-Books"},
        {"type": "estrategico", "msisdn": "5511988888888", "name": "YouTube Premium"},
    ]
    payload = mcp_main._workflow_payload("vas_estrategico", {"msisdn": "5511999999999", "items": items})
    assert len(payload["items"]) == 3
    result = action(payload, {"input": payload})
    assert len(result["linhas"]) == 2
    first = result["linhas"][0]
    assert first["msisdn"] == "5511999999999"
    assert "Netflix" in first["bundle_names"]
    assert "Aya E-Books" in first["estrategico_names"]


def test_vas_estrategico_rejeitado_declara_rag_para_cada_servico_estrategico():
    from app.domain.contas.workflow_actions import build_contas_workflow_actions

    class Service:
        pass

    action = build_contas_workflow_actions(Service()).get("registrar_atendimento_vas_estrategico")
    params = {
        "linhas": [
            {"msisdn": "5511999999999", "bundle_names": ["Netflix"], "estrategico_names": ["Aya E-Books"]},
            {"msisdn": "5511988888888", "bundle_names": [], "estrategico_names": ["YouTube Premium"]},
        ],
        "mensagem_base": "concluido",
    }
    result = action(params, {"input": {"resposta_usuario": "NAO"}})
    assert result["requires_rag"] is True
    assert len(result["rag_queries"]) == 2
    assert any("Aya E-Books" in q for q in result["rag_queries"])
    assert any("YouTube Premium" in q for q in result["rag_queries"])


def test_cancelamento_sem_candidatos_nao_dispara_contestacao(monkeypatch):
    cancel = _result(
        output={"cancelar_vas_avulso": {
            "success": True,
            "cancelados": [{"subject": "VAS X", "success": True}],
            "contestation_candidates": [],
            "protocolos_por_linha": [{"protocolo_id": "PRT-CANCEL-1"}],
        }}
    )
    runtime = _Runtime(cancel)
    monkeypatch.setattr(mcp_main, "get_workflow_runtime", lambda: runtime)
    result = asyncio.run(mcp_main._run_cancelamento_com_contestacao({
        "msisdn": "11999999999", "subject": "VAS X", "items": [{"msisdn": "11999999999", "name": "VAS X"}],
    }))
    assert len(runtime.calls) == 1
    assert result["success"] is True
    assert result["cancelamento_vas_protocol"] == "PRT-CANCEL-1"


def test_cancelamento_falha_operacional_com_candidato_dispara_contestacao(monkeypatch):
    cancel = _result(output={"cancelar_vas_avulso": {
        "success": False,
        "cancelados": [],
        "nao_cancelados": [{"subject": "VAS X", "success": False, "error": "block_vas_failed"}],
        "contestation_candidates": [{"subject": "VAS X", "msisdn": "11999999999", "value": "10.00", "eligible_for_contestation": True}],
        "protocolos_por_linha": [{"protocolo_id": "PRT-CANCEL-1"}],
    }})
    contest = _result(output={
        "registrar_protocolo": {"protocolo_id": "PRT-CONT-1"},
        "abrir_contestacao_cliente": {"success": True, "items_response": [{"status": "CRIAR", "itemName": "VAS X"}]},
        "enviar_sms": {"success": True},
    })
    runtime = _Runtime(cancel, contest)
    monkeypatch.setattr(mcp_main, "get_workflow_runtime", lambda: runtime)
    result = asyncio.run(mcp_main._run_cancelamento_com_contestacao({
        "msisdn": "11999999999", "subject": "VAS X", "items": [{"msisdn": "11999999999", "name": "VAS X", "value": "10.00"}],
        "complete_invoices_payload": {"billingProfile": {"customer": {"document": "123", "customerId": "C1"}}, "paymentItems": [{"invoiceId": "I1"}]},
    }))
    assert [c[0] for c in runtime.calls] == ["cancelamento_vas_avulso", "contestacao_tool"]
    assert result["contestacao_protocol"] == "PRT-CONT-1"
    assert result["cancelamento_vas_protocol"] == "PRT-CANCEL-1"


def test_cancelamento_sms_falho_preserva_sucesso_e_flag(monkeypatch):
    cancel = _result(output={"cancelar_vas_avulso": {
        "success": True,
        "cancelados": [{"subject": "VAS X", "success": True}],
        "contestation_candidates": [{"subject": "VAS X", "msisdn": "11999999999", "value": "10.00"}],
        "protocolos_por_linha": [{"protocolo_id": "PRT-CANCEL-1"}],
    }})
    contest = _result(output={
        "registrar_protocolo": {"protocolo_id": "PRT-CONT-1"},
        "abrir_contestacao_cliente": {"success": True, "items_response": [{"status": "CRIAR", "itemName": "VAS X"}]},
        "enviar_sms": {"success": False, "error": "sms indisponível"},
    })
    runtime = _Runtime(cancel, contest)
    monkeypatch.setattr(mcp_main, "get_workflow_runtime", lambda: runtime)
    result = asyncio.run(mcp_main._run_cancelamento_com_contestacao({
        "msisdn": "11999999999", "subject": "VAS X", "items": [{"msisdn": "11999999999", "name": "VAS X", "value": "10.00"}],
        "complete_invoices_payload": {"billingProfile": {"customer": {"document": "123", "customerId": "C1"}}, "paymentItems": [{"invoiceId": "I1"}]},
    }))
    assert result["success"] is True
    assert result["sms_not_send_error"] is True


def test_cancelamento_aceita_cpf_como_alias_social_sec_no(monkeypatch):
    cancel = _result(output={"cancelar_vas_avulso": {
        "success": True,
        "cancelados": [{"servico": "VAS X"}],
        "contestation_candidates": [{"subject": "VAS X", "msisdn": "11999999999", "value": "10.00"}],
        "protocolos_por_linha": [{"protocolo_id": "PRT-CANCEL-1"}],
    }})
    contest = _result(output={
        "registrar_protocolo": {"protocolo_id": "PRT-CONT-1"},
        "abrir_contestacao_cliente": {"success": True},
    })
    runtime = _Runtime(cancel, contest)
    monkeypatch.setattr(mcp_main, "get_workflow_runtime", lambda: runtime)
    asyncio.run(mcp_main._run_cancelamento_com_contestacao({
        "msisdn": "11999999999", "cpf": "123.456.789-01", "subject": "VAS X",
        "items": [{"msisdn": "11999999999", "name": "VAS X", "value": "10.00"}],
        "complete_invoices_payload": {"billingProfile": {"customer": {"customerId": "C1"}}, "paymentItems": [{"invoiceId": "I1"}]},
    }))
    _, cancel_payload, _ = runtime.calls[0]
    _, contest_payload, _ = runtime.calls[1]
    assert cancel_payload["social_sec_no"] == "12345678901"
    assert contest_payload["social_sec_no"] == "12345678901"


def test_cancelamento_falha_workflow_preserva_output_parcial_nao_cancelados(monkeypatch):
    failed = _result(
        status="FAILED",
        error="Falha técnica ao processar serviços VAS no IU.",
        output={"cancelar_vas_avulso": {
            "cancelados": [],
            "nao_cancelados": [{"msisdn": "11964460192", "servico": "VOD + Canais Abertos", "erro": "Falha no IU", "motivo": "query_vas_failed"}],
            "nao_encontrados": [],
        }},
    )
    runtime = _Runtime(failed)
    monkeypatch.setattr(mcp_main, "get_workflow_runtime", lambda: runtime)
    result = asyncio.run(mcp_main._run_cancelamento_com_contestacao({"msisdn": "11964460192", "subject": "VOD + Canais Abertos"}))
    assert result["status"] == "FAILED"
    assert result["output"]["cancelar_vas_avulso"]["nao_cancelados"][0]["motivo"] == "query_vas_failed"


def test_cancelamento_aceita_alias_itens_para_contestacao(monkeypatch):
    cancel = _result(output={"cancelar_vas_avulso": {
        "success": False,
        "cancelados": [],
        "itens_para_contestacao": [{"servico": "Inexistente", "msisdn": "11999999999", "valor": "14,99", "iu_reason": "service_not_found"}],
        "nao_encontrados": [{"servico": "Inexistente"}],
    }})
    contest = _result(output={
        "registrar_protocolo": {"protocolo_id": "PRT-CANDIDATO"},
        "abrir_contestacao_cliente": {"success": True, "items_response": [{"status": "INICIADA", "itemName": "Inexistente"}]},
    })
    runtime = _Runtime(cancel, contest)
    monkeypatch.setattr(mcp_main, "get_workflow_runtime", lambda: runtime)
    result = asyncio.run(mcp_main._run_cancelamento_com_contestacao({
        "msisdn": "11999999999", "subject": "Inexistente", "items": [{"msisdn": "11999999999", "name": "Inexistente", "value": "14,99"}],
        "complete_invoices_payload": {"billingProfile": {"customer": {"document": "123", "customerId": "C1"}}, "paymentItems": [{"invoiceId": "I1"}]},
    }))
    assert [c[0] for c in runtime.calls] == ["cancelamento_vas_avulso", "contestacao_tool"]
    assert result["contestacao_protocol"] == "PRT-CANDIDATO"


def test_invoice_explanation_service_failure_preserva_fraseologia_sem_auto_finalize():
    from contas_mcp.servers.contas_mcp_server.main import _result_payload
    result = {
        "execution_id": "exec-ie-falha",
        "workflow_name": "invoice_explanation",
        "workflow_version": 2,
        "status": "COMPLETED",
        "output": {"resposta_falha_servico": {"success": False, "service_failed": True, "mensagem": "texto incorreto"}},
        "trace": [{"node": "resposta_falha_servico"}],
        "error": None,
    }
    payload = _result_payload(result, workflow_name="invoice_explanation")
    assert payload["success"] is False
    assert payload["service_failed"] is True
    assert payload["auto_finalize_on_failure"] is False
    assert payload["mensagem"] == "Para continuar com a sua solicitação, aguarde um instante."
