"""
Test unitari per app.parsers.pec_parser.

Copertura:
  - Tutti i tipi di ricevuta PEC (accettazione, avvenuta-consegna, ecc.)
  - Varianti per provider: Aruba, Namirial, Legalmail/Poste, InfoCert
  - Precedenza X-TipoRicevuta su X-Ricevuta
  - Estrazione X-Riferimento-Message-ID
  - State machine outbound (apply_outbound_transition)
  - Funzione get_state_transition per tutti i tipi
"""

import email

import pytest

from app.parsers.pec_parser import (
    VALID_OUTBOUND_TRANSITIONS,
    PecClassification,
    apply_outbound_transition,
    classify_pec_message,
    get_state_transition,
)


# ─── Helper per costruire messaggi di test ────────────────────────────────────


def make_msg(headers: dict[str, str], body: str = "") -> email.message.Message:
    """Costruisce un email.message.Message con gli header dati."""
    raw = ""
    for k, v in headers.items():
        raw += f"{k}: {v}\r\n"
    raw += f"\r\n{body}"
    return email.message_from_string(raw)


# ─── Test classificazione tipi ricevuta (provider standard / Aruba) ───────────


class TestArubaProvider:
    """Header con trattini, stile Aruba PEC (provider più diffuso)."""

    def test_accettazione(self):
        msg = make_msg({"X-Ricevuta": "accettazione"})
        result = classify_pec_message(msg)
        assert result.pec_type == "accettazione"
        assert result.is_receipt is True

    def test_avvenuta_consegna(self):
        msg = make_msg({"X-Ricevuta": "avvenuta-consegna"})
        result = classify_pec_message(msg)
        assert result.pec_type == "avvenuta_consegna"
        assert result.is_receipt is True

    def test_mancata_consegna(self):
        msg = make_msg({"X-Ricevuta": "mancata-consegna"})
        result = classify_pec_message(msg)
        assert result.pec_type == "mancata_consegna"
        assert result.is_receipt is True

    def test_non_accettazione(self):
        msg = make_msg({"X-Ricevuta": "non-accettazione"})
        result = classify_pec_message(msg)
        assert result.pec_type == "non_accettazione"
        assert result.is_receipt is True

    def test_presa_in_carico(self):
        msg = make_msg({"X-Ricevuta": "presa-in-carico"})
        result = classify_pec_message(msg)
        assert result.pec_type == "presa_in_carico"
        assert result.is_receipt is True

    def test_errore_consegna(self):
        msg = make_msg({"X-Ricevuta": "errore-consegna"})
        result = classify_pec_message(msg)
        assert result.pec_type == "errore_consegna"
        assert result.is_receipt is True

    def test_preavviso_mancata_consegna(self):
        msg = make_msg({"X-Ricevuta": "preavviso-mancata-consegna"})
        result = classify_pec_message(msg)
        assert result.pec_type == "preavviso_mancata_consegna"
        assert result.is_receipt is True

    def test_rilevazione_virus(self):
        msg = make_msg({"X-Ricevuta": "rilevazione-virus"})
        result = classify_pec_message(msg)
        assert result.pec_type == "rilevazione_virus"
        assert result.is_receipt is True

    def test_posta_certificata_assenza_header(self):
        """Messaggio PEC originale: nessun header X-Ricevuta."""
        msg = make_msg({
            "From": "mittente@pec.it",
            "To": "destinatario@pec.it",
            "Subject": "Test PEC",
        })
        result = classify_pec_message(msg)
        assert result.pec_type == "posta_certificata"
        assert result.is_receipt is False

    def test_posta_certificata_esplicita(self):
        """X-Ricevuta: posta-certificata (usato da Aruba per indicare il messaggio stesso)."""
        msg = make_msg({"X-Ricevuta": "posta-certificata"})
        result = classify_pec_message(msg)
        assert result.pec_type == "posta_certificata"
        assert result.is_receipt is False


# ─── Test provider Namirial (usa X-TipoRicevuta) ─────────────────────────────


class TestNamirialProvider:
    """Namirial usa X-TipoRicevuta invece di X-Ricevuta."""

    def test_x_tipo_ricevuta_avvenuta_consegna(self):
        msg = make_msg({"X-TipoRicevuta": "avvenuta-consegna"})
        result = classify_pec_message(msg)
        assert result.pec_type == "avvenuta_consegna"
        assert result.x_tipo_ricevuta == "avvenuta-consegna"

    def test_x_tipo_ricevuta_accettazione(self):
        msg = make_msg({"X-TipoRicevuta": "accettazione"})
        result = classify_pec_message(msg)
        assert result.pec_type == "accettazione"

    def test_x_tipo_ricevuta_ha_precedenza_su_x_ricevuta(self):
        """X-TipoRicevuta deve avere precedenza su X-Ricevuta."""
        msg = make_msg({
            "X-Ricevuta": "accettazione",
            "X-TipoRicevuta": "avvenuta-consegna",
        })
        result = classify_pec_message(msg)
        assert result.pec_type == "avvenuta_consegna"  # X-TipoRicevuta vince

    def test_mancata_consegna(self):
        msg = make_msg({"X-TipoRicevuta": "mancata-consegna"})
        result = classify_pec_message(msg)
        assert result.pec_type == "mancata_consegna"


# ─── Test provider Legalmail/Poste (varianti underscore e spazi) ──────────────


class TestLegalmailProvider:
    """Legalmail/Poste Italiane può usare underscore o spazi nei valori."""

    def test_avvenuta_consegna_underscore(self):
        msg = make_msg({"X-Ricevuta": "avvenuta_consegna"})
        result = classify_pec_message(msg)
        assert result.pec_type == "avvenuta_consegna"

    def test_non_accettazione_underscore(self):
        msg = make_msg({"X-Ricevuta": "non_accettazione"})
        result = classify_pec_message(msg)
        assert result.pec_type == "non_accettazione"

    def test_mancata_consegna_spazio(self):
        msg = make_msg({"X-Ricevuta": "mancata consegna"})
        result = classify_pec_message(msg)
        assert result.pec_type == "mancata_consegna"

    def test_avvenuta_consegna_spazio(self):
        msg = make_msg({"X-Ricevuta": "avvenuta consegna"})
        result = classify_pec_message(msg)
        assert result.pec_type == "avvenuta_consegna"

    def test_presa_in_carico_underscore(self):
        msg = make_msg({"X-Ricevuta": "presa_in_carico"})
        result = classify_pec_message(msg)
        assert result.pec_type == "presa_in_carico"

    def test_abbreviazione_consegna(self):
        """Legalmail usa talvolta solo 'consegna' come abbreviazione."""
        msg = make_msg({"X-Ricevuta": "consegna"})
        result = classify_pec_message(msg)
        assert result.pec_type == "avvenuta_consegna"

    def test_rilevazione_virus_underscore(self):
        msg = make_msg({"X-Ricevuta": "rilevazione_virus"})
        result = classify_pec_message(msg)
        assert result.pec_type == "rilevazione_virus"


# ─── Test case insensitivity ──────────────────────────────────────────────────


class TestCaseInsensitivity:
    """I valori degli header PEC devono essere trattati case-insensitive."""

    def test_uppercase_accettazione(self):
        msg = make_msg({"X-Ricevuta": "ACCETTAZIONE"})
        result = classify_pec_message(msg)
        assert result.pec_type == "accettazione"

    def test_mixed_case_avvenuta_consegna(self):
        msg = make_msg({"X-Ricevuta": "Avvenuta-Consegna"})
        result = classify_pec_message(msg)
        assert result.pec_type == "avvenuta_consegna"

    def test_uppercase_x_tipo(self):
        msg = make_msg({"X-TipoRicevuta": "MANCATA-CONSEGNA"})
        result = classify_pec_message(msg)
        assert result.pec_type == "mancata_consegna"


# ─── Test X-Riferimento-Message-ID ───────────────────────────────────────────


class TestRiferimentoMessageId:
    """Il campo X-Riferimento-Message-ID collega la ricevuta al messaggio originale."""

    def test_estrazione_riferimento(self):
        msg = make_msg({
            "X-Ricevuta": "avvenuta-consegna",
            "X-Riferimento-Message-ID": "<msg123@pec.it>",
        })
        result = classify_pec_message(msg)
        assert result.riferimento_message_id == "<msg123@pec.it>"

    def test_riferimento_assente(self):
        msg = make_msg({"X-Ricevuta": "accettazione"})
        result = classify_pec_message(msg)
        assert result.riferimento_message_id is None

    def test_posta_certificata_senza_riferimento(self):
        """I messaggi originali non hanno X-Riferimento-Message-ID."""
        msg = make_msg({"From": "mittente@pec.it", "To": "dest@pec.it"})
        result = classify_pec_message(msg)
        assert result.riferimento_message_id is None

    def test_riferimento_con_spazi_extra(self):
        """Il valore deve essere pulito da spazi iniziali/finali."""
        msg = make_msg({
            "X-Ricevuta": "accettazione",
            "X-Riferimento-Message-ID": "  <msg456@pec.it>  ",
        })
        result = classify_pec_message(msg)
        assert result.riferimento_message_id == "<msg456@pec.it>"

    def test_x_raw_headers_conservati(self):
        """I valori raw degli header devono essere conservati nel risultato."""
        msg = make_msg({
            "X-Ricevuta": "accettazione",
            "X-TipoRicevuta": "avvenuta-consegna",
        })
        result = classify_pec_message(msg)
        assert result.x_ricevuta == "accettazione"
        assert result.x_tipo_ricevuta == "avvenuta-consegna"

    def test_tipo_sconosciuto_mappa_posta_certificata(self):
        """Valori sconosciuti devono mappare a posta_certificata."""
        msg = make_msg({"X-Ricevuta": "valore-sconosciuto"})
        result = classify_pec_message(msg)
        assert result.pec_type == "posta_certificata"
        assert result.is_receipt is False


# ─── Test state machine outbound ─────────────────────────────────────────────


class TestStateMachine:
    """Test della state machine per messaggi outbound."""

    # ── get_state_transition ──────────────────────────────────────────────────

    def test_accettazione_produce_accepted(self):
        assert get_state_transition("accettazione") == "accepted"

    def test_presa_in_carico_produce_accepted(self):
        assert get_state_transition("presa_in_carico") == "accepted"

    def test_avvenuta_consegna_produce_delivered(self):
        assert get_state_transition("avvenuta_consegna") == "delivered"

    def test_non_accettazione_produce_anomaly(self):
        assert get_state_transition("non_accettazione") == "anomaly"

    def test_mancata_consegna_produce_anomaly(self):
        assert get_state_transition("mancata_consegna") == "anomaly"

    def test_errore_consegna_produce_anomaly(self):
        assert get_state_transition("errore_consegna") == "anomaly"

    def test_preavviso_mancata_produce_anomaly(self):
        assert get_state_transition("preavviso_mancata_consegna") == "anomaly"

    def test_rilevazione_virus_produce_anomaly(self):
        assert get_state_transition("rilevazione_virus") == "anomaly"

    def test_posta_certificata_nessuna_transizione(self):
        """posta_certificata non è una ricevuta: nessuna transizione."""
        assert get_state_transition("posta_certificata") is None

    def test_tipo_sconosciuto_nessuna_transizione(self):
        assert get_state_transition("unknown") is None

    # ── apply_outbound_transition ─────────────────────────────────────────────

    def test_sent_plus_accettazione_da_accepted(self):
        assert apply_outbound_transition("sent", "accettazione") == "accepted"

    def test_queued_plus_accettazione_da_accepted(self):
        assert apply_outbound_transition("queued", "accettazione") == "accepted"

    def test_accepted_plus_avvenuta_consegna_da_delivered(self):
        assert apply_outbound_transition("accepted", "avvenuta_consegna") == "delivered"

    def test_sent_plus_avvenuta_consegna_non_valido(self):
        """Non si può saltare direttamente da sent a delivered."""
        assert apply_outbound_transition("sent", "avvenuta_consegna") is None

    def test_delivered_terminal(self):
        """delivered è uno stato terminale: nessuna transizione possibile."""
        assert apply_outbound_transition("delivered", "avvenuta_consegna") is None
        assert apply_outbound_transition("delivered", "mancata_consegna") is None

    def test_anomaly_terminal(self):
        """anomaly è uno stato terminale: nessuna transizione possibile."""
        assert apply_outbound_transition("anomaly", "avvenuta_consegna") is None

    def test_sent_plus_mancata_consegna_da_anomaly(self):
        assert apply_outbound_transition("sent", "mancata_consegna") == "anomaly"

    def test_accepted_plus_non_accettazione_da_anomaly(self):
        assert apply_outbound_transition("accepted", "non_accettazione") == "anomaly"

    def test_posta_certificata_nessun_effetto(self):
        """posta_certificata non è una ricevuta: nessuna transizione."""
        assert apply_outbound_transition("sent", "posta_certificata") is None

    # ── VALID_OUTBOUND_TRANSITIONS struttura ──────────────────────────────────

    def test_valid_transitions_queued(self):
        assert "accepted" in VALID_OUTBOUND_TRANSITIONS["queued"]
        assert "anomaly" in VALID_OUTBOUND_TRANSITIONS["queued"]
        assert "delivered" not in VALID_OUTBOUND_TRANSITIONS["queued"]

    def test_valid_transitions_sent(self):
        assert "accepted" in VALID_OUTBOUND_TRANSITIONS["sent"]
        assert "anomaly" in VALID_OUTBOUND_TRANSITIONS["sent"]

    def test_valid_transitions_accepted(self):
        assert "delivered" in VALID_OUTBOUND_TRANSITIONS["accepted"]
        assert "anomaly" in VALID_OUTBOUND_TRANSITIONS["accepted"]
        assert "accepted" not in VALID_OUTBOUND_TRANSITIONS["accepted"]

    def test_delivered_non_in_valid_transitions(self):
        """delivered è terminale: non deve comparire come chiave."""
        assert "delivered" not in VALID_OUTBOUND_TRANSITIONS

    def test_anomaly_non_in_valid_transitions(self):
        """anomaly è terminale: non deve comparire come chiave."""
        assert "anomaly" not in VALID_OUTBOUND_TRANSITIONS


# ─── Test PecClassification dataclass ────────────────────────────────────────


class TestPecClassificationDataclass:
    """Test dei campi del dataclass PecClassification."""

    def test_tutti_i_campi_presenti(self):
        msg = make_msg({
            "X-Ricevuta": "accettazione",
            "X-TipoRicevuta": "avvenuta-consegna",
            "X-Riferimento-Message-ID": "<ref@pec.it>",
        })
        result = classify_pec_message(msg)
        assert isinstance(result, PecClassification)
        assert result.pec_type == "avvenuta_consegna"
        assert result.is_receipt is True
        assert result.riferimento_message_id == "<ref@pec.it>"
        assert result.x_ricevuta == "accettazione"
        assert result.x_tipo_ricevuta == "avvenuta-consegna"

    def test_messaggio_puro_tutti_none(self):
        msg = make_msg({"From": "a@pec.it", "To": "b@pec.it"})
        result = classify_pec_message(msg)
        assert result.pec_type == "posta_certificata"
        assert result.is_receipt is False
        assert result.riferimento_message_id is None
        assert result.x_ricevuta is None
        assert result.x_tipo_ricevuta is None
