"""Build the fully-static deployable for Cloudflare (Pages + R2).

Emits the Pages output tree and a separate R2 media sync tree:

  out_dir/
    _redirects                      apex + no-slash → /<app>/  (308)
    <app>/                          the app (served at benstull.art/<app>/)
      index.html app.js scrub.js i18n.js alteration.js style.css
      config.js                     GENERATED: { mediaBase, static: true }
      clips.json ring.json media-versions.json   baked API responses
  media_out/                        R2 upload tree — ONLY manifest-referenced files
    <clip base.mp4 / transition morphs / audio>

The app lives under a path segment (so apex can redirect to it) and uses RELATIVE
asset urls, which resolve correctly under the `/<app>/` prefix. The baked JSON is
produced through the real app (TestClient) so it is byte-identical to the API. The
media tree contains ONLY referenced files — never the master/mezzanine pipeline
sources. Cloudflare-side steps (R2 sync, Pages deploy, CORS, redirect) live in
deploy/cloudflare/ — this script only produces artifacts.

Run as a MODULE so the repo root (not tools/) is on sys.path — otherwise the
sibling tools/http.py shadows the stdlib `http` that fastapi/starlette import:

    .venv/bin/python -m tools.build_static
"""
from __future__ import annotations

import json
import shutil
from pathlib import Path

from fastapi.testclient import TestClient

from simulator.app import MEDIA_DIR, create_app

STATIC = Path(__file__).resolve().parent.parent / "simulator" / "static"
# Frontend files that ship; everything else in static/ (author*, review*) is dev-only.
PUBLIC_ASSETS = ["index.html", "app.js", "scrub.js", "i18n.js", "alteration.js", "style.css",
                 "credits.html", "credits.js"]


def _bake_api(app_dir: Path) -> dict:
    app = create_app()
    client = TestClient(app)
    clips = client.get("/api/clips").json()
    ring = client.get("/api/ring").json()
    versions = client.get("/api/media-versions").json()
    (app_dir / "clips.json").write_text(json.dumps(clips, ensure_ascii=False))
    (app_dir / "ring.json").write_text(json.dumps(ring, ensure_ascii=False))
    (app_dir / "media-versions.json").write_text(json.dumps(versions, ensure_ascii=False))
    return versions["versions"]


def _write_config(app_dir: Path, media_base: str) -> None:
    (app_dir / "config.js").write_text(
        "// GENERATED by tools/build_static.py — do not edit in dist/.\n"
        f'window.HEF_CONFIG = {{ mediaBase: "{media_base}", static: true }};\n'
    )


def _write_redirects(out: Path, seg: str) -> None:
    # Apex → app path, and the no-trailing-slash form → slashed (so RELATIVE asset
    # urls resolve under the prefix). Cloudflare Pages `_redirects` syntax.
    (out / "_redirects").write_text(
        f"/  /{seg}/  308\n"
        f"/{seg}  /{seg}/  308\n"
    )


def build_static(out_dir, media_out, *, media_base: str, app_path: str) -> dict:
    out = Path(out_dir)
    media = Path(media_out)
    seg = app_path.strip("/")
    app_dir = out / seg
    for d in (out, media):
        if d.exists():
            shutil.rmtree(d)
    app_dir.mkdir(parents=True)
    media.mkdir(parents=True)

    for name in PUBLIC_ASSETS:
        src = STATIC / name
        if src.exists():
            shutil.copy2(src, app_dir / name)

    versions = _bake_api(app_dir)
    _write_config(app_dir, media_base)
    _write_redirects(out, seg)

    # Sync ONLY referenced media: `versions` covers clip bases + transition morphs;
    # add each scale's audio explicitly. Never the master/mezzanine pipeline sources.
    referenced = set(versions.keys())
    ring = json.loads((app_dir / "ring.json").read_text())
    for s in ring.get("scales", []):
        if s.get("audio"):
            referenced.add(f"audio/{s['audio']}")
    n = 0
    for rel in sorted(referenced):
        src = MEDIA_DIR / rel
        if not src.exists():
            raise FileNotFoundError(f"referenced media missing on disk: {rel}")
        dst = media / rel
        dst.parent.mkdir(parents=True, exist_ok=True)
        shutil.copy2(src, dst)
        n += 1

    return {"out": str(out), "app_dir": str(app_dir), "media": str(media),
            "media_files": n, "app_path": app_path}


if __name__ == "__main__":
    import argparse

    ap = argparse.ArgumentParser()
    ap.add_argument("--out", default="dist")
    ap.add_argument("--media-out", default="dist-media")
    ap.add_argument("--media-base", default="https://static.benstull.art/")
    ap.add_argument("--app-path", default="/human-experience-simulator")
    a = ap.parse_args()
    r = build_static(a.out, a.media_out, media_base=a.media_base, app_path=a.app_path)
    print(json.dumps(r, indent=2))
