"""Minimal outbound SMTP sender for the CRM (daily digest, test sends).

Config comes ONLY from SMTP_* environment variables. Two ways those get set:
  * dev / bare run: a local .env (see .env.example).
  * Start9 box: docker_entrypoint.sh reads the files the "Configure Digest SMTP"
    StartOS action writes under /data/secrets/smtp/ and exports them as env.
The backend never reads those files directly, so dev and prod share one path.

This is the package's OWN dedicated mailbox (per-package custom SMTP) — it is
independent of any StartOS system-wide SMTP account; nothing here calls into the
platform. Stdlib only (smtplib/ssl/email), consistent with the rest of runtime.
"""
import os
import smtplib
import ssl
from email.message import EmailMessage


class SMTPNotConfigured(Exception):
    """Raised when SMTP_* env is absent — callers turn this into a clear 'not
    configured' response rather than a 500."""


def smtp_configured():
    return bool(os.environ.get("SMTP_HOST", "").strip())


def load_smtp_config():
    host = os.environ.get("SMTP_HOST", "").strip()
    if not host:
        raise SMTPNotConfigured("SMTP_HOST is not set")
    # Port/security come from a free-text action field; normalize defensively.
    try:
        port = int(str(os.environ.get("SMTP_PORT", "") or "587").strip())
    except ValueError:
        port = 587
    security = (os.environ.get("SMTP_SECURITY", "") or "starttls").strip().lower()
    if security not in ("starttls", "tls", "none"):
        security = "starttls"
    return {
        "host": host,
        "port": port,
        "from_addr": os.environ.get("SMTP_FROM", "").strip(),
        "username": os.environ.get("SMTP_USERNAME", "").strip(),
        "password": os.environ.get("SMTP_PASSWORD", ""),
        "security": security,
    }


def _connect(cfg, timeout):
    """Open an authenticated SMTP connection per the configured security mode.
    'tls' = implicit TLS (SMTPS, usually 465); 'starttls' = upgrade on 587;
    'none' = plaintext (for a LAN relay that does its own transport security)."""
    if cfg["security"] == "tls":
        ctx = ssl.create_default_context()
        server = smtplib.SMTP_SSL(cfg["host"], cfg["port"], timeout=timeout, context=ctx)
    else:
        server = smtplib.SMTP(cfg["host"], cfg["port"], timeout=timeout)
        server.ehlo()
        if cfg["security"] == "starttls":
            server.starttls(context=ssl.create_default_context())
            server.ehlo()
    if cfg["username"]:
        server.login(cfg["username"], cfg["password"])
    return server


def send_email(to_addrs, subject, body, *, html=None, cfg=None, timeout=30):
    """Send one message. `to_addrs` is a str or list; `body` is plain text and
    `html` an optional HTML alternative. Returns {'sent_to', 'from'} on success;
    raises SMTPNotConfigured / ValueError / smtplib.SMTPException otherwise."""
    cfg = cfg or load_smtp_config()
    if isinstance(to_addrs, str):
        to_addrs = [to_addrs]
    to_addrs = [a for a in (str(x).strip() for x in to_addrs) if a]
    if not to_addrs:
        raise ValueError("no recipients")

    from_addr = cfg["from_addr"] or cfg["username"]
    if not from_addr:
        raise SMTPNotConfigured("no SMTP_FROM or SMTP_USERNAME to use as sender")

    msg = EmailMessage()
    msg["From"] = from_addr
    msg["To"] = ", ".join(to_addrs)
    msg["Subject"] = subject
    msg.set_content(body)
    if html:
        msg.add_alternative(html, subtype="html")

    server = _connect(cfg, timeout)
    try:
        server.send_message(msg, from_addr=from_addr, to_addrs=to_addrs)
    finally:
        try:
            server.quit()
        except Exception:
            pass
    return {"sent_to": to_addrs, "from": from_addr}
