"""Provider-neutral, email-client-safe transactional email presentation.

Capabilities supply semantic content; this module owns the shared HTML/text
shell. Delivery selection and provider credentials intentionally live outside
this module.
"""
from __future__ import annotations

import re
from dataclasses import dataclass, field
from html import escape
from urllib.parse import urlparse

NEUTRAL_ACCENT = "#1f2937"
MANAGED_ATTRIBUTION = "Sent securely via P2HS"

_SUPPORT_EMAIL_RE = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")

# Light-theme colors are inline (work everywhere, including clients that strip
# <style> blocks). This block only overrides them for clients that honor
# prefers-color-scheme, so Outlook/older Gmail silently keep the light theme.
_DARK_MODE_STYLE = """<style>
@media (prefers-color-scheme: dark) {
  .p2-body { background: #0b1220 !important; }
  .p2-card { background: #111827 !important; border-color: #1f2937 !important; }
  .p2-border { border-color: #1f2937 !important; }
  .p2-heading { color: #f3f4f6 !important; }
  .p2-body-text { color: #d1d5db !important; }
  .p2-muted { color: #9ca3af !important; }
  .p2-security { background: #1f2937 !important; }
  .p2-link { color: #60a5fa !important; }
}
</style>"""


def _clean(value: str | None, fallback: str = "") -> str:
    return " ".join((value or "").strip().split()) or fallback


def _safe_color(value: str | None) -> str:
    """Accept only a hex color with readable white CTA text."""
    raw = (value or "").strip()
    if len(raw) not in (4, 7) or not raw.startswith("#"):
        return NEUTRAL_ACCENT
    digits = raw[1:]
    if len(digits) == 3:
        digits = "".join(ch * 2 for ch in digits)
    try:
        r, g, b = (int(digits[i : i + 2], 16) for i in (0, 2, 4))
    except ValueError:
        return NEUTRAL_ACCENT
    # WCAG-ish luminance gate for white button text.
    luminance = (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255
    return f"#{digits.lower()}" if luminance < 0.48 else NEUTRAL_ACCENT


def _safe_url(value: str) -> str:
    parsed = urlparse(value.strip())
    if value.strip().startswith("/") and parsed.scheme == "":
        return value.strip()
    if parsed.scheme not in {"http", "https", "p2hs"} or not parsed.netloc and parsed.scheme != "p2hs":
        raise ValueError("transactional email action URL must use http(s) or p2hs")
    return value.strip()


@dataclass(frozen=True)
class ApplicationBranding:
    name: str
    logo_url: str | None = None
    accent_color: str | None = None
    support_identity: str | None = None
    environment_label: str | None = None

    @property
    def display_name(self) -> str:
        return _clean(self.name, "Your application")

    @property
    def accent(self) -> str:
        return _safe_color(self.accent_color)

    @property
    def safe_logo_url(self) -> str | None:
        if not self.logo_url:
            return None
        try:
            parsed = urlparse(self.logo_url.strip())
            if parsed.scheme in {"http", "https"} and parsed.netloc:
                return self.logo_url.strip()
        except ValueError:
            pass
        return None


@dataclass(frozen=True)
class EmailPresentation:
    template_key: str
    template_version: str
    title: str
    intro: str
    action_label: str | None = None
    action_url: str | None = None
    note: str | None = None
    fallback_url: str | None = None
    preheader: str | None = None
    details: tuple[tuple[str, str], ...] = ()
    security_notice: str | None = None


@dataclass(frozen=True)
class EmailMessage:
    recipient: str
    branding: ApplicationBranding
    presentation: EmailPresentation
    subject: str
    footer: str = MANAGED_ATTRIBUTION
    metadata: dict[str, str] = field(default_factory=dict)


def render_transactional_email(message: EmailMessage) -> tuple[str, str]:
    """Return deterministic ``(html, plain_text)`` for a semantic message."""
    p = message.presentation
    b = message.branding
    action_url = _safe_url(p.action_url) if p.action_url else None
    fallback_url = _safe_url(p.fallback_url or p.action_url) if (p.fallback_url or p.action_url) else None
    name = escape(b.display_name)
    title = escape(_clean(p.title))
    intro = escape(_clean(p.intro))
    accent = b.accent
    logo = ""
    if b.safe_logo_url:
        logo = (
            f'<img src="{escape(b.safe_logo_url, quote=True)}" alt="{name}" '
            'width="40" style="display:block;max-width:40px;height:auto;border:0;">'
        )
    env_marker = f' <span class="p2-muted" style="font-size:12px;color:#6b7280;">({escape(_clean(b.environment_label))})</span>' if b.environment_label else ""
    cta = ""
    if action_url and p.action_label:
        cta = (
            f'<a href="{escape(action_url, quote=True)}" style="display:inline-block;padding:13px 22px;'
            f'background:{accent};color:#ffffff;text-decoration:none;font-size:15px;font-weight:700;'
            f'border-radius:5px;">{escape(p.action_label)}</a>'
        )
    details = "".join(
        f'<tr><td class="p2-muted" style="padding:4px 16px 4px 0;color:#6b7280;font-size:14px;">{escape(k)}</td>'
        f'<td class="p2-heading" style="padding:4px 0;color:#111827;font-size:14px;">{escape(v)}</td></tr>'
        for k, v in p.details
    )
    details_block = f'<table role="presentation" cellpadding="0" cellspacing="0" style="margin:20px 0;">{details}</table>' if details else ""
    note = f'<p class="p2-body-text" style="margin:20px 0 0;color:#4b5563;font-size:14px;line-height:1.6;">{escape(p.note)}</p>' if p.note else ""
    security = f'<p class="p2-security p2-body-text" style="margin:20px 0 0;padding:12px 14px;background:#f3f4f6;color:#374151;font-size:13px;line-height:1.6;">{escape(p.security_notice)}</p>' if p.security_notice else ""
    fallback = ""
    if fallback_url:
        fallback = (
            '<p class="p2-muted" style="margin:24px 0 0;color:#6b7280;font-size:13px;line-height:1.6;">'
            'Having trouble with the button? Copy and paste this link into your browser:<br>'
            f'<a href="{escape(fallback_url, quote=True)}" class="p2-link" style="color:#1d4ed8;word-break:break-all;">'
            f'{escape(fallback_url)}</a></p>'
        )
    support = ""
    support_identity = _clean(b.support_identity)
    if support_identity:
        support_contact = (
            f'<a href="mailto:{escape(support_identity, quote=True)}" class="p2-link" style="color:#1d4ed8;">{escape(support_identity)}</a>'
            if _SUPPORT_EMAIL_RE.match(support_identity)
            else escape(support_identity)
        )
        support = f'<p class="p2-muted" style="margin:10px 0 0;color:#6b7280;">Need help? Contact {support_contact}</p>'
    html = f'''<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="color-scheme" content="light dark"><meta name="supported-color-schemes" content="light dark"><title>{title}</title>
{_DARK_MODE_STYLE}
</head>
<body class="p2-body" style="margin:0;background:#f8fafc;color:#111827;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Arial,sans-serif;">
<div style="display:none;max-height:0;overflow:hidden;opacity:0;">{escape(_clean(p.preheader or p.intro))}</div>
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" class="p2-body" style="background:#f8fafc;"><tr><td align="center" style="padding:32px 12px;">
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" class="p2-card" style="max-width:600px;background:#ffffff;border:1px solid #e5e7eb;">
<tr><td class="p2-border" style="padding:28px 32px 20px;border-bottom:1px solid #e5e7eb;">{logo}<div class="p2-heading" style="margin-top:10px;font-size:18px;font-weight:700;color:#111827;">{name}{env_marker}</div></td></tr>
<tr><td style="padding:32px;"><h1 class="p2-heading" style="margin:0 0 12px;font-size:25px;line-height:1.25;color:#111827;">{title}</h1><p class="p2-body-text" style="margin:0;color:#374151;font-size:16px;line-height:1.6;">{intro}</p>{details_block}<div style="margin-top:26px;">{cta}</div>{note}{security}{fallback}</td></tr>
<tr><td class="p2-border p2-muted" style="padding:20px 32px 28px;border-top:1px solid #e5e7eb;color:#6b7280;font-size:12px;line-height:1.6;">{escape(message.footer)}{support}</td></tr>
</table></td></tr></table></body></html>'''
    lines = [b.display_name, "", _clean(p.title), "", _clean(p.intro)]
    lines.extend(f"{k}: {v}" for k, v in p.details)
    if p.action_label and action_url:
        lines.extend(["", f"{p.action_label}: {action_url}"])
    if p.note:
        lines.extend(["", _clean(p.note)])
    if p.security_notice:
        lines.extend(["", _clean(p.security_notice)])
    if fallback_url:
        lines.extend(["", "Having trouble with the button?", fallback_url])
    lines.extend(["", message.footer])
    if support_identity:
        lines.extend(["", f"Need help? Contact {support_identity}"])
    return html, "\n".join(lines)


def magic_link_message(*, recipient: str, app_name: str, link_url: str, ttl_minutes: int, logo_url: str | None = None, accent_color: str | None = None, environment_label: str | None = None) -> EmailMessage:
    duration = f"{ttl_minutes} minutes" if ttl_minutes < 60 else f"{ttl_minutes // 60} hour{'s' if ttl_minutes // 60 != 1 else ''}"
    branding = ApplicationBranding(app_name, logo_url, accent_color, environment_label=environment_label)
    presentation = EmailPresentation(
        template_key="auth.magic_link", template_version="auth.magic_link.v1",
        title=f"Sign in to {branding.display_name}",
        intro="Use the secure link below to sign in to your account.",
        action_label="Sign in", action_url=link_url, fallback_url=link_url,
        preheader=f"Sign in to {branding.display_name}",
        security_notice=f"This link expires in {duration}. If you didn't request this email, you can safely ignore it.",
    )
    return EmailMessage(recipient, branding, presentation, f"Sign in to {branding.display_name}")
