"""render_bg.py: 用无头 Chrome/Edge 把主题化 HTML 背景渲成高清 PNG。

混合方案专用 —— 封面/章节页:先用本脚本渲一张杂志级背景图,build_deck 里
`P.add_picture_bg(slide, png)` 整页铺,再叠原生可编辑文字。背景不可改但文字能改,
是 editable 前提下能拿到的最高观感(DrawingML 渐变做不出 mesh 渐变 + 模糊光晕)。

用法:
    python render_bg.py --out cover.png --kind cover --primary C00000
    python render_bg.py --out sec.png  --kind section --primary C00000 --accent FFC107
    python render_bg.py --out x.png    --html mybg.html          # 渲任意 HTML

依赖:本机装了 Chrome 或 Edge(无需 pip 包)。两者都没有则报错退出。
产物默认 2560x1440(16:9 高清,2x 超采样),嵌进 13.33in 画布够清晰。
"""
from __future__ import annotations

import argparse
import subprocess
import sys
import tempfile
from pathlib import Path

_CHROME_CANDIDATES = [
    r"C:\Program Files\Google\Chrome\Application\chrome.exe",
    r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe",
    r"C:\Program Files\Microsoft\Edge\Application\msedge.exe",
    r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe",
]


def find_browser() -> str:
    for c in _CHROME_CANDIDATES:
        if Path(c).exists():
            return c
    # PATH 兜底
    import shutil
    for name in ("chrome", "chrome.exe", "msedge", "msedge.exe"):
        p = shutil.which(name)
        if p:
            return p
    raise SystemExit("[fatal] 未找到 Chrome / Edge,无法渲染背景图。改用 DrawingML 渐变背景(apply_brand)。")


def _hex(h: str) -> tuple[int, int, int]:
    h = h.lstrip("#")
    return int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16)


def _mix(c, d, t):
    return tuple(round(a + (b - a) * t) for a, b in zip(c, d))


def _css(c) -> str:
    return f"rgb({c[0]},{c[1]},{c[2]})"


def build_html(kind: str, primary: str, accent: str) -> str:
    p = _hex(primary)
    a = _hex(accent)
    dark = _mix(p, (0, 0, 0), 0.55)     # 深端
    deep = _mix(p, (0, 0, 0), 0.30)
    glow = _mix(p, (255, 255, 255), 0.25)  # 亮红光晕
    pc, dc, dpc, gc, ac = _css(p), _css(dark), _css(deep), _css(glow), _css(a)
    # 公共:mesh 渐变(多点径向叠加)+ 模糊光斑 + 细点纹理。文字由 build_deck 叠。
    # cover:左侧加暗罩,让左置白字更稳;section:整页深,中心略亮。
    overlay = (
        "radial-gradient(1200px 900px at 18% 50%, rgba(0,0,0,.34), transparent 60%),"
        if kind == "cover" else
        "radial-gradient(1000px 800px at 50% 42%, rgba(255,255,255,.06), transparent 60%),"
    )
    return f"""<!doctype html><html><head><meta charset="utf-8"><style>
html,body{{margin:0;padding:0}}
.bg{{width:1280px;height:720px;position:relative;overflow:hidden;
  background:
    {overlay}
    radial-gradient(700px 520px at 82% 16%, {gc}, transparent 58%),
    radial-gradient(900px 700px at 92% 96%, {dc}, transparent 55%),
    radial-gradient(620px 620px at 12% 8%,  rgba(255,255,255,.10), transparent 60%),
    linear-gradient(135deg, {pc} 0%, {dpc} 58%, {dc} 100%);
}}
.blob{{position:absolute;border-radius:50%;filter:blur(64px)}}
.b1{{width:420px;height:420px;right:-60px;top:-110px;background:{ac};opacity:.30}}
.b2{{width:360px;height:360px;right:160px;bottom:-130px;background:{gc};opacity:.40}}
.grid{{position:absolute;inset:0;opacity:.07;
  background-image:linear-gradient(rgba(255,255,255,.6) 1px,transparent 1px),
                   linear-gradient(90deg,rgba(255,255,255,.6) 1px,transparent 1px);
  background-size:54px 54px}}
.bar{{position:absolute;left:0;top:0;width:8px;height:720px;background:{ac};opacity:.9}}
</style></head><body><div class="bg">
  <div class="grid"></div>
  <div class="blob b1"></div>
  <div class="blob b2"></div>
  <div class="bar"></div>
</div></body></html>"""


def render(html: str, out: Path, w: int, h: int) -> None:
    browser = find_browser()
    with tempfile.TemporaryDirectory() as td:
        hp = Path(td) / "bg.html"
        hp.write_text(html, encoding="utf-8")
        url = hp.resolve().as_uri()
        # 用 1/2 窗口 + 2x 缩放 = 超采样,边缘/模糊更干净
        cmd = [
            browser, "--headless", "--disable-gpu", "--hide-scrollbars",
            "--default-background-color=00000000",
            f"--force-device-scale-factor=2",
            f"--window-size={w // 2},{h // 2}",
            f"--screenshot={out}", url,
        ]
        subprocess.run(cmd, check=False,
                       stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    if not out.exists():
        raise SystemExit(f"[fatal] 渲染失败,未生成 {out}(浏览器: {browser})")
    print(f"[ok] {out}  ({out.stat().st_size // 1024} KB)")


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--out", type=Path, required=True)
    ap.add_argument("--kind", choices=["cover", "section"], default="cover")
    ap.add_argument("--primary", default="C00000")
    ap.add_argument("--accent", default="FFC107")
    ap.add_argument("--html", type=Path, default=None, help="渲任意 HTML 文件(忽略 kind)")
    ap.add_argument("--w", type=int, default=2560)
    ap.add_argument("--h", type=int, default=1440)
    args = ap.parse_args()
    args.out.parent.mkdir(parents=True, exist_ok=True)
    html = (args.html.read_text(encoding="utf-8") if args.html
            else build_html(args.kind, args.primary, args.accent))
    render(html, args.out, args.w, args.h)


if __name__ == "__main__":
    main()
