"""Prepare public crops, labeled contact sheets, and recordings from a private capture index.
Usage: python3 make-assets.py private-index.json public-directory
"""
import datetime
import json
import pathlib
import shutil
import subprocess
import sys

index = json.loads(pathlib.Path(sys.argv[1]).read_text())
out = pathlib.Path(sys.argv[2])
out.mkdir(parents=True, exist_ok=True)
labels = {
    "known": "Codex: known 100,000-token limit",
    "budget": "Codex: token_budget enabled",
    "body": "Codex: body_after_prefix scope",
    "absent": "Codex: no configured limit",
    "zero": "Codex: zero limit (interrupted after compaction)",
    "capacity": "Codex: limit = capacity (258,400)",
    "above": "Codex: limit > capacity (300,000)",
    "clamped": "Codex: limit near capacity (240,000)",
    "child": "Codex: root that spawns a native child",
    "child-agent": "Codex: native child agent",
    "claude-default": "Claude: default auto-compaction setting",
    "claude-limit": "Claude: 150,000 window; 117,000 threshold",
}
def parse(t):
    return datetime.datetime.fromisoformat(t.replace("Z", "+00:00"))
def stamp(seconds):
    ms = max(0, round(seconds * 1000))
    return f"{ms//3600000:02}:{ms//60000%60:02}:{ms//1000%60:02},{ms%1000:03}"
for side, data in index.items():
    captures = data["captures"]
    tiles = []
    cues = []
    start = parse(data["startedAt"])
    duration = float(subprocess.check_output(["ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "csv=p=0", data["recording"]], text=True))
    for i, c in enumerate(captures):
        tile = out / f"{side}-tile-{i}.png"
        subprocess.run(["convert", "-background", "white", "-fill", "black", "-font", "DejaVu-Sans", "-pointsize", "28", "-size", "1280x45", "label:"+labels[c["case"]], "(", c["path"], "-crop", "1280x320+0+0", "+repage", ")", "-append", "+set", "label", str(tile)], check=True)
        tiles.append(str(tile))
        if c["case"] in ["known", "claude-default", "claude-limit"]:
            shutil.copyfile(c["path"], out / f"{side}-{c['case']}.png")
        t0 = (parse(c["startedAt"]) - start).total_seconds()
        t1 = (parse(captures[i+1]["startedAt"]) - start).total_seconds() if i+1 < len(captures) else duration
        cues.append(f"{i+1}\n{stamp(t0)} --> {stamp(t1)}\n{side.upper()}: {labels[c['case']]}\nWeb; light; 1440 x 900 CSS; DPR 2\n")
    grid = out / f"{side}-grid.png"
    subprocess.run(["montage", *tiles, "-tile", "2x6", "-geometry", "+0+0", str(grid)], check=True)
    header = f"{side.upper()} | real web app | light theme | 1440 x 900 CSS | DPR 2\nSame crop; two identical screenshots 500 ms apart before every still."
    subprocess.run(["convert", "-background", "white", "-fill", "black", "-font", "DejaVu-Sans", "-pointsize", "28", "-size", "2560x100", "label:"+header, str(grid), "-append", str(out / f"{side}-all-cases.png")], check=True)
    subtitle = out / f"{side}-cases.srt"
    subtitle.write_text("\n".join(cues))
    subprocess.run(["ffmpeg", "-v", "error", "-i", data["recording"], "-vf", f"crop=1280:500:1600:0,subtitles={subtitle}:force_style='Fontsize=14,Alignment=2,MarginV=8'", "-an", "-map_metadata", "-1", "-c:v", "libx264", "-crf", "18", str(out/f"{side}-all-cases.mp4")], check=True)
