"""Render the curated zero-key Remotion demos.

This script is Remotion-specific by design — the demos live in
`remotion-composer/public/demo-props/` as JSON props for existing React
scene components. It is NOT a cross-runtime demo harness.

For a HyperFrames demo, run `make hyperframes-doctor` to verify the runtime
floor, then either scaffold a real composition via `npx hyperframes init`
or drive `hyperframes_compose` from the Agent SDK. HyperFrames demos are
authored as HTML + GSAP in a project workspace, not as JSON props here.
"""

from __future__ import annotations

import argparse
import json
import shutil
import subprocess
import sys
from pathlib import Path


ROOT_DIR = Path(__file__).resolve().parent
COMPOSER_DIR = ROOT_DIR / "remotion-composer"
PROPS_DIR = COMPOSER_DIR / "public" / "demo-props"
OUTPUT_DIR = ROOT_DIR / "projects" / "demos" / "renders"

DEMO_DESCRIPTIONS = {
    "world-in-numbers": "Global scale story with titles, stats, and charts",
    "code-to-screen": "Developer workflow explainer with comparison and KPI cards",
    "focusflow-pitch": "Startup-style pitch built only from Remotion components",
}


def discover_demos() -> dict[str, Path]:
    if not PROPS_DIR.exists():
        return {}
    return {path.stem: path for path in sorted(PROPS_DIR.glob("*.json"))}


def find_command(*names: str) -> str | None:
    for name in names:
        resolved = shutil.which(name)
        if resolved:
            return resolved
    return None


def ensure_demo_environment() -> str:
    if not find_command("node", "node.exe"):
        raise SystemExit("Error: Node.js is required. Install it from https://nodejs.org/")

    npm_cmd = find_command("npm.cmd", "npm", "npm.exe")
    if not npm_cmd:
        raise SystemExit("Error: npm is required but was not found on PATH.")

    npx_cmd = find_command("npx.cmd", "npx", "npx.exe")
    if not npx_cmd:
        raise SystemExit("Error: npx is required but was not found on PATH.")

    if not (COMPOSER_DIR / "node_modules").exists():
        print("Installing Remotion dependencies...")
        subprocess.run([npm_cmd, "install"], cwd=COMPOSER_DIR, check=True)

    return npx_cmd


def validate_props_file(path: Path) -> None:
    with path.open("r", encoding="utf-8") as handle:
        payload = json.load(handle)

    if not isinstance(payload.get("cuts"), list) or not payload["cuts"]:
        raise SystemExit(f"Error: {path} must define at least one cut.")


def render_demo(name: str, props_path: Path, npx_cmd: str) -> None:
    validate_props_file(props_path)
    OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
    output_path = OUTPUT_DIR / f"{name}.mp4"

    print()
    print(f"Rendering: {name}")
    print(f"Props:     {props_path}")
    print(f"Output:    {output_path}")
    print()

    subprocess.run(
        [
            npx_cmd,
            "remotion",
            "render",
            "src/index.tsx",
            "Explainer",
            str(output_path),
            "--props",
            str(props_path),
            "--codec",
            "h264",
        ],
        cwd=COMPOSER_DIR,
        check=True,
    )

    if output_path.exists():
        size_mb = output_path.stat().st_size / (1024 * 1024)
        print(f"Done: {output_path} ({size_mb:.1f} MB)")
    else:
        print("Render finished without creating the expected output file.")


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(
        description="Render zero-key OpenMontage demo videos from checked-in Remotion props."
    )
    parser.add_argument("demo", nargs="?", help="Render one named demo instead of all demos.")
    parser.add_argument("--list", action="store_true", help="List available demo fixtures and exit.")
    args = parser.parse_args(argv)

    demos = discover_demos()
    if not demos:
        raise SystemExit(f"Error: No demo prop files were found in {PROPS_DIR}.")

    if args.list:
        print("Available zero-key demos:")
        for name in demos:
            description = DEMO_DESCRIPTIONS.get(name, "Checked-in Remotion demo")
            print(f"  {name:20} {description}")
        return 0

    if args.demo and args.demo not in demos:
        available = ", ".join(demos)
        raise SystemExit(f"Unknown demo '{args.demo}'. Available demos: {available}")

    npx_cmd = ensure_demo_environment()
    selected = {args.demo: demos[args.demo]} if args.demo else demos

    for name, props_path in selected.items():
        render_demo(name, props_path, npx_cmd)

    return 0


if __name__ == "__main__":
    sys.exit(main())
