#!/usr/bin/env python3
"""Slideshow video: images -> smooth Ken Burns moves (sub-pixel, no zoompan jitter), crossfades, optional captions
per slide and a music bed that fades out at the end.

Examples
  python slideshow.py --images https://.../1.png https://.../2.png https://.../3.png --music https://.../song.mp3 --aspect 9:16
  python slideshow.py --images ./photos --seconds 4 --xfade 0.8 --captions "Day one|The climb|Summit" --aspect 16:9
"""
from __future__ import annotations

import argparse
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent))
import kmedia as K  # noqa: E402


def main() -> None:
    ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    ap.add_argument("--images", nargs="+", required=True, help="image URLs / files / a folder (in order)")
    ap.add_argument("--seconds", type=float, default=3.5, help="per slide (default 3.5)")
    ap.add_argument("--xfade", type=float, default=0.7)
    ap.add_argument("--transition", default="fade", help="fade, dissolve, slideleft, circleopen, ... or cut")
    ap.add_argument("--motion", choices=["kenburns", "none"], default="kenburns")
    ap.add_argument("--fit", choices=["cover", "blur"], default="cover", help="blur keeps whole images (no crop)")
    ap.add_argument("--captions", default="", help="'text 1|text 2|...' one per slide (empty entries allowed)")
    ap.add_argument("--caption-style", choices=["box", "bold", "minimal"], default="box")
    ap.add_argument("--music", default="")
    ap.add_argument("--music-db", type=float, default=-4.0)
    ap.add_argument("--aspect", default="16:9")
    ap.add_argument("--res", type=int, default=1080)
    ap.add_argument("--fps", type=int, default=30)
    K.add_font_args(ap)
    ap.add_argument("--out", default="kenerate-out/slideshow-video/slideshow.mp4")
    ap.add_argument("--keep-temp", action="store_true")
    a = K.parse_args(ap)

    srcs = []
    for s in a.images:
        p = Path(s)
        if not K.is_url(s) and p.is_dir():
            srcs += sorted(str(x) for x in p.iterdir() if x.suffix.lower() in K.IMAGE_EXT)
        else:
            srcs.append(s)
    if not srcs:
        K.fail("No images found.")
    st = K.Status(5, "slideshow-video")
    K.require(status=st)
    W, H = K.size_for(a.aspect, a.res)
    tmp = K.workdir(a.keep_temp)
    out = Path(a.out)
    out.parent.mkdir(parents=True, exist_ok=True)

    st.step(f"Fetching {len(srcs)} image(s)" + (" + music" if a.music else ""))
    files = K.fetch_all(srcs, tmp / "in", st, prefix="img")
    music = K.fetch(a.music, tmp / "in", "music", st) if a.music else None

    st.step(f"Animating {len(files)} slide(s) at {W}x{H}")
    xf = 0 if a.transition == "cut" else min(a.xfade, a.seconds / 2.5)
    per = a.seconds + xf  # each slide is longer by the overlap so the visible time stays --seconds
    clips = []
    for i, f in enumerate(files):
        dst = tmp / f"slide_{i:02d}.mp4"
        if a.motion == "kenburns":
            d = K.still_to_clip(f, dst, W, H, per, a.fps, move=i, fit=a.fit)
        else:
            d = K.normalize_clip(f, dst, W, H, a.fps, fit="cover" if a.fit == "cover" else "blur", still_seconds=per,
                                still_motion=False)
        clips.append((dst, d))
        st.progress((i + 1) * 100 / len(files), f"slide {i + 1}/{len(files)}")

    st.step("Joining with transitions")
    joined = tmp / "joined.mp4"
    total = K.join_clips(clips, joined, a.transition if xf else "cut", xf, st)
    cur = joined

    caps = [c.strip() for c in a.captions.split("|")] if a.captions else []
    st.step(f"Captions ({sum(1 for c in caps if c)})" if caps else "Captions (none)")
    if any(caps):
        cues = []
        t = 0.0
        for i, (_, d) in enumerate(clips):
            start = t + (xf if i else 0) + 0.2
            end = t + d - xf - 0.1 if i < len(clips) - 1 else total - 0.2
            if i < len(caps) and caps[i]:
                cues.append((start, max(end, start + 0.5), caps[i]))
            t += d - xf
        lst = K.caption_track(cues, W, H, total, tmp / "caps", a.caption_style, "bottom", a.font, status=st)
        capped = tmp / "captioned.mp4"
        K.overlay_track(cur, lst, capped, st)
        cur = capped

    st.step("Music + export")
    if music:
        K.mix_music(cur, music, out, a.music_db, duck=False, keep_voice=False, fade_out=2.0, status=st)
    else:
        import shutil
        shutil.copyfile(cur, out)
    K.finish({"output": K.file_info(out), "slides": len(files), "seconds": round(total, 2), "frame": f"{W}x{H}",
              "elapsed_s": st.elapsed()})


if __name__ == "__main__":
    K.main_guard(main)
