"""
Parser specifico per header PEC (Fase 3).

Classifica il tipo di messaggio PEC leggendo gli header:
  - X-Ricevuta         → tipo ricevuta (Aruba, Legalmail)
  - X-TipoRicevuta     → tipo ricevuta alternativo (Namirial)
  - X-Riferimento-Message-ID → Message-ID del messaggio originale
  - X-Trasporto        → indicatore trasporto PEC (presenza indica PEC)

Il risultato della classificazione determina:
  - `pec_type`: valore enum DB (posta_certificata, accettazione, ...)
  - `is_receipt`: True se il messaggio è una ricevuta (non l'originale)
  - `riferimento_message_id`: usato per collegare parent_message_id

State machine messaggi outbound:
  sent/queued → accepted   (ricevuta di accettazione o presa in carico)
  accepted    → delivered  (ricevuta di avvenuta consegna)
  any valid   → anomaly    (non-accettazione, mancata consegna, errore, virus)
"""

import email.message
import logging
from dataclasses import dataclass

logger = logging.getLogger(__name__)


# ─── Mapping header PEC → enum DB ────────────────────────────────────────────
#
# I diversi provider PEC usano varianti nei valori degli header:
#   Aruba PEC:    X-Ricevuta con trattini (es. "avvenuta-consegna")
#   Namirial:     X-TipoRicevuta (stessa sintassi di Aruba)
#   Legalmail:    X-Ricevuta, talvolta con underscore o spazi
#   InfoCert:     X-Ricevuta, stessa sintassi Aruba

_PEC_TYPE_MAP: dict[str, str] = {
    # ── Standard con trattini (Aruba, Namirial, InfoCert) ──────────────────
    "accettazione":                 "accettazione",
    "non-accettazione":             "non_accettazione",
    "presa-in-carico":              "presa_in_carico",
    "avvenuta-consegna":            "avvenuta_consegna",
    "mancata-consegna":             "mancata_consegna",
    "errore-consegna":              "errore_consegna",
    "preavviso-mancata-consegna":   "preavviso_mancata_consegna",
    "rilevazione-virus":            "rilevazione_virus",
    "posta-certificata":            "posta_certificata",

    # ── Con underscore (variante Legalmail/Poste) ───────────────────────────
    "non_accettazione":             "non_accettazione",
    "presa_in_carico":              "presa_in_carico",
    "avvenuta_consegna":            "avvenuta_consegna",
    "mancata_consegna":             "mancata_consegna",
    "errore_consegna":              "errore_consegna",
    "preavviso_mancata_consegna":   "preavviso_mancata_consegna",
    "rilevazione_virus":            "rilevazione_virus",
    "posta_certificata":            "posta_certificata",

    # ── Con spazio (variante Legalmail rara) ────────────────────────────────
    "non accettazione":             "non_accettazione",
    "presa in carico":              "presa_in_carico",
    "avvenuta consegna":            "avvenuta_consegna",
    "mancata consegna":             "mancata_consegna",
    "errore consegna":              "errore_consegna",
    "preavviso mancata consegna":   "preavviso_mancata_consegna",
    "rilevazione virus":            "rilevazione_virus",

    # ── Abbreviazioni usate da alcuni provider ──────────────────────────────
    "consegna":         "avvenuta_consegna",   # Legalmail abbreviazione
    "no-consegna":      "mancata_consegna",
}


@dataclass
class PecClassification:
    """Risultato della classificazione di un messaggio PEC."""

    pec_type: str               # valore enum DB
    riferimento_message_id: str | None  # X-Riferimento-Message-ID
    x_ricevuta: str | None      # valore raw dell'header X-Ricevuta
    x_tipo_ricevuta: str | None  # valore raw dell'header X-TipoRicevuta
    is_receipt: bool            # True se è ricevuta (non messaggio originale)


def classify_pec_message(msg: email.message.Message) -> PecClassification:
    """
    Classifica il tipo di messaggio PEC analizzando gli header specifici.

    Header letti (in ordine di priorità):
    1. X-TipoRicevuta  (Namirial, standard più recente)
    2. X-Ricevuta      (Aruba, Legalmail, InfoCert)

    Se nessuno dei due è presente, il messaggio è classificato come
    'posta_certificata' (messaggio PEC originale).

    Per la correlazione con il messaggio originale (outbound):
    - X-Riferimento-Message-ID contiene il Message-ID del messaggio inviato
    """
    x_ricevuta = _clean_header(msg.get("X-Ricevuta"))
    x_tipo = _clean_header(msg.get("X-TipoRicevuta"))
    x_riferimento = _clean_header(msg.get("X-Riferimento-Message-ID"))

    # X-TipoRicevuta ha precedenza
    raw_type = (x_tipo or x_ricevuta or "").lower()
    pec_type = _PEC_TYPE_MAP.get(raw_type, "posta_certificata")
    is_receipt = pec_type != "posta_certificata"

    return PecClassification(
        pec_type=pec_type,
        riferimento_message_id=x_riferimento,
        x_ricevuta=x_ricevuta,
        x_tipo_ricevuta=x_tipo,
        is_receipt=is_receipt,
    )


def _clean_header(value: str | None) -> str | None:
    """Pulisce e normalizza il valore di un header PEC."""
    if not value:
        return None
    return value.strip()


# ─── State machine messaggi outbound ─────────────────────────────────────────
#
# Quando il worker riceve una ricevuta PEC (inbound), deve aggiornare
# lo stato del messaggio outbound corrispondente (trovato tramite
# X-Riferimento-Message-ID → message_id_header).
#
# Transizioni valide:
#   (current_state, receipt_type) → new_state
#
# Stati terminali: "delivered", "anomaly", "failed"
# Non si deve retrocedere di stato (es. delivered → anomaly non è valido)

# Tipo ricevuta → nuovo stato da applicare al messaggio outbound
_RECEIPT_TO_STATE: dict[str, str] = {
    "accettazione":             "accepted",
    "presa_in_carico":          "accepted",   # equivalente funzionale ad accepted
    "avvenuta_consegna":        "delivered",
    "non_accettazione":         "anomaly",
    "mancata_consegna":         "anomaly",
    "errore_consegna":          "anomaly",
    "preavviso_mancata_consegna": "anomaly",
    "rilevazione_virus":        "anomaly",
}

# Transizioni di stato valide per ciascuno stato corrente
# Solo le transizioni in avanti sono permesse (no retrocessioni)
VALID_OUTBOUND_TRANSITIONS: dict[str, set[str]] = {
    "queued":   {"accepted", "anomaly"},
    "sent":     {"accepted", "anomaly"},
    "accepted": {"delivered", "anomaly"},
    # delivered e anomaly sono terminali: nessuna transizione
}


def get_state_transition(pec_type: str) -> str | None:
    """
    Restituisce il nuovo stato da applicare al messaggio outbound
    quando arriva una ricevuta di questo tipo.

    Ritorna None se il tipo non provoca transizioni di stato
    (es. 'posta_certificata' non è una ricevuta).
    """
    return _RECEIPT_TO_STATE.get(pec_type)


def apply_outbound_transition(current_state: str, pec_type: str) -> str | None:
    """
    Applica la state machine al messaggio outbound.

    Args:
        current_state: stato corrente del messaggio outbound
        pec_type: tipo della ricevuta arrivata

    Returns:
        Il nuovo stato se la transizione è valida, None altrimenti.
        Ritorna None anche se lo stato è già terminale.
    """
    new_state = get_state_transition(pec_type)
    if new_state is None:
        return None  # Non è una ricevuta con effetto sullo stato

    valid_targets = VALID_OUTBOUND_TRANSITIONS.get(current_state, set())
    if new_state in valid_targets:
        return new_state

    logger.debug(
        f"Transizione stato ignorata: {current_state!r} → {new_state!r} "
        f"(ricevuta: {pec_type!r})"
    )
    return None
