from __future__ import annotations

import os
import subprocess
from pathlib import Path
from typing import Sequence

from PIL import Image, ImageDraw, ImageFont


def _wrap_caption(caption: str, *, max_chars_per_line: int = 26) -> str:
    caption = (caption or "").strip()
    if not caption:
        return ""
    words = caption.split()
    lines: list[str] = []
    cur = ""
    for w in words:
        if not cur:
            cur = w
            continue
        if len(cur) + 1 + len(w) <= max_chars_per_line:
            cur += " " + w
        else:
            lines.append(cur)
            cur = w
    if cur:
        lines.append(cur)
    return "\n".join(lines)


def create_placeholder_image(
    *,
    caption: str,
    image_path: str | Path,
    width: int,
    height: int,
) -> Path:
    out_path = Path(image_path)
    out_path.parent.mkdir(parents=True, exist_ok=True)

    # Simple, non-animated “photo” placeholder; replace later with real images.
    img = Image.new("RGB", (width, height), color=(20, 20, 28))
    draw = ImageDraw.Draw(img)

    # Background accent bar.
    bar_h = int(height * 0.22)
    draw.rectangle([0, 0, width, bar_h], fill=(46, 120, 190))

    # Load a reasonable font.
    font = None
    for candidate in [
        "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
        "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
    ]:
        if os.path.exists(candidate):
            try:
                font = ImageFont.truetype(candidate, size=int(height * 0.06))
                break
            except Exception:
                continue
    if font is None:
        font = ImageFont.load_default()

    wrapped = _wrap_caption(caption, max_chars_per_line=max(20, width // 55))
    if not wrapped:
        wrapped = " "

    # Measure multiline text.
    lines = wrapped.split("\n")
    line_h = font.getbbox("Ag")[3] - font.getbbox("Ag")[1]
    total_h = line_h * len(lines)
    y = int((height - total_h) / 2) + 10
    x_center = width // 2

    for line in lines:
        bbox = draw.textbbox((0, 0), line, font=font)
        line_w = bbox[2] - bbox[0]
        x = x_center - line_w // 2

        # Shadow for readability.
        draw.text((x + 2, y + 2), line, font=font, fill=(0, 0, 0))
        draw.text((x, y), line, font=font, fill=(255, 255, 255))
        y += line_h

    img.save(out_path)
    return out_path


def render_slideshow_with_burned_captions(
    *,
    image_paths: Sequence[str | Path],
    srt_path: str | Path,
    output_mp4: str | Path,
    scene_duration_sec: float,
    fps: int,
) -> Path:
    images = [str(Path(p)) for p in image_paths]
    if not images:
        raise ValueError("No images provided for slideshow rendering.")

    srt = str(Path(srt_path))
    out = Path(output_mp4)
    out.parent.mkdir(parents=True, exist_ok=True)

    dur = float(scene_duration_sec)
    dur = max(dur, 0.1)

    ffmpeg_cmd: list[str] = ["ffmpeg", "-y"]
    for img in images:
        ffmpeg_cmd += ["-loop", "1", "-t", str(dur), "-i", img]

    n = len(images)
    concat_inputs = "".join([f"[{i}:v]" for i in range(n)])
    filter_complex = f"{concat_inputs}concat=n={n}:v=1:a=0,subtitles={srt}"

    ffmpeg_cmd += [
        "-filter_complex",
        filter_complex,
        "-r",
        str(fps),
        "-pix_fmt",
        "yuv420p",
        str(out),
    ]

    # We don't use stdout parsing; just fail fast if ffmpeg errors.
    subprocess.run(ffmpeg_cmd, check=True)
    return out

