#!/usr/bin/env python3
from __future__ import annotations

import argparse
import html
import json
import os
import shutil
import subprocess
import sys
import tempfile
from pathlib import Path

DEFAULT_TITLE = "Audit export"
DEFAULT_FORMAT = "A4"

PREMIUM_PRINT_CSS = r'''
:root {
  color-scheme: light;
  --pdf-bg: #f5f7fb;
  --pdf-surface: #ffffff;
  --pdf-surface-soft: #f8fafc;
  --pdf-ink: #0f172a;
  --pdf-muted: #475569;
  --pdf-border: rgba(15, 23, 42, 0.10);
  --pdf-shadow: 0 12px 28px rgba(15, 23, 42, 0.08);
  --pdf-accent: #2563eb;
  --pdf-success: #16a34a;
  --pdf-warning: #d97706;
  --pdf-danger: #dc2626;
}

@page {
  size: A4 landscape;
  margin: 12mm;
}

html, body {
  margin: 0 !important;
  padding: 0 !important;
  background: var(--pdf-bg) !important;
  color: var(--pdf-ink) !important;
  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Arial, sans-serif !important;
  font-size: 12px !important;
  line-height: 1.45 !important;
  -webkit-print-color-adjust: exact !important;
  print-color-adjust: exact !important;
}

* { box-sizing: border-box !important; }

a { color: inherit !important; text-decoration: none !important; }

body { min-height: 100vh; }

.simple-chat-export-root {
  width: 100% !important;
}

.simple-chat-message__artifacts,
.simple-chat-export-root > * {
  width: 100% !important;
  max-width: none !important;
}

.simple-chat-export-root button,
.simple-chat-export-root .js-artifact-export-button,
.simple-chat-export-root .simple-chat-artifact__actions,
.simple-chat-export-root .simple-chat-message__artifacts-toolbar,
.simple-chat-export-root .audit-feed-block__actions,
.simple-chat-export-root [data-export-ignore="1"] {
  display: none !important;
}

.simple-chat-export-root [data-export-text-value="1"] {
  white-space: pre-wrap !important;
  min-height: 36px !important;
}

.simple-chat-export-root input,
.simple-chat-export-root textarea,
.simple-chat-export-root select,
.simple-chat-export-root .audit-export-field-value,
.simple-chat-export-root [data-export-text-value="1"] {
  display: block !important;
  width: 100% !important;
  border: 1px solid var(--pdf-border) !important;
  border-radius: 12px !important;
  background: #fff !important;
  color: var(--pdf-ink) !important;
  padding: 10px 12px !important;
  box-shadow: none !important;
}

.simple-chat-export-root .simple-chat-message__section,
.simple-chat-export-root .simple-audit-dashboard__section,
.simple-chat-export-root .simple-chat-chart-card,
.simple-chat-export-root .audit-feed-block,
.simple-chat-export-root .simple-chat-linked-post,
.simple-chat-export-root .simple-chat-linked-comment,
.simple-chat-export-root .simple-chat-artifact,
.simple-chat-export-root .simple-chat-artifact__content,
.simple-chat-export-root .simple-audit-card,
.simple-chat-export-root .simple-audit-pill {
  break-inside: avoid !important;
  page-break-inside: avoid !important;
}

.simple-chat-export-root .simple-chat-message__section,
.simple-chat-export-root .simple-audit-dashboard__section,
.simple-chat-export-root .audit-feed-block,
.simple-chat-export-root .simple-chat-linked-post,
.simple-chat-export-root .simple-chat-linked-comment,
.simple-chat-export-root .simple-chat-artifact,
.simple-chat-export-root .simple-chat-artifact__content {
  margin: 0 0 14px !important;
  padding: 14px 16px !important;
  border: 1px solid var(--pdf-border) !important;
  border-radius: 18px !important;
  background: var(--pdf-surface) !important;
  box-shadow: var(--pdf-shadow) !important;
}

.simple-chat-export-root .simple-audit-dashboard__head {
  display: flex !important;
  justify-content: space-between !important;
  align-items: flex-start !important;
  gap: 16px !important;
  margin: 0 0 14px !important;
  padding: 0 0 12px !important;
  border-bottom: 1px solid var(--pdf-border) !important;
}

.simple-chat-export-root h1,
.simple-chat-export-root h2,
.simple-chat-export-root h3,
.simple-chat-export-root h4,
.simple-chat-export-root h5,
.simple-chat-export-root h6 {
  margin: 0 0 8px !important;
  color: var(--pdf-ink) !important;
  line-height: 1.2 !important;
}

.simple-chat-export-root p,
.simple-chat-export-root li,
.simple-chat-export-root span,
.simple-chat-export-root div,
.simple-chat-export-root td,
.simple-chat-export-root th,
.simple-chat-export-root small,
.simple-chat-export-root label {
  color: inherit !important;
}

.simple-chat-export-root .simple-audit-overview-grid,
.simple-chat-export-root .simple-audit-comparison-grid,
.simple-chat-export-root .simple-audit-comments-grid,
.simple-chat-export-root .simple-audit-dashboard__split,
.simple-chat-export-root .simple-audit-embedded-charts,
.simple-chat-export-root .simple-chat-chart__grid,
.simple-chat-export-root .simple-chat-audit-blocks,
.simple-chat-export-root .simple-audit-dashboard__cards,
.simple-chat-export-root .simple-audit-dashboard__groups,
.simple-chat-export-root .simple-audit-pills {
  display: flex !important;
  flex-wrap: wrap !important;
  gap: 12px !important;
  align-items: stretch !important;
}

.simple-chat-export-root .simple-audit-overview-grid__cards,
.simple-chat-export-root .simple-audit-overview-grid__charts,
.simple-chat-export-root .simple-audit-comparison-grid__table,
.simple-chat-export-root .simple-audit-comparison-grid__charts,
.simple-chat-export-root .simple-audit-comments-grid__charts,
.simple-chat-export-root .simple-audit-comments-grid__groups,
.simple-chat-export-root .simple-audit-dashboard__split-col {
  flex: 1 1 320px !important;
  min-width: 280px !important;
}

.simple-chat-export-root .simple-audit-card,
.simple-chat-export-root .simple-audit-pill,
.simple-chat-export-root .simple-chat-chart-card,
.simple-chat-export-root [class*="metric"],
.simple-chat-export-root [class*="kpi"],
.simple-chat-export-root [class*="stat"] {
  flex: 1 1 220px !important;
  min-width: 180px !important;
  padding: 14px !important;
  border-radius: 16px !important;
  border: 1px solid var(--pdf-border) !important;
  background: var(--pdf-surface-soft) !important;
  box-shadow: none !important;
}

.simple-chat-export-root .simple-audit-card__label,
.simple-chat-export-root .simple-audit-pill__label,
.simple-chat-export-root .simple-chat-chart-card__head-sub,
.simple-chat-export-root .simple-chat-chart-card__range,
.simple-chat-export-root .audit-feed-block__description,
.simple-chat-export-root .audit-feed-block__connector-summary,
.simple-chat-export-root .simple-chat-chart__note,
.simple-chat-export-root .simple-audit-dashboard__meta {
  color: var(--pdf-muted) !important;
  font-size: 10px !important;
}

.simple-chat-export-root .simple-audit-card__value,
.simple-chat-export-root .simple-audit-pill__value {
  display: block !important;
  margin-top: 6px !important;
  font-size: 24px !important;
  font-weight: 700 !important;
  line-height: 1.05 !important;
  color: var(--pdf-ink) !important;
}

.simple-chat-export-root .simple-audit-pill--positive { background: #ecfdf5 !important; border-color: rgba(22,163,74,.20) !important; }
.simple-chat-export-root .simple-audit-pill--negative { background: #fef2f2 !important; border-color: rgba(220,38,38,.18) !important; }
.simple-chat-export-root .simple-audit-pill--warning { background: #fffbeb !important; border-color: rgba(217,119,6,.20) !important; }
.simple-chat-export-root .simple-audit-pill--neutral { background: #eff6ff !important; border-color: rgba(37,99,235,.18) !important; }

.simple-chat-export-root table {
  width: 100% !important;
  border-collapse: collapse !important;
  background: #fff !important;
  border-radius: 12px !important;
  overflow: hidden !important;
}

.simple-chat-export-root thead { display: table-header-group !important; }

.simple-chat-export-root th,
.simple-chat-export-root td {
  padding: 10px 12px !important;
  border: 1px solid rgba(15, 23, 42, 0.08) !important;
  text-align: left !important;
  vertical-align: top !important;
}

.simple-chat-export-root th {
  background: #eff6ff !important;
  font-weight: 700 !important;
}

.simple-chat-export-root tr,
.simple-chat-export-root img,
.simple-chat-export-root svg,
.simple-chat-export-root canvas,
.simple-chat-export-root .chart,
.simple-chat-export-root .graph {
  break-inside: avoid !important;
  page-break-inside: avoid !important;
}

.simple-chat-export-root img,
.simple-chat-export-root svg,
.simple-chat-export-root canvas {
  max-width: 100% !important;
  height: auto !important;
}
'''

HEADER_TEMPLATE = """
<div style="width:100%;font-size:9px;color:#64748b;padding:0 12mm;box-sizing:border-box;font-family:Inter, Arial, sans-serif;">
  <div style="display:flex;justify-content:space-between;width:100%;">
    <span class="title"></span>
    <span class="date"></span>
  </div>
</div>
""".strip()

FOOTER_TEMPLATE = """
<div style="width:100%;font-size:9px;color:#64748b;padding:0 12mm;box-sizing:border-box;font-family:Inter, Arial, sans-serif;">
  <div style="display:flex;justify-content:space-between;width:100%;">
    <span>Ubisoft Audit Export</span>
    <span><span class="pageNumber"></span> / <span class="totalPages"></span></span>
  </div>
</div>
""".strip()


class PdfExportError(RuntimeError):
    pass


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Render simple-chat-message__artifacts HTML to PDF via PrinceXML or Playwright/Chromium."
    )
    parser.add_argument("output_positional", nargs="?", help="Output PDF path (legacy positional form)")
    parser.add_argument("--input", help="Input HTML fragment file path. Reads stdin if omitted.")
    parser.add_argument("--output", help="Output PDF path.")
    parser.add_argument("--title", default=DEFAULT_TITLE, help="Document title")
    parser.add_argument("--format", default=DEFAULT_FORMAT, help="Paper format for Playwright fallback")
    parser.add_argument("--landscape", action="store_true", help="Landscape orientation")
    parser.add_argument("--base-url", default="", help="Base URL used to resolve relative assets")
    parser.add_argument("--prefer-prince", action="store_true", help="Try PrinceXML first")
    parser.add_argument("--engine", choices=["auto", "prince", "playwright"], default="auto")
    parser.add_argument("--timeout-ms", type=int, default=45000, help="Render timeout in milliseconds")
    args = parser.parse_args()

    if not args.output:
        args.output = args.output_positional
    if not args.output:
        parser.error("an output path is required via --output or positional output")
    return args


def read_input_payload(path: str | None) -> tuple[str, dict]:
    if path:
        return Path(path).read_text(encoding="utf-8"), {}

    raw = sys.stdin.read()
    if not raw.strip():
        raise PdfExportError("No input HTML provided.")

    try:
        payload = json.loads(raw)
    except json.JSONDecodeError:
        return raw, {}

    if isinstance(payload, dict):
        html_value = str(payload.get("html") or "")
        if html_value.strip():
            return html_value, payload
    return raw, {}


def normalize_fragment(fragment_html: str) -> str:
    body = fragment_html.strip()
    if not body:
        raise PdfExportError("Empty artifacts HTML.")
    return body


def build_document(fragment_html: str, title: str, base_url: str, landscape: bool, paper_format: str) -> str:
    safe_title = html.escape(title or DEFAULT_TITLE, quote=True)
    safe_base = html.escape(base_url, quote=True) if base_url else ""
    page_css = f"@page {{ size: {paper_format}{' landscape' if landscape else ''}; margin: 12mm; }}"
    return f"""<!doctype html>
<html lang=\"fr\">
<head>
  <meta charset=\"utf-8\">
  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">
  <title>{safe_title}</title>
  {f'<base href="{safe_base}">' if safe_base else ''}
  <style>{PREMIUM_PRINT_CSS}\n{page_css}</style>
</head>
<body>
  <main class=\"simple-chat-export-root\">{fragment_html}</main>
</body>
</html>
"""


def ensure_pdf(path: Path) -> None:
    if not path.exists():
        raise PdfExportError("No PDF file was generated.")
    if path.stat().st_size < 800:
        raise PdfExportError("Generated PDF is too small.")
    with path.open("rb") as handle:
        if not handle.read(5).startswith(b"%PDF-"):
            raise PdfExportError("Generated file is not a valid PDF.")


def discover_playwright_browser() -> str | None:
    for name in ("chromium", "google-chrome", "google-chrome-stable", "chrome", "chromium-browser"):
        path = shutil.which(name)
        if path:
            return path
    return None


def render_with_prince(document_html: str, output_path: Path, title: str) -> None:
    prince_bin = shutil.which("prince")
    if not prince_bin:
        raise PdfExportError("PrinceXML not found in PATH.")

    with tempfile.TemporaryDirectory(prefix="audit_pdf_prince_") as tmp_dir:
        html_path = Path(tmp_dir) / "document.html"
        css_path = Path(tmp_dir) / "print.css"
        html_path.write_text(document_html, encoding="utf-8")
        css_path.write_text(PREMIUM_PRINT_CSS, encoding="utf-8")

        cmd = [
            prince_bin,
            str(html_path),
            "--output", str(output_path),
            "--style", str(css_path),
            "--pdf-title", title or DEFAULT_TITLE,
            "--pdf-profile", "pdf/ua-1",
            "--tagged-pdf",
        ]
        proc = subprocess.run(cmd, capture_output=True, text=True)
        if proc.returncode != 0:
            stderr = (proc.stderr or proc.stdout or "").strip()
            raise PdfExportError(f"PrinceXML render failed: {stderr}")

    ensure_pdf(output_path)


def render_with_playwright(document_html: str, output_path: Path, title: str, base_url: str, paper_format: str, landscape: bool, timeout_ms: int) -> None:
    try:
        from playwright.sync_api import sync_playwright
    except Exception as exc:  # noqa: BLE001
        raise PdfExportError(f"Playwright unavailable: {exc}") from exc

    executable_path = discover_playwright_browser()
    launch_options = {"headless": True}
    if executable_path:
        launch_options["executable_path"] = executable_path

    with sync_playwright() as p:
        browser = p.chromium.launch(**launch_options)
        try:
            page = browser.new_page(viewport={"width": 1600, "height": 1100}, device_scale_factor=1.25)
            page.emulate_media(media="screen")
            page.set_content(document_html, wait_until="load")
            page.wait_for_timeout(350)
            page.evaluate(
                """
                async () => {
                  if (document.fonts && document.fonts.ready) {
                    try { await document.fonts.ready; } catch (e) {}
                  }
                  const waitForImages = Array.from(document.images || []).map((img) => {
                    if (img.complete) return Promise.resolve();
                    return new Promise((resolve) => {
                      img.addEventListener('load', resolve, { once: true });
                      img.addEventListener('error', resolve, { once: true });
                    });
                  });
                  await Promise.all(waitForImages);
                }
                """
            )
            page.pdf(
                path=str(output_path),
                format=paper_format,
                landscape=landscape,
                print_background=True,
                prefer_css_page_size=True,
                display_header_footer=True,
                header_template=HEADER_TEMPLATE,
                footer_template=FOOTER_TEMPLATE,
                margin={"top": "18mm", "right": "10mm", "bottom": "16mm", "left": "10mm"},
                outline=True,
                tagged=True,
                timeout=timeout_ms,
            )
        finally:
            browser.close()

    ensure_pdf(output_path)


def main() -> int:
    args = parse_args()
    output_path = Path(args.output).expanduser().resolve()
    output_path.parent.mkdir(parents=True, exist_ok=True)

    try:
        input_html, payload = read_input_payload(args.input)
        title = str(payload.get("title") or args.title or DEFAULT_TITLE)
        base_url = str(payload.get("base_url") or args.base_url or "")
        paper_format = str(payload.get("format") or args.format or DEFAULT_FORMAT)
        landscape = bool(payload.get("landscape")) or bool(args.landscape)
        fragment_html = normalize_fragment(input_html)
        document_html = build_document(fragment_html, title=title, base_url=base_url, landscape=landscape, paper_format=paper_format)

        errors: list[str] = []
        engine = args.engine
        order = []
        if engine == "prince":
            order = ["prince"]
        elif engine == "playwright":
            order = ["playwright"]
        elif args.prefer_prince:
            order = ["prince", "playwright"]
        else:
            order = ["playwright", "prince"]

        for selected in order:
            try:
                if selected == "playwright":
                    render_with_playwright(
                        document_html=document_html,
                        output_path=output_path,
                        title=title,
                        base_url=base_url,
                        paper_format=paper_format,
                        landscape=landscape,
                        timeout_ms=args.timeout_ms,
                    )
                else:
                    render_with_prince(document_html=document_html, output_path=output_path, title=title)
                return 0
            except Exception as exc:  # noqa: BLE001
                errors.append(f"{selected}: {exc}")

        raise PdfExportError(" | ".join(errors) if errors else "No PDF engine succeeded.")
    except Exception as exc:  # noqa: BLE001
        sys.stderr.write(f"PDF generation failed: {exc}")
        return 1


if __name__ == "__main__":
    raise SystemExit(main())
