"""
BizNode Pro — Email Update Receiver
=====================================
Polls the node owner's email (IMAP) for update packets from BizNode admin.
Downloads PDF attachments, processes them through packet_processor.

Runs as a scheduled job (every 5 minutes) or on-demand.

Usage:
    python -m updates.email_receiver              # Check once
    python -m updates.email_receiver --watch 300   # Poll every 5 min
"""

from __future__ import annotations

import email
import imaplib
import logging
import os
import sys
import tempfile
import time
from pathlib import Path

logger = logging.getLogger("updates.email_receiver")

_PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
if str(_PROJECT_ROOT) not in sys.path:
    sys.path.insert(0, str(_PROJECT_ROOT))

try:
    from dotenv import load_dotenv
    load_dotenv(_PROJECT_ROOT / ".env")
except ImportError:
    pass

# IMAP config from .env
IMAP_HOST = os.getenv("IMAP_HOST", "imap.gmail.com")
IMAP_PORT = int(os.getenv("IMAP_PORT", "993"))
IMAP_USER = os.getenv("IMAP_USER", "") or os.getenv("SMTP_USER", "")
IMAP_PASS = os.getenv("IMAP_PASSWORD", "") or os.getenv("SMTP_PASSWORD", "")

# Only process emails from BizNode admin
ADMIN_SENDER = os.getenv("BIZNODE_EMAIL", "biznode@1bz.biz")

# Where to store downloaded packets
INBOX_DIR = _PROJECT_ROOT / "updates" / "inbox"
PROCESSED_DIR = _PROJECT_ROOT / "updates" / "processed"


def check_email() -> list[str]:
    """
    Check email for BizNode update packets.
    Downloads PDF attachments named 'packet_*.pdf'.
    Returns list of downloaded file paths.
    """
    if not IMAP_USER or not IMAP_PASS:
        logger.error("IMAP not configured (set IMAP_USER/IMAP_PASSWORD or SMTP_USER/SMTP_PASSWORD)")
        return []

    INBOX_DIR.mkdir(parents=True, exist_ok=True)
    downloaded = []

    try:
        mail = imaplib.IMAP4_SSL(IMAP_HOST, IMAP_PORT)
        mail.login(IMAP_USER, IMAP_PASS)
        mail.select("INBOX")

        # Search for unread emails from BizNode admin with PDF attachment
        _, msg_ids = mail.search(None, f'(UNSEEN FROM "{ADMIN_SENDER}" SUBJECT "Update Packet")')

        if not msg_ids[0]:
            logger.debug("No new update packets in email")
            mail.logout()
            return []

        for msg_id in msg_ids[0].split():
            try:
                _, msg_data = mail.fetch(msg_id, "(RFC822)")
                raw = msg_data[0][1]
                msg = email.message_from_bytes(raw)

                subject = msg.get("Subject", "")
                sender = msg.get("From", "")
                logger.info("Processing email: %s from %s", subject, sender)

                # Walk through parts looking for PDF attachments
                for part in msg.walk():
                    if part.get_content_type() != "application/pdf":
                        continue

                    filename = part.get_filename() or ""
                    if not filename.startswith("packet_") or not filename.endswith(".pdf"):
                        continue

                    # Download
                    pdf_data = part.get_payload(decode=True)
                    save_path = INBOX_DIR / filename

                    # Don't re-download if already processed
                    if (PROCESSED_DIR / filename).exists():
                        logger.debug("Already processed: %s", filename)
                        continue

                    save_path.write_bytes(pdf_data)
                    downloaded.append(str(save_path))
                    logger.info("Downloaded: %s (%d bytes)", filename, len(pdf_data))

                # Mark as read
                mail.store(msg_id, "+FLAGS", "\\Seen")

            except Exception as exc:
                logger.error("Error processing email %s: %s", msg_id, exc)

        mail.logout()

    except imaplib.IMAP4.error as exc:
        logger.error("IMAP error: %s", exc)
    except Exception as exc:
        logger.error("Email check failed: %s", exc)

    return downloaded


def process_downloads() -> list[dict]:
    """Process all downloaded packets in the inbox."""
    from updates.packet_processor import process_packet

    PROCESSED_DIR.mkdir(parents=True, exist_ok=True)
    results = []

    inbox = INBOX_DIR
    if not inbox.exists():
        return results

    for pdf in sorted(inbox.glob("packet_*.pdf")):
        logger.info("Processing: %s", pdf.name)

        try:
            result = process_packet(str(pdf))
            results.append({
                "file": pdf.name,
                "success": result.success,
                "message": result.message,
                "type": result.update_type,
                "version": result.version,
            })

            if result.success:
                # Move to processed
                pdf.rename(PROCESSED_DIR / pdf.name)
                logger.info("Applied: %s v%s — %s", result.update_type, result.version, result.message)
            else:
                if "already applied" in result.message.lower():
                    pdf.rename(PROCESSED_DIR / pdf.name)
                    logger.info("Skipped (already applied): %s", pdf.name)
                else:
                    logger.warning("Failed: %s — %s", pdf.name, result.message)

        except Exception as exc:
            logger.error("Error processing %s: %s", pdf.name, exc)
            results.append({"file": pdf.name, "success": False, "message": str(exc)})

    return results


def check_and_process() -> list[dict]:
    """Full cycle: check email → download → process."""
    downloaded = check_email()
    if downloaded:
        logger.info("Downloaded %d packet(s), processing...", len(downloaded))
    return process_downloads()


def watch(interval: int = 300):
    """Poll email at regular intervals."""
    logger.info("Watching for update packets every %d seconds...", interval)
    while True:
        try:
            results = check_and_process()
            for r in results:
                status = "OK" if r["success"] else "FAIL"
                logger.info("[%s] %s: %s", status, r.get("file", "?"), r.get("message", ""))
        except Exception as exc:
            logger.error("Watch cycle error: %s", exc)

        time.sleep(interval)


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")

    if "--watch" in sys.argv:
        idx = sys.argv.index("--watch")
        interval = int(sys.argv[idx + 1]) if idx + 1 < len(sys.argv) else 300
        watch(interval)
    else:
        results = check_and_process()
        for r in results:
            status = "SUCCESS" if r["success"] else "FAILED"
            print(f"  [{status}] {r.get('file', '?')}: {r.get('message', '')}")
        if not results:
            print("  No packets to process.")
