"""Shared optional PDF export for ASUCB HTML reports.

Playwright is an OPTIONAL dependency (it pulls a ~100MB Chromium). It is
imported lazily so the plugin's minimal base install (requests, dotenv,
PyYAML) is unaffected. Install with:

    pip install playwright && python -m playwright install chromium

Then any report path can offer a `--pdf` flag that calls `export_pdf`.
"""
from __future__ import annotations

import subprocess
import sys
from pathlib import Path


class PDFExportUnavailable(RuntimeError):
    """Raised when Playwright (or its browser) is not installed."""


_INSTALL_HINT = (
    "PDF export needs Playwright (optional). Install it with:\n"
    "  pip install playwright\n"
    "  python -m playwright install chromium"
)


def pdf_available() -> bool:
    """True if Playwright is importable. Does not verify the browser binary."""
    try:
        import playwright  # noqa: F401
    except ImportError:
        return False
    return True


def attempt_install(
    *,
    interactive: bool = True,
    _input=input,
    _isatty: bool | None = None,
) -> bool:
    """Offer to install Playwright + Chromium with explicit user consent.

    Returns True only if the user consents AND both installs succeed.
    Never installs silently: requires an interactive TTY and a yes answer.
    Call this only after `export_pdf` raised PDFExportUnavailable.
    """
    if _isatty is None:
        _isatty = sys.stdin.isatty()
    if not (interactive and _isatty):
        return False

    answer = _input(
        "PDF export needs Playwright + Chromium (~100MB). Install now? [y/N] "
    ).strip().lower()
    if answer not in ("y", "yes"):
        return False

    try:
        subprocess.run(
            [sys.executable, "-m", "pip", "install", "playwright"], check=True
        )
        subprocess.run(
            [sys.executable, "-m", "playwright", "install", "chromium"], check=True
        )
    except (subprocess.CalledProcessError, FileNotFoundError) as exc:
        print(f"Install failed: {exc}")
        return False
    return True


def export_pdf(
    html: str | None = None,
    *,
    html_path: str | Path | None = None,
    output_path: str | Path,
    page_format: str = "Letter",
    print_background: bool = True,
) -> Path:
    """Render HTML to a PDF file and return its path.

    Provide exactly one source: an `html` string or an `html_path` file.
    Raises PDFExportUnavailable if Playwright or its Chromium is missing.
    """
    if (html is None) == (html_path is None):
        raise ValueError("Provide exactly one of `html` or `html_path`.")

    try:
        from playwright.sync_api import sync_playwright
    except ImportError as exc:
        raise PDFExportUnavailable(_INSTALL_HINT) from exc

    output_path = Path(output_path)
    output_path.parent.mkdir(parents=True, exist_ok=True)

    if html_path is not None:
        url = Path(html_path).resolve().as_uri()
    else:
        url = None

    try:
        with sync_playwright() as p:
            browser = p.chromium.launch()
            try:
                page = browser.new_page()
                if url is not None:
                    page.goto(url)
                else:
                    page.set_content(html, wait_until="load")
                page.pdf(
                    path=str(output_path),
                    format=page_format,
                    print_background=print_background,
                    margin={"top": "0", "bottom": "0", "left": "0", "right": "0"},
                )
            finally:
                browser.close()
    except PDFExportUnavailable:
        raise
    except Exception as exc:  # browser missing / launch failure
        msg = str(exc)
        if "Executable doesn't exist" in msg or "playwright install" in msg:
            raise PDFExportUnavailable(_INSTALL_HINT) from exc
        raise

    return output_path
