#!/usr/bin/env python3
"""Extract screenshots from a video at regular intervals, label each with a
timestamp, and assemble into a single montage grid image.

Usage:
    /opt/hermes/.venv/bin/python scripts/video-montage.py <video.mp4> <interval_sec> <output.png>

Requirements:
    - ffmpeg (system, for frame extraction)
    - Pillow (/opt/hermes/.venv/bin/python has it)

Why this script exists:
    ImageMagick (convert/montage) is NOT installed on the Hermes VPS and cannot
    be installed without root. Pillow in the Hermes venv is the reliable path
    for both text labeling and grid composition.

Example:
    # Extract one frame every 5 seconds, build a 4-column grid
    /opt/hermes/.venv/bin/python scripts/video-montage.py film.mp4 5 montage.png
"""
import subprocess
import sys
import os
import tempfile
import glob
from PIL import Image, ImageDraw, ImageFont


def extract_frames(video_path, interval_sec, out_dir):
    """Use ffmpeg to extract one frame every N seconds."""
    pattern = os.path.join(out_dir, "frame_%04d.png")
    subprocess.run(
        [
            "ffmpeg", "-y", "-i", video_path,
            "-vf", f"fps=1/{interval_sec}",
            pattern,
        ],
        capture_output=True, check=True,
    )
    return sorted(glob.glob(os.path.join(out_dir, "frame_*.png")))


def load_font(size=36):
    for path in [
        "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
        "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
        "/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf",
    ]:
        if os.path.exists(path):
            return ImageFont.truetype(path, size)
    return ImageFont.load_default()


def label_frames(frame_paths, interval_sec):
    """Return list of PIL Images with timestamp labels drawn on them."""
    font = load_font(36)
    labeled = []
    for i, fp in enumerate(frame_paths):
        ts = f"{i * interval_sec}s"
        img = Image.open(fp)
        draw = ImageDraw.Draw(img)
        x, y = 15, img.height - 50
        # Black outline for readability
        for dx in range(-3, 4, 2):
            for dy in range(-3, 4, 2):
                draw.text((x + dx, y + dy), ts, fill="black", font=font)
        draw.text((x, y), ts, fill="yellow", font=font)
        labeled.append(img)
    return labeled


def build_montage(images, cols=4, margin=10, bg=(10, 10, 10)):
    """Assemble images into a grid."""
    n = len(images)
    rows = (n + cols - 1) // cols
    w, h = images[0].size
    grid_w = cols * w + (cols + 1) * margin
    grid_h = rows * h + (rows + 1) * margin
    canvas = Image.new("RGB", (grid_w, grid_h), bg)
    for idx, img in enumerate(images):
        r, c = idx // cols, idx % cols
        x = margin + c * (w + margin)
        y = margin + r * (h + margin)
        canvas.paste(img, (x, y))
    return canvas


def main():
    if len(sys.argv) != 4:
        print(f"Usage: {sys.argv[0]} <video.mp4> <interval_sec> <output.png>")
        sys.exit(1)
    video_path, interval_str, output_path = sys.argv[1], sys.argv[2], sys.argv[3]
    interval = int(interval_str)

    with tempfile.TemporaryDirectory() as tmpdir:
        frames = extract_frames(video_path, interval, tmpdir)
        print(f"Extracted {len(frames)} frames")
        labeled = label_frames(frames, interval)
        montage = build_montage(labeled)
        montage.save(output_path, "PNG")
        size_mb = os.path.getsize(output_path) / 1024 / 1024
        print(f"Montage saved: {output_path} ({size_mb:.1f} MB)")


if __name__ == "__main__":
    main()
