from __future__ import annotations

from contextlib import contextmanager
from typing import Any

from app.domain.contas import contestation_validation
from app.domain.contas.contestation_validation import (
    validate_contestation_items,
)


def _vas_invoice(*items: dict[str, Any]) -> dict[str, Any]:
    return {"Servicos de Valor Adicionado": list(items)}


def test_validate_contestation_items_bloqueia_item_inexistente() -> None:
    items = [
        {
            "item_name": "Pacote nao cobrado",
            "claimed_amount": "10",
            "validated_amount": "10",
        }
    ]

    _, validation_log, error = validate_contestation_items(
        items,
        _vas_invoice({"desc": "Pacote cobrado", "valor": "10,00"}),
    )

    assert error == "Item 'Pacote nao cobrado' nao encontrado no json da fatura."
    assert validation_log[-1]["erro"] == "item_nao_encontrado_na_fatura"


def test_validate_contestation_items_bloqueia_item_fora_da_secao_vas() -> None:
    items = [
        {
            "item_name": "Pacote Dados",
            "claimed_amount": "10",
            "validated_amount": "10",
        }
    ]

    _, validation_log, error = validate_contestation_items(
        items,
        {"Plano": [{"desc": "Pacote Dados", "valor": "10,00"}]},
    )

    assert error == "Item 'Pacote Dados' nao e do tipo VAS no json da fatura."
    assert validation_log[-1]["erro"] == "item_fora_secao_vas"


def test_validate_contestation_items_bloqueia_vas_estrategico() -> None:
    items = [
        {
            "item_name": "Netflix Premium",
            "claimed_amount": "10",
            "validated_amount": "10",
        }
    ]

    _, validation_log, error = validate_contestation_items(
        items,
        _vas_invoice({"desc": "Netflix Premium", "valor": "10,00"}),
    )

    assert (
        error
        == "Item 'Netflix Premium' identificado como VAS estrategico e nao pode ser ajustado."
    )
    assert validation_log[-1]["erro"] == "vas_estrategico_nao_permitido"


def test_validate_contestation_items_bloqueia_valor_maior_que_o_item() -> None:
    items = [
        {
            "item_name": "Pacote Musica",
            "claimed_amount": "12",
            "validated_amount": "12",
        }
    ]

    _, validation_log, error = validate_contestation_items(
        items,
        _vas_invoice({"desc": "Pacote Musica", "valor": "10,00"}),
    )

    assert (
        error
        == "Valor de ajuste do item 'Pacote Musica' excede o valor cobrado na fatura."
    )
    assert validation_log[-1]["erro"] == "valor_ajuste_maior_que_item"


def _record_observation_factory(records: list[dict[str, Any]]):
    @contextmanager
    def _record_observation(
        *,
        name: str,
        input: dict[str, Any] | None = None,
        metadata: dict[str, Any] | None = None,
    ):
        record: dict[str, Any] = {
            "name": name,
            "input": input,
            "metadata": metadata,
            "updates": [],
        }

        class _Observation:
            def update(self, **kwargs: Any) -> None:
                record["updates"].append(dict(kwargs))

        records.append(record)
        yield _Observation()

    return _record_observation


def test_validate_contestation_items_emite_spans_quando_bloqueia(
    monkeypatch,
) -> None:
    records: list[dict[str, Any]] = []
    monkeypatch.setattr(
        contestation_validation,
        "_start_guardrail_observation",
        _record_observation_factory(records),
    )

    _, validation_log, error = validate_contestation_items(
        [
            {
                "item_name": "Pacote nao cobrado",
                "claimed_amount": "10",
                "validated_amount": "10",
            }
        ],
        _vas_invoice({"desc": "Pacote cobrado", "valor": "10,00"}),
    )

    assert error
    assert [r["name"] for r in records] == [
        "guardrail.CVAL.evaluated",
        "guardrail.CVAL.blocked",
    ]

    parent, child = records
    assert parent["metadata"] == {
        "mechanism": "guardrail_action_validation",
        "code": "CVAL",
        "action": "abrir_contestacao_cliente",
    }
    assert parent["input"]["items_count"] == 1
    assert parent["input"]["invoice_candidates_count"] == 1
    assert parent["updates"][-1]["level"] == "WARNING"
    assert parent["updates"][-1]["output"]["approved"] is False
    assert parent["updates"][-1]["output"]["validation_log"] == validation_log
    assert (
        parent["updates"][-1]["output"]["reason"]
        == "item_nao_encontrado_na_fatura"
    )

    assert child["metadata"]["reason"] == "item_nao_encontrado_na_fatura"
    assert child["updates"][-1]["level"] == "WARNING"
    assert child["updates"][-1]["output"]["blocked"] is True


def test_validate_contestation_items_emite_span_quando_aprova(
    monkeypatch,
) -> None:
    records: list[dict[str, Any]] = []
    monkeypatch.setattr(
        contestation_validation,
        "_start_guardrail_observation",
        _record_observation_factory(records),
    )

    items, validation_log, error = validate_contestation_items(
        [
            {
                "item_name": "Pacote Musica",
                "claimed_amount": "10",
                "validated_amount": "10",
            }
        ],
        _vas_invoice({"desc": "Pacote Musica", "valor": "10,00"}),
    )

    assert error is None
    assert validation_log[-1]["status"] == "aprovado"
    assert items[0]["validated_amount"] == "10"
    assert [r["name"] for r in records] == ["guardrail.CVAL.evaluated"]

    update = records[0]["updates"][-1]
    assert "level" not in update
    assert update["output"]["approved"] is True
    assert update["output"]["items_count"] == 1
    assert update["output"]["items_validated_count"] == 1
    assert update["output"]["items_approved_count"] == 1
    assert update["output"]["items_rejected_count"] == 0
    assert update["output"]["validation_log"] == validation_log


def test_validate_contestation_items_valida_todos_antes_de_retornar() -> None:
    items = [
        {
            "item_name": "Pacote Musica",
            "claimed_amount": "10",
            "validated_amount": "10",
        },
        {
            "item_name": "Pacote nao cobrado",
            "claimed_amount": "10",
            "validated_amount": "10",
        },
        {
            "item_name": "Pacote Filme",
            "claimed_amount": "5",
            "validated_amount": "5",
        },
    ]

    _, validation_log, error = validate_contestation_items(
        items,
        _vas_invoice(
            {"desc": "Pacote Musica", "valor": "10,00"},
            {"desc": "Pacote Filme", "valor": "5,00"},
        ),
    )

    assert error == "Item 'Pacote nao cobrado' nao encontrado no json da fatura."
    statuses = [(entry["item_name"], entry["status"]) for entry in validation_log]
    assert statuses == [
        ("Pacote Musica", "aprovado"),
        ("Pacote nao cobrado", "reprovado"),
        ("Pacote Filme", "aprovado"),
    ]


def test_validate_contestation_items_usa_classe_avulso_canonica() -> None:
    items = [
        {
            "item_name": "Tamboro Mensal",
            "claimed_amount": "14.99",
            "validated_amount": "14.99",
        }
    ]

    _, validation_log, error = validate_contestation_items(
        items,
        {
            "Secao Qualquer": [
                {
                    "desc": "Tamboro Mensal",
                    "value": 14.99,
                    "classe": "avulso",
                    "verb": "cancelar",
                }
            ]
        },
    )

    assert error is None
    assert validation_log[-1]["status"] == "aprovado"
    assert validation_log[-1]["secao_vas"] is True


def test_validate_contestation_items_bloqueia_classe_estrategico_canonica() -> None:
    items = [
        {
            "item_name": "ServicoNovo",
            "claimed_amount": "10",
            "validated_amount": "10",
        }
    ]

    _, validation_log, error = validate_contestation_items(
        items,
        {
            "Servicos de Valor Adicionado": [
                {
                    "desc": "ServicoNovo",
                    "value": 10.0,
                    "classe": "estrategico",
                    "estrategico": True,
                    "verb": "falar sobre",
                }
            ]
        },
    )

    assert (
        error
        == "Item 'ServicoNovo' identificado como VAS estrategico e nao pode ser ajustado."
    )
    assert validation_log[-1]["erro"] == "vas_estrategico_nao_permitido"
    assert validation_log[-1]["vas_estrategico"] is True


def test_validate_contestation_items_span_blocked_inclui_contagens(
    monkeypatch,
) -> None:
    records: list[dict[str, Any]] = []
    monkeypatch.setattr(
        contestation_validation,
        "_start_guardrail_observation",
        _record_observation_factory(records),
    )

    _, validation_log, error = validate_contestation_items(
        [
            {
                "item_name": "Pacote Musica",
                "claimed_amount": "10",
                "validated_amount": "10",
            },
            {
                "item_name": "Pacote nao cobrado",
                "claimed_amount": "10",
                "validated_amount": "10",
            },
        ],
        _vas_invoice({"desc": "Pacote Musica", "valor": "10,00"}),
    )

    assert error
    assert [r["name"] for r in records] == [
        "guardrail.CVAL.evaluated",
        "guardrail.CVAL.blocked",
    ]
    parent, child = records
    parent_output = parent["updates"][-1]["output"]
    assert parent_output["items_validated_count"] == 2
    assert parent_output["items_approved_count"] == 1
    assert parent_output["items_rejected_count"] == 1
    assert parent_output["validation_log"] == validation_log

    child_output = child["updates"][-1]["output"]
    assert child_output["items_validated_count"] == 2
    assert child_output["items_approved_count"] == 1
    assert child_output["items_rejected_count"] == 1


def test_validate_contestation_items_bloqueia_quando_total_ajustes_excede_fatura() -> None:
    items = [
        {
            "item_name": "VAS Musica",
            "claimed_amount": "100",
            "validated_amount": "100",
        },
        {
            "item_name": "VAS Filme",
            "claimed_amount": "100",
            "validated_amount": "100",
        },
    ]

    _, validation_log, error = validate_contestation_items(
        items,
        {
            "Fatura Resumo": [{"desc": "Total geral", "value": 150.0}],
            "Servicos de Valor Adicionado": [
                {"desc": "VAS Musica", "valor": "100,00"},
                {"desc": "VAS Filme", "valor": "100,00"},
            ],
        },
    )

    assert error == (
        "Valor total de ajustes (200) excede o valor total da fatura (150)."
    )
    total_entry = validation_log[-1]
    assert total_entry["erro"] == "total_ajustes_excede_fatura"
    assert total_entry["valor_total_ajustes"] == "200"
    assert total_entry["valor_total_fatura"] == "150"


def test_validate_contestation_items_aprova_quando_total_ajustes_dentro_do_limite() -> None:
    items = [
        {
            "item_name": "VAS Musica",
            "claimed_amount": "50",
            "validated_amount": "50",
        }
    ]

    _, validation_log, error = validate_contestation_items(
        items,
        {
            "Fatura Resumo": [{"desc": "Total geral", "value": 150.0}],
            "Servicos de Valor Adicionado": [
                {"desc": "VAS Musica", "valor": "50,00"},
            ],
        },
    )

    assert error is None
    assert validation_log[-1]["status"] == "aprovado"


def test_contestation_action_classifies_already_contested_and_missing_requested_item():
    from app.domain.contas.service import ContasDomainService
    from app.domain.contas.workflow_actions import build_contas_workflow_actions

    class Client:
        def billing_analysis(self, msisdn):
            return {"currentInvoice": {"items": [{"description": "TIM Fashion Mensal", "value": 14.99}, {"description": "Outro VAS", "value": 5.0}]}}
        def contestar(self, payload):
            return {"itemsResponse": [
                {"itemName": "TIM Fashion Mensal", "message": "Item ja foi contestado anteriormente", "status": "REJEITADA"}
            ]}

    action = build_contas_workflow_actions(ContasDomainService(client=Client())).get("abrir_contestacao_cliente")
    result = action({
        "msisdn": "11999999999", "skip_invoice_item_validation": True,
        "items": [
            {"itemName": "TIM Fashion Mensal", "claimedAmount": "14,99", "validatedAmount": "14,99"},
            {"itemName": "Outro VAS", "claimedAmount": "5,00", "validatedAmount": "5,00"},
        ],
    }, {"input": {}})
    assert result["itens_ja_contestados"] == ["TIM Fashion Mensal"]
    assert result["contested_items"] == []
    assert result["not_contested_items"][0]["itemName"] == "Outro VAS"
    assert result["contested_invoice_amount_open"] == "0.00"


def test_contestation_action_calculates_totals_only_for_items_actually_contested():
    from app.domain.contas.service import ContasDomainService
    from app.domain.contas.workflow_actions import build_contas_workflow_actions

    class Client:
        def contestar(self, payload):
            return {"itemsResponse": [
                {"itemName": "TIM Fashion Mensal", "status": "INICIADA"},
                {"itemName": "Outro VAS", "status": "REJEITADA", "message": "fora da alcada"},
            ]}

    action = build_contas_workflow_actions(ContasDomainService(client=Client())).get("abrir_contestacao_cliente")
    result = action({
        "msisdn": "11999999999", "skip_invoice_item_validation": True,
        "items": [
            {"itemName": "TIM Fashion Mensal", "claimedAmount": "14,99", "validatedAmount": "14,99"},
            {"itemName": "Outro VAS", "claimedAmount": "5,00", "validatedAmount": "5,00"},
        ],
    }, {"input": {}})
    assert [x["itemName"] for x in result["contested_items"]] == ["TIM Fashion Mensal"]
    assert [x["itemName"] for x in result["not_contested_items"]] == ["Outro VAS"]
    assert result["contested_invoice_amount_open"] == "14.99"
    assert result["contested_invoice_amount"] == "14.99"


def test_validate_contestation_items_reconhece_vas_por_type_do_billing_analysis() -> None:
    items = [
        {
            "item_name": "TIM Fashion Mensal",
            "claimed_amount": "10",
            "validated_amount": "10",
        }
    ]
    invoice = {
        "currentInvoice": [
            {
                "type": "servicos_contratados_de_parceiros",
                "desc": "Serviços de valor adicionado",
                "value": "76.98",
                "items": [
                    {
                        "type": "servicos_contratados_de_parceiros",
                        "desc": "TIM Fashion Mensal",
                        "value": "10.0",
                        "contestable": True,
                    }
                ],
            }
        ]
    }

    validated, validation_log, error = validate_contestation_items(items, invoice)

    assert error is None
    assert validation_log[-1]["status"] == "aprovado"
    assert validation_log[-1]["secao_vas"] is True
    assert validation_log[-1]["item_fatura_resolvido"] == "TIM Fashion Mensal"
    assert validation_log[-1]["tipo_fatura"] == "servicos_contratados_de_parceiros"
    assert validated[0]["validated_amount"] == "10"


def test_validate_contestation_items_prefere_evidencia_vas_quando_item_aparece_em_multiplas_visoes() -> None:
    items = [
        {
            "item_name": "TIM Fashion Mensal",
            "claimed_amount": "10",
            "validated_amount": "10",
        }
    ]
    invoice = {
        "invoiceVariation": [
            {
                "desc": "TIM Fashion Mensal",
                "value": "10.0",
            }
        ],
        "currentInvoice": [
            {
                "type": "servicos_contratados_de_parceiros",
                "desc": "Serviços de valor adicionado",
                "value": "76.98",
                "items": [
                    {
                        "type": "servicos_contratados_de_parceiros",
                        "desc": "TIM Fashion Mensal",
                        "value": "10.0",
                        "contestable": True,
                    }
                ],
            }
        ],
    }

    _, validation_log, error = validate_contestation_items(items, invoice)

    assert error is None
    assert validation_log[-1]["status"] == "aprovado"
    assert validation_log[-1]["secao_vas"] is True
    assert validation_log[-1]["tipo_fatura"] == "servicos_contratados_de_parceiros"


def test_parse_amount_preserva_decimal_json_e_ptbr() -> None:
    assert contestation_validation._parse_amount("19.99") == contestation_validation.Decimal("19.99")
    assert contestation_validation._parse_amount("R$ 19,99") == contestation_validation.Decimal("19.99")
    assert contestation_validation._parse_amount("1.999,99") == contestation_validation.Decimal("1999.99")
    assert contestation_validation._parse_amount("1,999.99") == contestation_validation.Decimal("1999.99")


def test_validate_contestation_items_resolve_homonimo_por_valor_exato() -> None:
    items = [{"item_name": "Tamboro Mensal", "claimed_amount": "19.99", "validated_amount": "19.99"}]
    _, validation_log, error = validate_contestation_items(
        items,
        _vas_invoice(
            {"desc": "Tamboro Mensal", "valor": "14.99"},
            {"desc": "Tamboro Mensal", "valor": "19.99"},
        ),
    )
    assert error is None
    assert validation_log[-1]["status"] == "aprovado"
    assert validation_log[-1]["valor_item_fatura"] == "19.99"


def test_validate_contestation_items_bloqueia_valor_acima_de_todos_homonimos() -> None:
    items = [{"item_name": "Tamboro Mensal", "claimed_amount": "29.98", "validated_amount": "29.98"}]
    _, validation_log, error = validate_contestation_items(
        items,
        _vas_invoice(
            {"desc": "Tamboro Mensal", "valor": "14.99"},
            {"desc": "Tamboro Mensal", "valor": "19.99"},
        ),
    )
    assert error == "Valor de ajuste do item 'Tamboro Mensal' excede o valor cobrado na fatura."
    assert validation_log[-1]["erro"] == "valor_ajuste_maior_que_item"
    assert validation_log[-1]["valor_item_fatura"] == "19.99"
