#!/usr/bin/env python3
"""Offline ESSIE desktop pack · fullscreen tabs · Start · wallpaper · no taskbar."""
from __future__ import annotations

import json
import time
from pathlib import Path

import hotswap_core as H

# Menu only · no plugs open on start (engine/Kate path · not Firefox-first)
START_TABS = []


def _json_for_script(obj) -> str:
    """JSON safe inside <script> · never break on </script> in plug source."""
    return (
        json.dumps(obj, ensure_ascii=False)
        .replace("<", "\\u003c")
        .replace(">", "\\u003e")
        .replace("&", "\\u0026")
        .replace("\u2028", "\\u2028")
        .replace("\u2029", "\\u2029")
    )


def _theme_data_uris() -> str:
    import base64

    root = H.ROOT / "assets" / "player"
    parts = [":root {"]
    for key, fname in (
        ("--player-bg-dark", "theme-dark.jpg"),
        ("--player-bg-rainbow", "theme-rainbow.jpg"),
    ):
        p = root / fname
        if not p.exists():
            continue
        b64 = base64.b64encode(p.read_bytes()).decode("ascii")
        parts.append(f'  {key}: url("data:image/jpeg;base64,{b64}");')
    parts.append("}")
    if len(parts) <= 2:
        return "/* no player theme images */"
    return "\n".join(parts)


def _host_thermo() -> dict:
    zones = []
    for p in sorted(Path("/sys/class/thermal").glob("thermal_zone*/temp")):
        try:
            raw = int(p.read_text().strip())
            if raw < 1000:
                raw = raw * 1000
            zones.append({"path": str(p), "temp_mC": raw})
        except (OSError, ValueError):
            continue
    if not zones:
        return {"temp_mC": None, "celsius": None, "src": "none"}
    best = max(zones, key=lambda z: z["temp_mC"])
    return {
        "temp_mC": best["temp_mC"],
        "celsius": best["temp_mC"] / 1000.0,
        "zones": zones[:8],
        "src": "sysfs",
    }


def _desktop_runtime() -> str:
    p = H.ROOT / "js" / "desktop.js"
    if not p.exists():
        raise SystemExit("missing js/desktop.js")
    return p.read_text()


def _js_commands() -> str:
    p = H.ROOT / "js" / "commands.js"
    if not p.exists():
        return "/* no commands.js */"
    return p.read_text()


def build_pack() -> Path:
    H.ensure_dirs()
    stamp = time.strftime("%Y%m%d-%H%M%S")
    out = H.OUT / f"pack-{stamp}.html"
    man = H.load_manifest()
    seats = H.load_seats()
    active = (seats.get("seats") or {}).get("ui.stage") or seats.get("active")

    css_parts = []
    for name in ("spv.css", "shell.css"):
        p = H.ROOT / "css" / name
        if not p.exists():
            continue
        txt = p.read_text()
        if name == "shell.css":
            txt = txt.replace('@import url("spv.css");', "/* spv.css inlined above */")
        txt = txt.replace('url("../assets/player/theme-dark.jpg")', "none")
        txt = txt.replace('url("../assets/player/theme-rainbow.jpg")', "none")
        css_parts.append(txt)
    css_parts.append(_theme_data_uris())
    css = "\n".join(css_parts)

    blocks = []
    for p in man.get("plugs", []):
        src_dom, src_js = H.SPVS / p["dom"], H.SPVS / p["js"]
        if active and active.get("id") == p["id"]:
            if (H.CUR / p["dom"]).exists():
                src_dom = H.CUR / p["dom"]
            if (H.CUR / p["js"]).exists():
                src_js = H.CUR / p["js"]
        if not src_dom.exists() or not src_js.exists():
            continue
        blocks.append(
            {
                "id": p["id"],
                "title": p.get("title", p["id"]),
                "icon": p.get("icon", "·"),
                "theme": p.get("theme", "dark"),
                "dom": src_dom.read_text(),
                "js": src_js.read_text(),
                "rides": p.get("rides", "BGF"),
                "family": p.get("family") or p.get("proto") or "",
            }
        )

    alsa_blob, stream_blob, probe_blob, nv_blob = {}, {}, {}, {}
    try:
        import alsa_io as A

        alsa_blob = A.export_for_pack()
    except Exception as e:
        alsa_blob = {"engine": "alsa", "error": str(e), "devices": []}
    try:
        import stream_io as S

        stream_blob = S.export_for_pack()
        for bad in ("stream_key", "key", "rtmp_url", "url"):
            stream_blob.pop(bad, None)
    except Exception as e:
        stream_blob = {"engine": "stream", "error": str(e), "secure": True}
    try:
        import nv_mp4 as NV

        nv_blob = NV.export_for_pack()
        for bad in ("stream_key", "key", "rtmp_url", "url"):
            nv_blob.pop(bad, None)
    except Exception as e:
        nv_blob = {"engine": "nv_mp4", "error": str(e), "loose_ffmpeg": True, "ready_playback": True}
    try:
        import proto_probe as PP

        probe_blob = PP.run_all(
            ["icmp", "dns", "ssh", "https", "ntp", "serial", "git", "rtmp", "tcp"]
        )
    except Exception as e:
        probe_blob = {"probes": {}, "error": str(e)}
    thermo_blob = _host_thermo()
    runtime = _desktop_runtime()
    commands_js = _js_commands()

    ids = {b["id"] for b in blocks}
    start_tabs = [t for t in START_TABS if t in ids]

    html = f"""<!DOCTYPE html>
<html lang="en"><head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<title>ESSIE</title>
<meta name="description" content="ESSIE desktop · fullscreen tabs · Start · SPV plugs"/>
<style>
{css}
</style></head>
<body class="mode-online desk-os desk-fullscreen">
<div id="wallpaper" aria-hidden="true"></div>

<div id="tabbar" class="tabbar">
  <button type="button" id="start-btn" class="start-btn" title="Start · ESSIE plugs">
    <span class="start-mark">E</span>
  </button>
  <div id="tab-row" class="tab-row" role="tablist"></div>
  <div class="tabbar-end">
    <span id="hud-pill" class="hud-pill">ESSIE</span>
    <button type="button" id="net-toggle" class="net-toggle on" data-mode="online">Online</button>
  </div>
</div>

<aside id="start-menu" class="start-menu" aria-label="Start">
  <div class="fly-head">
    <strong>ESSIE</strong>
    <span>launch · open tab</span>
  </div>
  <div id="fly-plugs" class="fly-plugs"></div>
</aside>

<aside id="ctx-menu" class="ctx-menu" aria-label="Context">
  <div class="ctx-head">ESSIE</div>
  <div id="ctx-list" class="ctx-list"></div>
</aside>

<div class="desk-body">
  <main class="desk-main">
    <section id="stage" class="stage-surface"></section>
  </main>
  <aside id="chat" class="desk-chat">
    <div id="chat-log" class="chat-log"></div>
    <form id="chat-form" class="chat-form" autocomplete="off">
      <input id="chat-in" type="text" placeholder="/in js_all · /fox · Start menu" />
      <button type="submit">↵</button>
    </form>
  </aside>
</div>

<script>window.__BGRTX_PACK__={_json_for_script(blocks)};</script>
<script>window.__BGRTX_START_TABS__={_json_for_script(start_tabs)};</script>
<script>window.__BGRTX_ALSA__={_json_for_script(alsa_blob)};</script>
<script>window.__BGRTX_STREAM__={_json_for_script(stream_blob)};</script>
<script>window.__BGRTX_NV_MP4__={_json_for_script(nv_blob)};</script>
<script>window.__BGRTX_PROBES__={_json_for_script(probe_blob)};</script>
<script>window.__BGRTX_THERMO__={_json_for_script(thermo_blob)};</script>
<script>
{commands_js}
</script>
<script>
{runtime}
</script>
<!-- no Firefox kiosk · engine sheet is preferred display; pack is optional -->
</body></html>
"""
    out.write_text(html)
    return out


if __name__ == "__main__":
    print("PACK", build_pack())
