#!/usr/bin/env python3
"""Generate Director Pass QA artifacts for a preview video.

Outputs:
- first_second_sheet.png with frames at 0.0, 0.5, 1.0, 1.5 seconds
- director_qa_template.json with verdict fields to be filled by the agent/human/vision QA
- optional beat frames every N seconds for beat-density review

This script does not judge aesthetics by itself; it makes the required QA artifacts impossible to forget.
"""
from __future__ import annotations

import argparse
import json
import subprocess
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont


def ffprobe_duration(video: Path) -> float:
    return float(subprocess.check_output([
        "ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "default=nw=1:nk=1", str(video)
    ], text=True).strip())


def font(size: int):
    for p in ["/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", "/usr/share/fonts/truetype/liberation2/LiberationSans-Bold.ttf"]:
        if Path(p).exists():
            return ImageFont.truetype(p, size)
    return ImageFont.load_default()


def extract_frame(video: Path, ts: float, out: Path) -> None:
    # For 0.0s use -frames after input to avoid seeking past the first frame.
    subprocess.check_call([
        "ffmpeg", "-y", "-hide_banner", "-loglevel", "error", "-ss", f"{ts:.3f}", "-i", str(video), "-frames:v", "1", str(out)
    ])


def make_sheet(frames: list[tuple[float, Path]], out: Path) -> None:
    thumb_w, thumb_h = 270, 480
    sheet = Image.new("RGB", (thumb_w * len(frames), thumb_h + 54), (12, 12, 12))
    draw = ImageDraw.Draw(sheet)
    fnt = font(26)
    for i, (ts, frame) in enumerate(frames):
        im = Image.open(frame).convert("RGB").resize((thumb_w, thumb_h), Image.Resampling.LANCZOS)
        x = i * thumb_w
        sheet.paste(im, (x, 0))
        label = f"{ts:.1f}s"
        draw.text((x + 12, thumb_h + 12), label, fill=(255, 255, 255), font=fnt)
    sheet.save(out, quality=94)


def main() -> int:
    ap = argparse.ArgumentParser(description="Create Director Pass QA artifacts for a preview video.")
    ap.add_argument("video", type=Path)
    ap.add_argument("--out-dir", type=Path, required=True)
    ap.add_argument("--candidate-id", default=None)
    ap.add_argument("--beat-step", type=float, default=3.0)
    args = ap.parse_args()

    video = args.video.resolve()
    out_dir = args.out_dir.resolve()
    out_dir.mkdir(parents=True, exist_ok=True)
    duration = ffprobe_duration(video)

    first_second_times = [0.0, 0.5, 1.0, 1.5]
    first_frames: list[tuple[float, Path]] = []
    for ts in first_second_times:
        if ts < duration:
            out = out_dir / f"first_second_{str(ts).replace('.', '_')}s.png"
            extract_frame(video, ts, out)
            first_frames.append((ts, out))
    first_sheet = out_dir / "first_second_sheet.png"
    make_sheet(first_frames, first_sheet)

    beat_frames = []
    t = 0.0
    while t < duration:
        out = out_dir / f"beat_{str(round(t, 1)).replace('.', '_')}s.png"
        extract_frame(video, min(t, max(0, duration - 0.05)), out)
        beat_frames.append({"time": round(t, 2), "path": str(out)})
        t += args.beat_step

    template = {
        "candidate_id": args.candidate_id,
        "video": str(video),
        "duration": duration,
        "first_second_sheet": str(first_sheet),
        "first_second_frames": [{"time": ts, "path": str(p)} for ts, p in first_frames],
        "beat_frames": beat_frames,
        "director_pass_required_verdicts": {
            "silent_recognition_verdict": None,
            "silent_recognition_notes": None,
            "readability_check": None,
            "readability_notes": None,
            "cheap_effect_check": None,
            "cheap_effect_notes": "Confirm no artificial shake, frame vibration, rotation wobble, cheap flash, PowerPoint/mockup look, arrows, circles, boxes, labels, or cards.",
            "beat_density_check": None,
            "beat_density_notes": "Confirm a meaningful visual/story beat every ~2–4 seconds.",
            "eye_direction_check": None,
            "eye_direction_notes": "Confirm the viewer's eye is guided to the dangerous screen / near mistake / safe move.",
            "end_silence_check": None,
            "end_silence_notes": "Confirm final video does not outlast voiceover except for an intentional <=0.5s resolve tail."
        }
    }
    template_path = out_dir / "director_qa_template.json"
    template_path.write_text(json.dumps(template, indent=2), encoding="utf-8")
    print(json.dumps({"ok": True, "duration": duration, "first_second_sheet": str(first_sheet), "template": str(template_path)}, indent=2))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
