#!/usr/bin/env python3
"""
Dagelijks overzicht van automatisch gesorteerde mail.

Zoekt in alle IMAP-mappen (behalve Inbox, Sent, Trash, Spam, Drafts) naar
berichten die gisteren zijn binnengekomen, en stuurt een HTML-samenvatting
naar hans@australius.nl.

Eenmalige setup:  python3 dagelijks_overzicht.py --setup
Handmatig testen: python3 dagelijks_overzicht.py [--gisteren | --datum 2026-07-01] [--mail-to adres]
Plannen (macOS):  zie dagelijks_overzicht.plist
"""

import imaplib
import email
import email.header
import email.utils
import sys
import ssl
import smtplib
import re
import html as html_lib
import urllib.parse
import config
from datetime import date, timedelta, datetime
from collections import defaultdict
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from imap_utils import list_folders, quote_mailbox
from babel.dates import format_date
import babel
import random

# Mappen die NIET in het overzicht komen (systeem + inbox zelf)
UITSLUITINGEN = {
    "INBOX", "INBOX.Sent", "INBOX.Trash", "INBOX.Spam",
    "INBOX.Drafts", "INBOX.Spam", "INBOX.Archief",
    "INBOX.Archief.2020", "INBOX.Archief.2021", "INBOX.Archief.2022",
    "INBOX.Archief.2023", "INBOX.Archief.2024", "INBOX.Archief.2025",
    "INBOX.Archief.2026",
}
# Patroon: archief-mappen en technisch-interne mappen overslaan
UITSLUIT_PATRONEN = re.compile(
    r"^INBOX\.(Archief|Trash|Spam|Sent|Drafts|Technisch)", re.I
)


# ── config ────────────────────────────────────────────────────────────────────

def load_config():
    """Laad account via centrale config.py. Gebruik --account om te wisselen."""
    cfg = config.load()
    return {
        "username":  cfg.username,
        "password":  cfg.password,
        "imap_host": cfg.imap_host,
        "imap_port": cfg.imap_port,
        "smtp_host": cfg.smtp_host,
        "smtp_port": cfg.smtp_port,
        "from":      cfg.username,
        "to":        cfg.username,   # stuur digest naar zichzelf
    }


def arg_value(args: list[str], flag: str) -> str | None:
    """Return the value after a simple command-line flag."""
    if flag not in args:
        return None
    idx = args.index(flag)
    if idx + 1 >= len(args):
        print(f"Ontbrekende waarde voor {flag}")
        sys.exit(2)
    return args[idx + 1]


# ── helpers ───────────────────────────────────────────────────────────────────

def decode_hdr(value) -> str:
    if not value:
        return ""
    try:
        parts = email.header.decode_header(value)
        out = []
        for raw, cs in parts:
            if isinstance(raw, bytes):
                out.append(raw.decode(cs or "utf-8", errors="replace"))
            else:
                out.append(str(raw))
        return " ".join(out).strip()
    except Exception:
        return str(value or "")


def imap_date(d: date) -> str:
    """Zet date om naar IMAP-zoekformaat: 02-Jul-2026"""
    return d.strftime("%d-%b-%Y")


def leesbare_map(imap_naam: str) -> str:
    """Maak de mapnaam leesbaar door INBOX. te verwijderen."""
    return imap_naam.removeprefix("INBOX.").replace(".", " › ")


def apple_mail_url(message_id: str) -> str:
    """Return an Apple Mail message:// URL for a Message-ID header."""
    clean = (message_id or "").strip()
    if not clean:
        return ""
    if not (clean.startswith("<") and clean.endswith(">")):
        clean = f"<{clean.strip('<>')}>"
    return "message://" + urllib.parse.quote(clean, safe="")


def fetch_body(data) -> bytes | None:
    """Return the first bytes payload from an IMAP FETCH response."""
    if not data:
        return None
    for item in data:
        if isinstance(item, tuple) and len(item) >= 2 and isinstance(item[1], bytes):
            return item[1]
    return None

def invalid_locale(code):
    return babel.Locale(code).language_name is None

def get_random_locale() -> (str, str, str):
    """Return the locale code, the local name and the english name of a language."""
    all_ids = babel.localedata.locale_identifiers()
    code = all_ids[random.randrange(0, len(all_ids))]
    while (invalid_locale(code)):
        code = all_ids[random.randrange(0, len(all_ids))]
    return (code, babel.Locale(code).language_name, babel.Locale(code).english_name)

def create_mail_subject(zoekdatum, random = False) -> str:

    # Als random = true, toon dan de datum ook in een willekeurige andere taal
    # Anders alleen in het Nederlands

    datum_nl = format_date(zoekdatum, "EEEE d MMMM yyyy", locale="nl") 
    if random:
        random_locale, random_lang, random_elang = get_random_locale()
        datum_bl = format_date(zoekdatum, locale=random_locale)
        return f"Mailoverzicht van {datum_bl} ({random_lang} - {random_elang}) of {datum_nl}"
    else:
        return f"Mailoverzicht van {datum_nl}"

# ── IMAP ophalen ──────────────────────────────────────────────────────────────

def haal_berichten_op(mail: imaplib.IMAP4_SSL, zoekdatum: date) -> dict[str, list[dict]]:
    """
    Doorzoek alle relevante mappen op berichten van zoekdatum.
    Geeft {mapnaam: [{from, subject, time}, ...]} terug.
    """
    since  = imap_date(zoekdatum)
    before = imap_date(zoekdatum + timedelta(days=1))

    alle_mappen = list_folders(mail)
    if not alle_mappen:
        return {}

    resultaat: dict[str, list[dict]] = defaultdict(list)

    for folder in sorted(alle_mappen):
        if folder in UITSLUITINGEN:
            continue
        if UITSLUIT_PATRONEN.match(folder):
            continue

        status, data = mail.select(quote_mailbox(folder), readonly=True)
        if status != "OK":
            continue

        # Zoek op INTERNALDATE (serverontvangst, niet Date-header)
        status, data = mail.search(
            None, f'SINCE {since} BEFORE {before}'
        )
        if status != "OK" or not data[0]:
            continue

        ids = data[0].split()
        if not ids:
            continue

        for uid in ids:
            status, msg_data = mail.fetch(
                uid, "(BODY.PEEK[HEADER.FIELDS (FROM SUBJECT DATE MESSAGE-ID)])"
            )
            raw_header = fetch_body(msg_data)
            if status != "OK" or raw_header is None:
                continue

            msg      = email.message_from_bytes(raw_header)
            from_raw = decode_hdr(msg.get("From", ""))
            subject  = decode_hdr(msg.get("Subject", "(geen onderwerp)"))
            date_str = msg.get("Date", "")
            message_id = (msg.get("Message-ID") or "").strip()

            # Tijdstip opmaken
            try:
                dt = email.utils.parsedate_to_datetime(date_str)
                tijdstip = dt.strftime("%H:%M")
            except Exception:
                tijdstip = "?"

            # Afzendernaam uit From-header
            m = re.match(r'^"?([^"<@\n]{2,})"?\s*<', from_raw)
            afzender = m.group(1).strip() if m else from_raw.split("@")[0]

            resultaat[folder].append({
                "from": afzender[:40],
                "subject": subject[:80],
                "time": tijdstip,
                "message_id": message_id,
            })

    return resultaat


# ── HTML opbouwen ─────────────────────────────────────────────────────────────

def bouw_html(berichten: dict[str, list[dict]], zoekdatum: date) -> str:
    datum_nl = zoekdatum.strftime("%-d %B %Y")
    totaal   = sum(len(v) for v in berichten.values())

    html = f"""<!DOCTYPE html>
<html lang="nl">
<head>
<meta charset="utf-8">
<style>
  body      {{ font-family: -apple-system, Arial, sans-serif; font-size: 14px;
               color: #222; max-width: 700px; margin: 20px auto; }}
  h1        {{ font-size: 18px; color: #1a1a2e; margin-bottom: 4px; }}
  .meta     {{ color: #666; font-size: 13px; margin-bottom: 24px; }}
  h2        {{ font-size: 14px; font-weight: 600; color: #333;
               background: #f4f4f8; padding: 6px 10px;
               border-left: 3px solid #5c6bc0; margin: 20px 0 6px; }}
  table     {{ width: 100%; border-collapse: collapse; margin-bottom: 4px; }}
  td        {{ padding: 4px 6px; vertical-align: top; }}
  .time     {{ color: #888; width: 42px; white-space: nowrap; }}
  .from     {{ color: #555; width: 160px; }}
  .subject  {{ color: #222; }}
  .footer   {{ margin-top: 32px; font-size: 12px; color: #aaa;
               border-top: 1px solid #eee; padding-top: 10px; }}
</style>
</head>
<body>
<h1>Automatisch gesorteerde mail — {datum_nl}</h1>
<p class="meta">{totaal} bericht{'en' if totaal != 1 else ''} in {len(berichten)} map{'pen' if len(berichten) != 1 else ''}</p>
"""

    for folder in sorted(berichten.keys()):
        items = berichten[folder]
        label = html_lib.escape(leesbare_map(folder))
        html += f'<h2>{label} <span style="font-weight:normal;color:#888">({len(items)})</span></h2>\n'
        html += '<table>\n'
        for item in items:
            time = html_lib.escape(item["time"])
            sender = html_lib.escape(item["from"])
            subject = html_lib.escape(item["subject"])
            url = apple_mail_url(item.get("message_id", ""))
            if url:
                subject_html = f'<a href="{html_lib.escape(url, quote=True)}">{subject}</a>'
            else:
                subject_html = subject
            html += (
                f'<tr>'
                f'<td class="time">{time}</td>'
                f'<td class="from">{sender}</td>'
                f'<td class="subject">{subject_html}</td>'
                f'</tr>\n'
            )
        html += '</table>\n'

    html += f'<p class="footer">Gegenereerd door dagelijks_overzicht.py · {datetime.now().strftime("%Y-%m-%d %H:%M")}</p>'
    html += '</body></html>'
    return html


# ── e-mail versturen ──────────────────────────────────────────────────────────

def stuur_overzicht(html: str, zoekdatum: date, config: dict):
    # datum_nl = format_date(zoekdatum, "EEEE d MMMM yyyy", locale="nl")  # zoekdatum.strftime("%-d %B %Y")
    # random_locale, random_lang, random_elang = get_random_locale()
    # datum_bl = format_date(zoekdatum, locale=random_locale)
    msg = MIMEMultipart("alternative")
    # msg["Subject"] = f"Mailoverzicht van {datum_bl} ({random_lang} - {random_elang}) of {datum_nl}"
    msg["Subject"] = create_mail_subject(zoekdatum, False)
    msg["From"]    = config["from"]
    msg["To"]      = config["to"]
    msg.attach(MIMEText(html, "html", "utf-8"))

    context = ssl.create_default_context()
    with smtplib.SMTP(config["smtp_host"], config["smtp_port"]) as server:
        server.starttls(context=context)
        server.login(config["username"], config["password"])
        server.sendmail(config["from"], [config["to"]], msg.as_string())

    print(f"Overzicht verstuurd naar {config['to']}")


# ── main ──────────────────────────────────────────────────────────────────────

def run():
    args = sys.argv[1:]
    cfg_dict = load_config()
    if mail_to := arg_value(args, "--mail-to"):
        cfg_dict["to"] = mail_to
    print(f"Account: {cfg_dict['username']}\n")

    # Datum bepalen
    if datum := arg_value(args, "--datum"):
        zoekdatum = date.fromisoformat(datum)
    else:
        zoekdatum = date.today() - timedelta(days=1)  # standaard: gisteren

    print(f"Ophalen berichten van {zoekdatum} ...")

    try:
        mail = imaplib.IMAP4_SSL(cfg_dict["imap_host"], cfg_dict["imap_port"])
        mail.login(cfg_dict["username"], cfg_dict["password"])
    except Exception as e:
        print(f"IMAP verbinding mislukt: {e}")
        sys.exit(1)

    berichten = haal_berichten_op(mail, zoekdatum)
    mail.logout()

    totaal = sum(len(v) for v in berichten.values())
    print(f"{totaal} berichten gevonden in {len(berichten)} mappen.")

    html = bouw_html(berichten, zoekdatum)
    stuur_overzicht(html, zoekdatum, cfg_dict)


if __name__ == "__main__":
    run()
