from __future__ import annotations

import json
import logging
import os
from dataclasses import dataclass
from functools import lru_cache
from typing import Any

logger = logging.getLogger(__name__)

_TRIPLETS_ENV_VAR = "TIM_PROTOCOL_TRIPLETS_JSON"
_STAGES = ("open", "close")
_FIELDS = ("reason1", "reason2", "reason3")


@dataclass(frozen=True, slots=True)
class ProtocolTriplet:
    reason1: str
    reason2: str
    reason3: str


_DEFAULT_CATALOG: dict[str, dict[str, ProtocolTriplet]] = {
    "atendimento_geral": {
        "open": ProtocolTriplet("Informação", "Conta", "Valor"),
        "close": ProtocolTriplet("Informação", "Conta", "Serviço"),
    },
    "cancelamento_vas_avulso": {
        "open": ProtocolTriplet(
            "Solicitação", "Serviço VAS", "Ativação/Desativação"
        ),
        "close": ProtocolTriplet(
            "Solicitação", "Serviço VAS", "Ativação/Desativação"
        ),
    },
    "contestacao": {
        "open": ProtocolTriplet("Informação", "Conta", "Serviço"),
        "close": ProtocolTriplet("Reclamação", "Conta", "Valor"),
    },
    "vas_estrategico": {
        "open": ProtocolTriplet("Informação", "Conta", "Serviço"),
        "close": ProtocolTriplet("Informação", "Conta", "Serviço"),
    },
    "invoice_explanation_aceite_fechado": {
        "open": ProtocolTriplet("Informação", "Conta", "Serviço"),
        "close": ProtocolTriplet("Informação", "Conta", "Serviço"),
    },
    "finalizacao_informacional_fechada": {
        "open": ProtocolTriplet("Informação", "Conta", "Serviço"),
        "close": ProtocolTriplet("Informação", "Conta", "Esclarecimento"),
    },
    "valor_divergente": {
        "open": ProtocolTriplet("Informação", "Conta", "Valor"),
        "close": ProtocolTriplet("Informação", "Conta", "Valor"),
    },
    "pro_rata": {
        "open": ProtocolTriplet("Informação", "Conta", "Valor"),
        "close": ProtocolTriplet("Informação", "Conta", "Valor"),
    },
    "pro_rata_mensalidade": {
        "open": ProtocolTriplet("Informação", "Conta", "Mensalidade"),
        "close": ProtocolTriplet("Informação", "Conta", "Mensalidade"),
    },
    "pro_rata_reclamacao": {
        "open": ProtocolTriplet("Reclamação", "Conta", "Valor"),
        "close": ProtocolTriplet("Reclamação", "Conta", "Valor"),
    },
    "termino_desconto": {
        "open": ProtocolTriplet("Informação", "Conta", "Serviço"),
        "close": ProtocolTriplet("Informação", "Conta", "Serviço"),
    },
    "conta_certa_manual": {
        "open": ProtocolTriplet(
            "Processo Interno", "Conta", "Conta Certa - Venc {dia_vencimento}"
        ),
        "close": ProtocolTriplet(
            "Processo Interno", "Conta", "Conta Certa - Venc {dia_vencimento}"
        ),
    },
}


class _SafeFormatDict(dict[str, Any]):
    def __missing__(self, key: str) -> str:
        return ""


def _normalize_text(value: Any) -> str:
    return str(value or "").strip()


def _override_triplet(base: ProtocolTriplet, override: dict[str, Any]) -> ProtocolTriplet:
    return ProtocolTriplet(
        reason1=_normalize_text(override.get("reason1")) or base.reason1,
        reason2=_normalize_text(override.get("reason2")) or base.reason2,
        reason3=_normalize_text(override.get("reason3")) or base.reason3,
    )


@lru_cache(maxsize=1)
def _catalog_with_overrides() -> dict[str, dict[str, ProtocolTriplet]]:
    catalog = {
        scenario: dict(stages)
        for scenario, stages in _DEFAULT_CATALOG.items()
    }
    raw = os.getenv(_TRIPLETS_ENV_VAR, "").strip()
    if not raw:
        return catalog
    try:
        payload = json.loads(raw)
    except json.JSONDecodeError:
        logger.warning("triplets.override.invalid_json env=%s", _TRIPLETS_ENV_VAR)
        return catalog
    if not isinstance(payload, dict):
        logger.warning("triplets.override.invalid_root env=%s", _TRIPLETS_ENV_VAR)
        return catalog

    for scenario, stage_payload in payload.items():
        scenario_key = _normalize_text(scenario).lower()
        if not scenario_key or not isinstance(stage_payload, dict):
            continue
        current = catalog.get(scenario_key, {})
        for stage in _STAGES:
            override = stage_payload.get(stage)
            if not isinstance(override, dict):
                continue
            if stage in current:
                current[stage] = _override_triplet(current[stage], override)
                continue
            if any(_normalize_text(override.get(field)) for field in _FIELDS):
                current[stage] = ProtocolTriplet(
                    reason1=_normalize_text(override.get("reason1")),
                    reason2=_normalize_text(override.get("reason2")),
                    reason3=_normalize_text(override.get("reason3")),
                )
        if current:
            catalog[scenario_key] = current
    return catalog


def resolve_protocol_triplet(
    scenario: str,
    *,
    stage: str,
    context: dict[str, Any] | None = None,
    fallback_scenario: str = "atendimento_geral",
) -> ProtocolTriplet:
    stage_key = _normalize_text(stage).lower()
    if stage_key not in _STAGES:
        stage_key = "open"
    scenario_key = _normalize_text(scenario).lower() or fallback_scenario
    fallback_key = _normalize_text(fallback_scenario).lower() or "atendimento_geral"

    catalog = _catalog_with_overrides()
    stages = catalog.get(scenario_key) or catalog.get(fallback_key) or {}
    raw_triplet = stages.get(stage_key)
    if raw_triplet is None:
        fallback_stages = catalog.get("atendimento_geral", {})
        raw_triplet = fallback_stages.get(stage_key) or ProtocolTriplet("", "", "")

    fmt_values = _SafeFormatDict(
        {key: _normalize_text(value) for key, value in (context or {}).items()}
    )
    return ProtocolTriplet(
        reason1=raw_triplet.reason1.format_map(fmt_values).strip(),
        reason2=raw_triplet.reason2.format_map(fmt_values).strip(),
        reason3=raw_triplet.reason3.format_map(fmt_values).strip(),
    )
