"""Image + video thumbnail generation.

Images go through Pillow; videos go through ffmpeg's first-frame snapshot
at ~5% into the runtime (avoids fade-ins/title cards while still being a
representative frame).

Thumbnails are stored at /images/thumbs/{sha[:3]}/{sha}.jpg or .png; the
3-char prefix bucket avoids hot single-directory inode pressure.
"""

import subprocess
from dataclasses import dataclass
from pathlib import Path

from PIL import Image

THUMB_MAX_PX = 400
JPEG_QUALITY = 85
FFMPEG_TIMEOUT_SECONDS = 60


@dataclass(frozen=True)
class ThumbnailResult:
    path: Path
    width: int
    height: int
    mime: str


def thumbnail_path_for(sha256_hex: str, *, alpha: bool, root: Path) -> Path:
    ext = ".png" if alpha else ".jpg"
    bucket = sha256_hex[:3]
    return root / "thumbs" / bucket / f"{sha256_hex}{ext}"


class Thumbnailer:
    def __init__(self, images_root: Path):
        self.images_root = images_root

    def generate_image_thumbnail(
        self, source: Path, sha256_hex: str
    ) -> ThumbnailResult:
        """Reads source, fits inside THUMB_MAX_PX × THUMB_MAX_PX preserving
        aspect ratio. Writes PNG if source has alpha, else JPEG.
        """
        with Image.open(source) as im:
            im.load()
            has_alpha = im.mode in ("RGBA", "LA") or (
                im.mode == "P" and "transparency" in im.info
            )
            if has_alpha:
                im = im.convert("RGBA")
            else:
                im = im.convert("RGB")
            im.thumbnail((THUMB_MAX_PX, THUMB_MAX_PX), Image.Resampling.LANCZOS)

            dest = thumbnail_path_for(sha256_hex, alpha=has_alpha, root=self.images_root)
            dest.parent.mkdir(parents=True, exist_ok=True)
            if has_alpha:
                im.save(dest, "PNG", optimize=True)
                mime = "image/png"
            else:
                im.save(dest, "JPEG", quality=JPEG_QUALITY, optimize=True, progressive=True)
                mime = "image/jpeg"
            return ThumbnailResult(path=dest, width=im.width, height=im.height, mime=mime)

    def generate_video_thumbnail(
        self, source: Path, sha256_hex: str, duration_seconds: float | None = None
    ) -> ThumbnailResult:
        """Snapshots a frame ~5% into the video via ffmpeg, then runs that
        frame through the image-thumbnail pipeline.

        duration_seconds is optional; if absent, we snapshot at the 1-second
        mark which is a reasonable fallback for short clips.
        """
        offset = max(1.0, (duration_seconds or 20.0) * 0.05)
        tmp = self.images_root / "thumbs" / "_tmp" / f"{sha256_hex}.png"
        tmp.parent.mkdir(parents=True, exist_ok=True)
        try:
            cmd = [
                "ffmpeg",
                "-ss",
                f"{offset:.2f}",
                "-i",
                str(source),
                "-frames:v",
                "1",
                "-vf",
                f"scale='if(gt(iw,ih),{THUMB_MAX_PX},-2)':'if(gt(iw,ih),-2,{THUMB_MAX_PX})'",
                "-y",
                str(tmp),
            ]
            subprocess.run(
                cmd,
                check=True,
                capture_output=True,
                timeout=FFMPEG_TIMEOUT_SECONDS,
            )
            return self.generate_image_thumbnail(tmp, sha256_hex)
        finally:
            if tmp.exists():
                tmp.unlink()
