#!/usr/bin/env python3
"""
Neyralfoundry Runner — slim glue script.

Reads a Studio bundle JSON (exported from neyralfoundry.com/studio.html),
calls a video diffusion model via diffusers, and writes the resulting MP4
with the Neyralfoundry visible watermark burned in.

This is a STARTING POINT. Adapt the RENDER_FN block to your local model
of choice (HunyuanVideo, CogVideoX, Wan2.1, AnimateDiff via diffusers,
or any other video diffusion runtime).

Requirements:
  - Python 3.10+
  - torch with CUDA 12.x
  - diffusers >= 0.30
  - ffmpeg in PATH (for watermark)

Usage:
  python neyralfoundry_runner.py bundle.json [--out reel.mp4]

License: MIT for this glue. Models you use have their own licenses.
"""
import argparse
import json
import os
import subprocess
import sys
import tempfile
from pathlib import Path


def load_bundle(path: str) -> dict:
    with open(path, "r", encoding="utf-8") as f:
        bundle = json.load(f)
    expected = ("schema_version", "scene_id", "prompt", "seed",
                "duration_sec", "target")
    for key in expected:
        if key not in bundle:
            raise SystemExit(f"bundle missing required key: {key}")
    return bundle


def render_with_diffusers(bundle: dict, raw_path: str) -> None:
    """
    REPLACE THIS BLOCK to call your local video diffusion model.

    Examples:
      - HunyuanVideo CLI
      - diffusers.AnimateDiffPipeline
      - diffusers.HunyuanVideoPipeline
      - ComfyUI HTTP API (POST to /prompt)
      - Wan2.1 CLI
      - SVD (Stable Video Diffusion) via diffusers

    The bundle gives you: prompt, seed, duration_sec, target.width,
    target.height, target.fps, cfg, steps. Map them to your runtime's
    expected interface.

    The output must be an MP4 at `raw_path` matching target resolution
    and fps. After this returns, the runner will burn the watermark.
    """
    print(f"[runner] PLACEHOLDER render → {raw_path}")
    print(f"[runner] prompt: {bundle['prompt'][:80]}...")
    print(f"[runner] seed:   {bundle['seed']}  duration: {bundle['duration_sec']}s")
    print(f"[runner] target: {bundle['target']['width']}x{bundle['target']['height']} @ {bundle['target']['fps']}fps")
    print()
    print("  TODO: replace render_with_diffusers() with your actual model call.")
    print("  Expected output: an MP4 at the path passed in raw_path.")
    print()

    # Generate a black placeholder so the watermark step can run end-to-end:
    width = bundle["target"]["width"]
    height = bundle["target"]["height"]
    fps = bundle["target"]["fps"]
    duration = bundle["duration_sec"]
    subprocess.run([
        "ffmpeg", "-y", "-loglevel", "error",
        "-f", "lavfi", "-i", f"color=c=black:s={width}x{height}:r={fps}:d={duration}",
        "-c:v", "libx264", "-preset", "ultrafast", "-crf", "28",
        "-pix_fmt", "yuv420p", raw_path,
    ], check=True)


def burn_watermark(raw_path: str, out_path: str) -> None:
    """Apply the Neyralfoundry visible watermark via ffmpeg drawtext."""
    # Locate a usable font
    font = None
    for cand in (
        "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
        "/Library/Fonts/Arial.ttf",
        "C:/Windows/Fonts/arial.ttf",
    ):
        if os.path.exists(cand):
            font = cand
            break
    if not font:
        sys.exit("ERROR: no usable TTF font found. Install DejaVu Sans or Arial.")

    wm = (
        f"drawtext=fontfile={font}:text='made with neyralfoundry.com':"
        "fontcolor=white@0.55:fontsize=22:x=w-tw-30:y=h-th-30:"
        "shadowcolor=black@0.55:shadowx=2:shadowy=2:"
        "box=1:boxcolor=black@0.18:boxborderw=8"
    )
    subprocess.run([
        "ffmpeg", "-y", "-loglevel", "error",
        "-i", raw_path,
        "-vf", wm,
        "-c:v", "libx264", "-preset", "slow", "-crf", "18",
        "-pix_fmt", "yuv420p", "-movflags", "+faststart",
        "-an", out_path,
    ], check=True)


def main() -> None:
    p = argparse.ArgumentParser()
    p.add_argument("bundle", help="Studio bundle JSON (exported from neyralfoundry.com/studio.html)")
    p.add_argument("--out", default="reel.mp4", help="Output MP4 path (default: reel.mp4)")
    args = p.parse_args()

    bundle = load_bundle(args.bundle)
    print(f"[runner] loaded scene: {bundle.get('title', bundle['scene_id'])}")

    with tempfile.TemporaryDirectory() as tmp:
        raw = str(Path(tmp) / "raw.mp4")
        render_with_diffusers(bundle, raw)
        burn_watermark(raw, args.out)

    print(f"[runner] wrote {args.out}")
    print("[runner] watermark obligation: 'made with neyralfoundry.com' must remain on shared output. See https://neyralfoundry.com/legal.html")


if __name__ == "__main__":
    main()
