"""MIME message construction + PDF generation for attachments and message bodies.

Real PDF engine: WeasyPrint. Real MIME: stdlib email.mime.
"""
import io
import mimetypes
import os
import re
import secrets
from email.mime.application import MIMEApplication
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import formatdate, make_msgid
from typing import List, Optional

from weasyprint import HTML as WeasyHTML


class HtmlValidationError(Exception):
    pass


def build_mime_message(
    from_email: str,
    to_email: str,
    subject: str,
    html_body: str = "",
    text_body: str = "",
    attachments: Optional[list] = None,   # list of {filename, content(bytes), mime_type}
    unsubscribe_url: str = "",
    unsubscribe_mailto: str = "",
) -> MIMEMultipart:
    """Build a real MIME multipart message. From header always equals the authenticated account."""
    attachments = attachments or []
    if html_body and text_body:
        msg = MIMEMultipart("alternative")
    else:
        msg = MIMEMultipart("mixed")
    msg["From"] = from_email  # MUST be the authenticated Gmail account (never spoofed)
    msg["To"] = to_email
    msg["Subject"] = subject
    msg["Date"] = formatdate(localtime=True)
    msg["Message-ID"] = make_msgid()
    msg["X-Mailer"] = "BulkGmailMailer (permission-based sending)"

    # List-Unsubscribe headers for compliance
    if unsubscribe_url:
        parts = [f"<{unsubscribe_url}>"]
        if unsubscribe_mailto:
            parts.append(f"<mailto:{unsubscribe_mailto}>")
        msg["List-Unsubscribe"] = ", ".join(parts)
        msg["List-Unsubscribe-Post"] = "List-Unsubscribe=One-Click"

    # Text part first, then HTML (preferred last in alternative)
    if text_body:
        msg.attach(MIMEText(text_body, "plain", "utf-8"))
    if html_body:
        msg.attach(MIMEText(html_body, "html", "utf-8"))

    # Attachments
    for att in attachments:
        data = att["content"]
        ctype = att.get("mime_type") or "application/octet-stream"
        maintype, subtype = ctype.split("/", 1)
        if maintype == "text":
            part = MIMEText(data.decode("utf-8", errors="replace"), subtype, "utf-8")
            part.set_param("name", att["filename"], header="Content-Disposition")
        elif maintype == "image":
            part = MIMEImage(data, _subtype=subtype)
        elif maintype == "application" and subtype == "pdf":
            part = MIMEApplication(data, _subtype=subtype)
        else:
            part = MIMEBase(maintype, subtype)
            part.set_payload(data)
        part.add_header("Content-Disposition", "attachment", filename=att["filename"])
        msg.attach(part)
    return msg


def validate_html(html: str) -> List[str]:
    """Basic structural validation of rendered HTML. Returns list of problems."""
    problems = []
    if not html or not html.strip():
        problems.append("Empty HTML body")
        return problems
    from bs4 import BeautifulSoup
    soup = BeautifulSoup(html, "lxml")
    if soup.find(True) is None:
        problems.append("No HTML elements found")
    for tag in soup.find_all(["script", "iframe", "object", "embed", "form"]):
        problems.append(f"Disallowed tag <{tag.name}> present")
    for img in soup.find_all("img"):
        if not (img.get("src") or "").strip():
            problems.append("img without src")
    leftovers = re.findall(r"%([A-Za-z0-9_]+)%", html)
    if leftovers:
        problems.append(f"Unsubstituted variables remain: {sorted(set(leftovers))[:10]}")
    return problems


def html_to_pdf(html: str) -> bytes:
    """Real HTML → PDF conversion via WeasyPrint."""
    try:
        return WeasyHTML(string=html).write_pdf()
    except Exception as e:
        raise HtmlValidationError(f"PDF conversion failed: {e}") from e


def sanitize_preview(html: str) -> str:
    """Sanitize HTML for safe rendering in the in-app editor/preview (server-side)."""
    import bleach
    allowed_tags = list(bleach.ALLOWED_TAGS) + ["table", "thead", "tbody", "tr", "td", "th",
                                          "img", "p", "span", "div", "h1", "h2", "h3",
                                          "strong", "em", "a", "ul", "ol", "li", "br", "hr", "font", "center"]
    allowed_attrs = {"a": ["href", "title"], "img": ["src", "alt", "width", "height"],
                     "*": ["style", "class", "align", "bgcolor", "width", "border", "cellpadding", "cellspacing"]}
    protocols = ["http", "https", "mailto"]
    return bleach.clean(html or "", tags=allowed_tags, attributes=allowed_attrs, protocols=protocols, strip=False)


def sanitize_html_for_email(html: str) -> str:
    """Remove scripts/forms from outgoing email HTML (keep email-safe markup)."""
    return sanitize_preview(html)


def generate_attachment_filename(kind: str, invoice_number: str) -> str:
    """Unique, deterministic filenames like Invoice_INV-XXXX.pdf per campaign rules."""
    inv = re.sub(r"[^A-Za-z0-9-]", "", invoice_number or "")
    if kind == "invoice":
        return f"Invoice_{inv or 'INV' + secrets.token_hex(3)}.pdf"
    if kind == "order":
        return f"Order_{inv or 'ORD' + secrets.token_hex(3)}.pdf"
    if kind == "receipt":
        return f"Receipt_{inv or 'RF' + secrets.token_hex(3)}.pdf"
    return f"Document_{inv or secrets.token_hex(4)}.pdf"
