"""
Test unitari per SmtpSender.

Verifica la costruzione del messaggio MIME senza connessioni SMTP reali.
Il test del send() effettivo verso server reali è un test di integrazione
(eseguito separatamente con flag --real-smtp).
"""

import email as email_lib
import email.policy
from email.mime.multipart import MIMEMultipart

import pytest

# ─── Chiave test fissa – deve coincidere con ENCRYPTION_KEY ──────────────────
_TEST_KEY_HEX = "b" * 64

# Imposta la variabile d'ambiente e invalida la cache prima di qualsiasi import
import os as _os
_os.environ["ENCRYPTION_KEY"] = _TEST_KEY_HEX
_os.environ.setdefault("SECRET_KEY", "test-secret-worker")

# Invalida la cache di get_settings se già caricata
try:
    from app.config import get_settings as _gs
    _gs.cache_clear()
except Exception:
    pass
# ─────────────────────────────────────────────────────────────────────────────


# ─── Fixtures helper ─────────────────────────────────────────────────────────


def _make_fake_mailbox():
    """Crea un oggetto mailbox-like con attributi minimi per SmtpSender."""
    import base64
    import os
    from unittest.mock import MagicMock

    from cryptography.hazmat.primitives.ciphers.aead import AESGCM

    key = bytes.fromhex(_TEST_KEY_HEX)

    def _enc(value: str) -> str:
        nonce = os.urandom(12)
        ct = AESGCM(key).encrypt(nonce, value.encode(), None)
        return base64.b64encode(nonce + ct).decode()

    mailbox = MagicMock()
    mailbox.email_address = "test@pec.example.it"
    mailbox.smtp_host_enc = _enc("smtp.example.it")
    mailbox.smtp_port_enc = _enc("465")
    mailbox.smtp_user_enc = _enc("test@pec.example.it")
    mailbox.smtp_pass_enc = _enc("secret")
    mailbox.smtp_use_tls = True

    return mailbox


# ─── Test costruzione MIME ────────────────────────────────────────────────────


class TestBuildMimeMessage:
    """Verifica la costruzione del messaggio MIME con varie combinazioni."""

    def _get_sender(self):
        """Restituisce SmtpSender con mailbox mock."""
        # La chiave è già impostata a livello di modulo (_TEST_KEY_HEX)
        from app.config import get_settings
        get_settings.cache_clear()  # forza rilettura env var

        from app.smtp.sender import SmtpSender
        return SmtpSender(_make_fake_mailbox())

    def test_build_plain_text_only(self):
        """Verifica struttura MIME con solo testo semplice."""
        sender = self._get_sender()
        msg, msg_id = sender.build_mime_message(
            to_addresses=["dest@pec.it"],
            cc_addresses=[],
            subject="Test oggetto",
            body_text="Testo del corpo",
        )

        assert isinstance(msg, MIMEMultipart)
        assert msg["From"] == "test@pec.example.it"
        assert msg["To"] == "dest@pec.it"
        assert msg["Subject"] == "Test oggetto"
        assert msg_id.startswith("<")
        assert msg_id.endswith(">")
        assert "Cc" not in msg

    def test_build_with_cc(self):
        """Verifica che il campo Cc venga incluso correttamente."""
        sender = self._get_sender()
        msg, _ = sender.build_mime_message(
            to_addresses=["dest1@pec.it"],
            cc_addresses=["cc@pec.it", "cc2@pec.it"],
            subject="Test Cc",
            body_text="corpo",
        )

        assert "Cc" in msg
        assert "cc@pec.it" in msg["Cc"]
        assert "cc2@pec.it" in msg["Cc"]

    def test_build_multiple_to(self):
        """Verifica destinatari multipli nel campo To."""
        sender = self._get_sender()
        msg, _ = sender.build_mime_message(
            to_addresses=["dest1@pec.it", "dest2@pec.it"],
            cc_addresses=[],
            subject="Multi dest",
            body_text="corpo",
        )

        assert "dest1@pec.it" in msg["To"]
        assert "dest2@pec.it" in msg["To"]

    def test_build_with_html(self):
        """Verifica che corpo HTML venga aggiunto come parte MIME."""
        sender = self._get_sender()
        msg, _ = sender.build_mime_message(
            to_addresses=["dest@pec.it"],
            cc_addresses=[],
            subject="Test HTML",
            body_text="Testo semplice",
            body_html="<p>Testo <b>HTML</b></p>",
        )

        # Trova le parti del messaggio
        raw = msg.as_string()
        assert "text/plain" in raw
        assert "text/html" in raw

    def test_build_with_attachment(self):
        """Verifica che un allegato venga incluso nel messaggio."""
        sender = self._get_sender()
        attachments = [
            {
                "filename": "documento.pdf",
                "content": b"%PDF-1.4 fake content",
                "content_type": "application/pdf",
            }
        ]
        msg, _ = sender.build_mime_message(
            to_addresses=["dest@pec.it"],
            cc_addresses=[],
            subject="Test allegato",
            body_text="Vedi allegato",
            attachments=attachments,
        )

        raw = msg.as_string()
        assert "documento.pdf" in raw

    def test_build_multiple_attachments(self):
        """Verifica più allegati in un unico messaggio."""
        sender = self._get_sender()
        attachments = [
            {"filename": "file1.txt", "content": b"contenuto 1", "content_type": "text/plain"},
            {"filename": "file2.txt", "content": b"contenuto 2", "content_type": "text/plain"},
        ]
        msg, _ = sender.build_mime_message(
            to_addresses=["dest@pec.it"],
            cc_addresses=[],
            subject="Multi allegati",
            body_text="Due allegati",
            attachments=attachments,
        )

        raw = msg.as_string()
        assert "file1.txt" in raw
        assert "file2.txt" in raw

    def test_message_id_unique(self):
        """Verifica che ogni messaggio abbia un Message-ID unico."""
        sender = self._get_sender()
        _, id1 = sender.build_mime_message(
            to_addresses=["a@pec.it"], cc_addresses=[], subject="A", body_text="a"
        )
        _, id2 = sender.build_mime_message(
            to_addresses=["b@pec.it"], cc_addresses=[], subject="B", body_text="b"
        )
        assert id1 != id2

    def test_required_headers_present(self):
        """Verifica che tutti gli header obbligatori siano presenti."""
        sender = self._get_sender()
        msg, _ = sender.build_mime_message(
            to_addresses=["dest@pec.it"],
            cc_addresses=[],
            subject="Test headers",
            body_text="corpo",
        )

        required_headers = ["From", "To", "Subject", "Date", "Message-ID", "MIME-Version"]
        for header in required_headers:
            assert header in msg, f"Header mancante: {header}"

    def test_eml_bytes_serializable(self):
        """Verifica che il messaggio sia serializzabile in bytes."""
        sender = self._get_sender()
        msg, _ = sender.build_mime_message(
            to_addresses=["dest@pec.it"],
            cc_addresses=[],
            subject="Serializzazione",
            body_text="corpo",
        )

        raw = msg.as_bytes()
        assert len(raw) > 0
        assert isinstance(raw, bytes)

    def test_empty_body_creates_valid_message(self):
        """Verifica che un messaggio con corpo vuoto sia comunque valido."""
        sender = self._get_sender()
        msg, _ = sender.build_mime_message(
            to_addresses=["dest@pec.it"],
            cc_addresses=[],
            subject="Corpo vuoto",
            body_text="",
        )

        raw = msg.as_bytes()
        assert len(raw) > 0


# ─── Test decifrazione credenziali ───────────────────────────────────────────


class TestDecryptSmtpCredentials:
    """Verifica la decifrazione delle credenziali SMTP."""

    def test_decrypt_returns_correct_values(self):
        """Le credenziali decifrate devono corrispondere ai valori originali."""
        from app.config import get_settings
        get_settings.cache_clear()

        from app.smtp.sender import decrypt_smtp_credentials

        mailbox = _make_fake_mailbox()
        creds = decrypt_smtp_credentials(mailbox)

        assert creds["host"] == "smtp.example.it"
        assert creds["port"] == 465
        assert creds["user"] == "test@pec.example.it"
        assert creds["password"] == "secret"
        assert creds["use_tls"] is True

    def test_wrong_key_raises_error(self):
        """Una chiave errata deve sollevare un'eccezione."""
        import os
        from app.config import get_settings

        # Imposta chiave sbagliata
        os.environ["ENCRYPTION_KEY"] = "a" * 64
        get_settings.cache_clear()

        from app.smtp.sender import decrypt_smtp_credentials
        mailbox = _make_fake_mailbox()  # cifrato con chiave "b"*64

        with pytest.raises(Exception):
            decrypt_smtp_credentials(mailbox)

        # Ripristina chiave corretta per test successivi
        os.environ["ENCRYPTION_KEY"] = _TEST_KEY_HEX
        get_settings.cache_clear()
