"""Email (.eml) scanning for the email profile (R-8).

Parses a message, scans the header and body text for PII and banners, and scans each
attachment with the full engine so attachment metadata (tracked changes, comments,
author fields) and embedded PII are surfaced. This only decodes message parts; it makes
no network calls.
"""
from __future__ import annotations

import email
import os
import re
import tempfile
from typing import Optional

from .models import ExtractedDoc, Finding, TextUnit

_CTYPE_EXT = {
    "application/pdf": ".pdf",
    "application/vnd.openxmlformats-officedocument.wordprocessingml.document": ".docx",
    "image/jpeg": ".jpg",
    "image/jpg": ".jpg",
    "image/png": ".png",
    "image/tiff": ".tif",
    "text/plain": ".txt",
}


def _strip_html(html: str) -> str:
    return re.sub(r"<[^>]+>", " ", html)


def _decode(part) -> str:
    payload = part.get_payload(decode=True) or b""
    charset = part.get_content_charset() or "utf-8"
    return payload.decode(charset, "replace")


def scan_eml(path: str, profile: Optional[str] = None) -> tuple[list[Finding], list[str]]:
    from .detectors.metadata import scan_metadata
    from .engine import scan_doc

    findings: list[Finding] = []
    notes: list[str] = []
    with open(path, "rb") as fh:
        msg = email.message_from_binary_file(fh)

    header_text = "\n".join(
        f"{h}: {msg.get(h, '')}" for h in ("From", "To", "Cc", "Subject") if msg.get(h)
    )
    body_parts: list[str] = []

    for part in msg.walk():
        ctype = part.get_content_type()
        if ctype.startswith("multipart/"):
            continue
        disposition = part.get_content_disposition()
        filename = part.get_filename()
        is_attachment = disposition == "attachment" or (
            filename is not None and ctype not in ("text/plain", "text/html")
        )
        if is_attachment:
            data = part.get_payload(decode=True)
            if not data:
                continue
            ext = os.path.splitext(filename or "")[1].lower() or _CTYPE_EXT.get(ctype, ".bin")
            tmp = tempfile.NamedTemporaryFile(delete=False, suffix=ext)
            try:
                tmp.write(data)
                tmp.close()
                from .extract import extract
                doc = extract(tmp.name)
                doc.metadata_findings.extend(scan_metadata(tmp.name, doc.kind))
                label = filename or os.path.basename(tmp.name)
                for f in scan_doc(doc, profile=profile):
                    f.file = path
                    f.location = f"attachment[{label}]:{f.location}"
                    findings.append(f)
                for note in doc.notes:
                    notes.append(f"attachment[{label}]: {note}")
            finally:
                os.unlink(tmp.name)
        elif ctype == "text/plain":
            body_parts.append(_decode(part))
        elif ctype == "text/html":
            body_parts.append(_strip_html(_decode(part)))

    full = header_text + "\n" + "\n".join(body_parts)
    body_doc = ExtractedDoc(path=path, kind="eml",
                            units=[TextUnit(full, "body", 0)], full_text=full)
    for f in scan_doc(body_doc, profile=profile):
        f.location = f.location or "body"
        findings.append(f)
    return findings, notes
