#!/usr/bin/env python3
"""
Test integrazione SMTP/IMAP REALE – Casella Aruba PEC
======================================================

Testa:
1. Connessione SMTP SSL (porta 465) → smtps.pec.aruba.it
2. Invio PEC reale a matteo1801@spidmail.it
3. Connessione IMAP SSL (porta 993) → imaps.pec.aruba.it
4. Lista cartelle IMAP + ispezione completa messaggi recenti
5. Rilevazione ricevute accettazione/consegna

Eseguire DENTRO il container worker:
  docker exec -e PYTHONPATH=/worker pechub-worker-1 python \
    /worker/tests/integration/test_smtp_real_aruba.py
"""

import asyncio
import base64
import email
import email.header
import os
import sys
from datetime import datetime
from unittest.mock import MagicMock

# ─── Variabili d'ambiente ─────────────────────────────────────────────────────
if "ENCRYPTION_KEY" not in os.environ:
    os.environ["ENCRYPTION_KEY"] = "6465762d656e6372797074696f6e2d6b65792d6e6f742d666f722d70726f6400"
os.environ.setdefault("SECRET_KEY", "dev-secret-key-not-for-production-use-only-for-local-0000000000000")
os.environ.setdefault("DATABASE_URL", "postgresql+asyncpg://pechub:pechub_dev_password@db:5432/pechub")
os.environ.setdefault("REDIS_URL", "redis://redis:6379/0")
os.environ.setdefault("MINIO_ENDPOINT", "minio:9000")

# ─── Parametri PEC Aruba ─────────────────────────────────────────────────────
PEC_EMAIL    = "matteo.giustini@arubapec.it"
PEC_PASSWORD = "MadonnaPuttana1!"
SMTP_HOST    = "smtps.pec.aruba.it"
SMTP_PORT    = 465
IMAP_HOST    = "imaps.pec.aruba.it"
IMAP_PORT    = 993
TO_ADDRESS   = "matteo1801@spidmail.it"


# ─── Helpers cifratura ────────────────────────────────────────────────────────

def _encrypt(value: str) -> str:
    from cryptography.hazmat.primitives.ciphers.aead import AESGCM
    key = bytes.fromhex(os.environ["ENCRYPTION_KEY"])
    nonce = os.urandom(12)
    ct = AESGCM(key).encrypt(nonce, value.encode("utf-8"), None)
    return base64.b64encode(nonce + ct).decode("ascii")


def make_fake_mailbox() -> MagicMock:
    m = MagicMock()
    m.email_address = PEC_EMAIL
    m.smtp_host_enc = _encrypt(SMTP_HOST)
    m.smtp_port_enc = _encrypt(str(SMTP_PORT))
    m.smtp_user_enc = _encrypt(PEC_EMAIL)
    m.smtp_pass_enc = _encrypt(PEC_PASSWORD)
    m.smtp_use_tls  = True
    return m


def _decode_header(raw: str) -> str:
    """Decodifica un header MIME (gestisce encoded-words =?utf-8?...?=)."""
    try:
        parts = email.header.decode_header(raw)
        decoded = []
        for part, enc in parts:
            if isinstance(part, bytes):
                decoded.append(part.decode(enc or "utf-8", errors="replace"))
            else:
                decoded.append(part)
        return "".join(decoded)
    except Exception:
        return raw


def _is_pec_receipt(headers_text: str) -> tuple[bool, str]:
    """
    Determina se un messaggio è una ricevuta PEC.
    Restituisce (is_receipt, tipo_ricevuta).
    """
    lower = headers_text.lower()

    # Header proprietari Aruba/PEC
    receipt_markers = [
        ("x-ricevuta:", "ricevuta"),
        ("x-trasporto:", "posta-certificata"),
        ("x-tiporicevuta:", "tipo-ricevuta"),
        ("x-riferimentomessaggioid:", "riferimento"),
    ]
    for marker, label in receipt_markers:
        if marker in lower:
            return True, label

    # Pattern nel Subject
    subject_keywords = [
        "accettazione:",
        "avvenuta consegna:",
        "avvenuta-consegna:",
        "errore di consegna:",
        "posta certificata:",
        "ricevuta di accettazione",
        "ricevuta di avvenuta consegna",
    ]
    for kw in subject_keywords:
        if kw in lower:
            return True, f"subject-match:{kw}"

    return False, ""


# ─── STEP 0: Connettività SMTP ────────────────────────────────────────────────

async def test_smtp_connectivity() -> bool:
    import aiosmtplib

    _sep()
    print("STEP 0 – VERIFICA CONNETTIVITÀ SMTP")
    _sep()

    try:
        smtp = aiosmtplib.SMTP(
            hostname=SMTP_HOST, port=SMTP_PORT,
            use_tls=True, start_tls=False, timeout=15,
        )
        await smtp.connect()
        print(f"  ✅ TCP+SSL OK → {SMTP_HOST}:{SMTP_PORT}")
        await smtp.login(PEC_EMAIL, PEC_PASSWORD)
        print(f"  ✅ Autenticazione SMTP OK")
        exts = list((smtp.esmtp_extensions or {}).keys())
        print(f"  ℹ️  ESMTP: {exts}")
        await smtp.quit()
        print(f"  ✅ QUIT OK")
        return True
    except Exception as exc:
        print(f"  ❌ ERRORE: {exc}")
        import traceback; traceback.print_exc()
        return False


# ─── STEP 1: Invio SMTP ───────────────────────────────────────────────────────

async def test_smtp_send() -> tuple[str | None, bool]:
    try:
        from app.config import get_settings
        get_settings.cache_clear()
    except Exception:
        pass

    from app.smtp.sender import SmtpSender

    mailbox = make_fake_mailbox()
    sender  = SmtpSender(mailbox)

    ts      = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    subject = f"[PEChub TEST] Verifica SMTP+Ricevute – {ts}"
    body    = (
        f"Messaggio di test automatico PEChub – {ts}\n\n"
        "Verifica:\n"
        "  1. Connessione SMTP SSL porta 465 → smtps.pec.aruba.it\n"
        "  2. Autenticazione e invio PEC\n"
        "  3. Ricezione ricevuta di accettazione\n"
        "  4. Ricezione ricevuta di avvenuta consegna\n\n"
        f"Mittente : {PEC_EMAIL}\n"
        f"Destinato: {TO_ADDRESS}\n\n"
        "Non rispondere. Generato da PEChub SaaS.\n"
    )

    _sep()
    print("STEP 1 – INVIO SMTP")
    _sep()
    print(f"  Da     : {PEC_EMAIL}")
    print(f"  A      : {TO_ADDRESS}")
    print(f"  Host   : {SMTP_HOST}:{SMTP_PORT} (SSL)")
    print(f"  Oggetto: {subject}")
    print()

    try:
        message_id, raw_eml = await sender.send(
            to_addresses=[TO_ADDRESS],
            cc_addresses=[],
            subject=subject,
            body_text=body,
        )
        print(f"  ✅ INVIO OK")
        print(f"     Message-ID : {message_id}")
        print(f"     EML size   : {len(raw_eml)} bytes")
        return message_id, True
    except Exception as exc:
        print(f"  ❌ ERRORE SMTP: {exc}")
        import traceback; traceback.print_exc()
        return None, False


# ─── STEP 2: Ispezione IMAP completa ─────────────────────────────────────────

async def test_imap_full_inspection() -> None:
    """
    Connessione IMAP completa:
    1. Lista tutte le cartelle
    2. Ispeziona INBOX con fetch header completo
    3. Cerca ricevute PEC in tutte le cartelle rilevanti
    """
    import aioimaplib

    _sep()
    print("STEP 2 – ISPEZIONE IMAP COMPLETA")
    _sep()
    print(f"  Host : {IMAP_HOST}:{IMAP_PORT} (SSL)")
    print(f"  User : {PEC_EMAIL}")
    print()

    try:
        client = aioimaplib.IMAP4_SSL(host=IMAP_HOST, port=IMAP_PORT, timeout=30)
        await asyncio.wait_for(client.wait_hello_from_server(), timeout=30)

        status, _ = await client.login(PEC_EMAIL, PEC_PASSWORD)
        if status != "OK":
            print(f"  ❌ Login IMAP fallito: {status}")
            return
        print(f"  ✅ Login IMAP riuscito")

        # ── Lista cartelle ────────────────────────────────────────────────────
        status, folder_data = await client.list('""', "*")
        folders = []
        if status == "OK":
            for item in folder_data:
                if not item:
                    continue
                raw = item.decode("utf-8", errors="replace") if isinstance(item, bytes) else str(item)
                # Estrai il nome cartella (ultima parte dopo l'ultimo separatore)
                if '"/"' in raw or "|" in raw or raw.strip():
                    # Formato tipico: (\HasChildren) "/" "INBOX"
                    parts = raw.split('"')
                    if len(parts) >= 3:
                        folder_name = parts[-2].strip()
                        if folder_name:
                            folders.append(folder_name)
                    elif raw.strip():
                        # Prova a estrarre diversamente
                        tokens = raw.split()
                        if tokens:
                            folders.append(tokens[-1].strip('"'))

        # Rimuovi duplicati mantenendo ordine
        seen = set()
        unique_folders = []
        for f in folders:
            if f not in seen and f:
                seen.add(f)
                unique_folders.append(f)

        print(f"  📂 Cartelle IMAP disponibili ({len(unique_folders)}):")
        for f in unique_folders:
            print(f"     • {f}")
        print()

        # ── Cartelle da ispezionare per ricevute ──────────────────────────────
        # Aruba PEC mette ricevute in INBOX, ma può avere cartelle dedicate
        priority_folders = ["INBOX"]
        for f in unique_folders:
            fl = f.lower()
            if any(kw in fl for kw in ["ricevut", "certif", "sent", "inviati", "outbox", "pec"]):
                if f not in priority_folders:
                    priority_folders.append(f)

        all_receipts: list[dict] = []

        for folder in priority_folders:
            print(f"  📁 Ispezione cartella: {folder!r}")
            print(f"  {'─' * 50}")

            try:
                status, sel_data = await client.select(folder)
                if status != "OK":
                    print(f"     ⚠️  SELECT fallito: {status}")
                    print()
                    continue

                # Conta messaggi
                total = 0
                for line in sel_data:
                    if isinstance(line, bytes) and b"EXISTS" in line:
                        try:
                            total = int(line.split()[0])
                        except Exception:
                            pass
                print(f"     📬 Messaggi totali: {total}")

                if total == 0:
                    print(f"     (cartella vuota)")
                    print()
                    continue

                # Fetch ultimi 10 messaggi (per numero sequenza)
                start_seq = max(1, total - 9)
                status, fetch_data = await client.fetch(
                    f"{start_seq}:{total}",
                    "(BODY.PEEK[HEADER])"
                )

                if status != "OK":
                    print(f"     ⚠️  FETCH fallito: {status}")
                    print()
                    continue

                # Parsa i risultati fetch
                messages_found: list[dict] = []
                current_headers = []
                current_seq = None

                for part in fetch_data:
                    if not part:
                        continue
                    if isinstance(part, bytes):
                        raw = part.decode("utf-8", errors="replace")
                    else:
                        raw = str(part)

                    # Linea di risposta FETCH (es: "1 FETCH (BODY[HEADER] {1234}")
                    if "FETCH" in raw and "BODY" in raw:
                        # Salva il messaggio precedente se esiste
                        if current_headers and current_seq is not None:
                            hdr_text = "\n".join(current_headers)
                            messages_found.append({
                                "seq": current_seq,
                                "headers": hdr_text,
                            })
                        # Estrai numero sequenza
                        try:
                            current_seq = int(raw.split()[0])
                        except Exception:
                            current_seq = "?"
                        current_headers = []
                    elif raw.strip() == ")":
                        # Fine del fetch per questo messaggio
                        if current_headers and current_seq is not None:
                            hdr_text = "\n".join(current_headers)
                            messages_found.append({
                                "seq": current_seq,
                                "headers": hdr_text,
                            })
                            current_headers = []
                            current_seq = None
                    else:
                        # Corpo dell'header
                        if current_seq is not None:
                            current_headers.append(raw)

                # Aggiungi l'ultimo messaggio pendente
                if current_headers and current_seq is not None:
                    messages_found.append({
                        "seq": current_seq,
                        "headers": "\n".join(current_headers),
                    })

                # De-duplicazione per seq
                seen_seqs: set = set()
                unique_msgs: list[dict] = []
                for m in messages_found:
                    if m["seq"] not in seen_seqs:
                        seen_seqs.add(m["seq"])
                        unique_msgs.append(m)

                print(f"     🔍 Analizzati {len(unique_msgs)} messaggi recenti")
                print()

                for msg in unique_msgs:
                    hdrs = msg["headers"]
                    is_receipt, receipt_type = _is_pec_receipt(hdrs)

                    # Estrai soggetto
                    subject_raw = ""
                    for line in hdrs.splitlines():
                        if line.lower().startswith("subject:"):
                            subject_raw = line[8:].strip()
                            break
                    subject_decoded = _decode_header(subject_raw) if subject_raw else "(nessun oggetto)"

                    # Estrai From
                    from_raw = ""
                    for line in hdrs.splitlines():
                        if line.lower().startswith("from:"):
                            from_raw = line[5:].strip()
                            break

                    # Estrai Date
                    date_raw = ""
                    for line in hdrs.splitlines():
                        if line.lower().startswith("date:"):
                            date_raw = line[5:].strip()
                            break

                    # Estrai headers PEC specifici
                    x_ricevuta = ""
                    x_trasporto = ""
                    for line in hdrs.splitlines():
                        ll = line.lower()
                        if ll.startswith("x-ricevuta:"):
                            x_ricevuta = line.split(":", 1)[1].strip()
                        elif ll.startswith("x-trasporto:"):
                            x_trasporto = line.split(":", 1)[1].strip()

                    if is_receipt:
                        marker = "🏆 RICEVUTA PEC"
                        all_receipts.append({
                            "folder": folder,
                            "seq": msg["seq"],
                            "subject": subject_decoded,
                            "from": from_raw,
                            "date": date_raw,
                            "x_ricevuta": x_ricevuta,
                            "x_trasporto": x_trasporto,
                            "receipt_type": receipt_type,
                        })
                    else:
                        marker = "📧"

                    print(f"     {marker} [seq={msg['seq']}]")
                    print(f"       Subject   : {subject_decoded}")
                    print(f"       From      : {from_raw[:80] if from_raw else '(n/a)'}")
                    print(f"       Date      : {date_raw[:60] if date_raw else '(n/a)'}")
                    if x_ricevuta:
                        print(f"       X-Ricevuta: {x_ricevuta}")
                    if x_trasporto:
                        print(f"       X-Trasporto: {x_trasporto}")
                    print()

            except Exception as folder_err:
                print(f"     ❌ Errore cartella {folder!r}: {folder_err}")
                print()

        # ── Riepilogo ricevute ────────────────────────────────────────────────
        _sep()
        print("RIEPILOGO RICEVUTE PEC")
        _sep()

        if all_receipts:
            print(f"  ✅ TROVATE {len(all_receipts)} RICEVUTA/E PEC:\n")
            for r in all_receipts:
                print(f"  ── {r['folder']} / seq={r['seq']} ──")
                print(f"     Subject    : {r['subject']}")
                print(f"     From       : {r['from']}")
                print(f"     Date       : {r['date']}")
                print(f"     X-Ricevuta : {r.get('x_ricevuta', '-')}")
                print(f"     X-Trasporto: {r.get('x_trasporto', '-')}")
                print(f"     Tipo       : {r['receipt_type']}")
                print()
        else:
            print("  ⚠️  Nessuna ricevuta PEC trovata.")
            print("     Possibili cause:")
            print("     • Le ricevute non sono ancora arrivate (Aruba PEC: <30s normalmente)")
            print("     • La casella destinatario non è PEC → ricevuta solo di accettazione")
            print("     • I messaggi in INBOX non sono ancora stati aggiornati")
            print()
            print("  💡 Ri-esegui lo STEP 2 manualmente tra qualche secondo con:")
            print("     docker exec -e PYTHONPATH=/worker pechub-worker-1 python -c \"")
            print("       import asyncio, sys; sys.path.insert(0,'/worker')")
            print("       from tests.integration.test_smtp_real_aruba import test_imap_full_inspection")
            print("       asyncio.run(test_imap_full_inspection())\"")

        await client.logout()
        print(f"\n  ✅ Logout IMAP completato")

    except asyncio.TimeoutError:
        print(f"  ❌ Timeout IMAP ({IMAP_HOST}:{IMAP_PORT})")
    except Exception as exc:
        print(f"  ❌ ERRORE IMAP: {exc}")
        import traceback; traceback.print_exc()


# ─── Utilities ────────────────────────────────────────────────────────────────

def _sep(char: str = "─", width: int = 60) -> None:
    print(char * width)


def _banner() -> None:
    _sep("═")
    print("  PEChub – Test SMTP/IMAP Reale (Aruba PEC)")
    _sep("═")
    print(f"  Timestamp   : {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    print(f"  Casella PEC : {PEC_EMAIL}")
    print(f"  SMTP        : {SMTP_HOST}:{SMTP_PORT} (SSL)")
    print(f"  IMAP        : {IMAP_HOST}:{IMAP_PORT} (SSL)")
    print(f"  Destinatario: {TO_ADDRESS}")
    _sep("═")
    print()


# ─── Main ─────────────────────────────────────────────────────────────────────

async def main() -> None:
    _banner()

    # STEP 0: connettività SMTP
    conn_ok = await test_smtp_connectivity()
    print()
    if not conn_ok:
        print("❌ Connettività SMTP fallita.")
        sys.exit(1)

    # STEP 1: invio
    message_id, send_ok = await test_smtp_send()
    print()
    if not send_ok:
        print("❌ Invio SMTP fallito – skip verifica ricevute.")
        sys.exit(1)

    # Attendi ricevute
    wait_secs = 45
    print(f"⏳ Attesa {wait_secs}s per ricezione ricevute Aruba...")
    for r in range(wait_secs, 0, -5):
        print(f"   {r}s...", end="\r", flush=True)
        await asyncio.sleep(5)
    print(f"   Attesa completata.          ")
    print()

    # STEP 2: ispezione IMAP completa
    await test_imap_full_inspection()

    print()
    _sep("═")
    print("  TEST COMPLETATO")
    _sep("═")
    if message_id:
        print(f"  Message-ID: {message_id}")
    print()


if __name__ == "__main__":
    asyncio.run(main())
