"""VidPuls – Startseiten-Showcase: Testvideos + animierte Seitenleisten."""

from __future__ import annotations

import json
import re
import shutil
import subprocess
from pathlib import Path

from jam.jampulse_templates import INDUSTRY_TEMPLATES

SITE_SHOWCASE = Path.home() / "Documents" / "VidPuls" / "showcase"

# Zusätzliche Stock-Suchbegriffe für schnelle 4K-Clips (9:16)
_STOCK_QUERY: dict[str, str] = {
    "handwerker": "craftsman workshop tools cinematic vertical",
    "restaurant": "restaurant chef cooking food vertical",
    "dating": "couple coffee date smile vertical",
    "coach": "business coach office success vertical",
    "immobilien": "modern apartment house keys vertical",
    "fitness": "gym workout motivation vertical",
    "ki_web": "web developer laptop coding vertical",
    "social_mentor": "smartphone social media creator vertical",
    "friseur": "hair salon stylist beauty vertical",
    "auto": "car mechanic garage service vertical",
}


def showcase_dir() -> Path:
    return SITE_SHOWCASE


def showcase_items() -> list[dict]:
    out: list[dict] = []
    for t in INDUSTRY_TEMPLATES[:10]:
        out.append(
            {
                "id": t["id"],
                "label": t["label"],
                "icon": t.get("icon", "🎬"),
                "prompt": t["prompt"],
                "stock_query": _STOCK_QUERY.get(t["id"], t["label"]),
            }
        )
    return out


def load_manifest() -> list[dict]:
    path = showcase_dir() / "manifest.json"
    if path.is_file():
        try:
            data = json.loads(path.read_text(encoding="utf-8"))
            if isinstance(data, list) and data:
                return data
        except (json.JSONDecodeError, OSError):
            pass
    return []


def save_manifest(items: list[dict]) -> Path:
    d = showcase_dir()
    d.mkdir(parents=True, exist_ok=True)
    path = d / "manifest.json"
    path.write_text(json.dumps(items, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
    return path


def _ffmpeg() -> str | None:
    return shutil.which("ffmpeg")


def _make_4k_loop(src: Path, dest: Path, *, duration: int = 10) -> bool:
    ff = _ffmpeg()
    if not ff or not src.is_file():
        return False
    dest.parent.mkdir(parents=True, exist_ok=True)
    vf = (
        "scale=2160:3840:force_original_aspect_ratio=increase,"
        "crop=2160:3840,"
        "eq=brightness=0.04:saturation=1.12:contrast=1.05,"
        "fps=30"
    )
    cmd = [
        ff,
        "-y",
        "-stream_loop",
        "2",
        "-i",
        str(src),
        "-t",
        str(duration),
        "-vf",
        vf,
        "-c:v",
        "libx264",
        "-preset",
        "fast",
        "-crf",
        "20",
        "-pix_fmt",
        "yuv420p",
        "-an",
        str(dest),
    ]
    try:
        subprocess.run(cmd, check=True, capture_output=True, timeout=180)
        return dest.is_file() and dest.stat().st_size > 8000
    except (subprocess.CalledProcessError, subprocess.TimeoutExpired, OSError):
        return False


def generate_showcase_clip(item: dict, *, duration: int = 10, full_ia1: bool = False) -> dict | None:
    """Ein Showcase-Clip: schnell (Stock→4K) oder voll (ia1minute 4K)."""
    sid = str(item["id"])
    out_dir = showcase_dir()
    out_dir.mkdir(parents=True, exist_ok=True)
    dest = out_dir / f"{sid}.mp4"

    if full_ia1:
        from jam.ia1minute import run_ia1minute

        work = out_dir / f"_work_{sid}"
        result = run_ia1minute(
            str(item["prompt"]),
            work_dir=work,
            hd=True,
            commercial=False,
            target_sec=60,
            resolution="4k",
            aspect_ratio="9:16",
        )
        if not result.get("ok"):
            return None
        src = Path(str(result.get("video") or ""))
        if not src.is_file():
            return None
        if not _make_4k_loop(src, dest, duration=duration):
            shutil.copy2(src, dest)
    else:
        from jam.tiktok_remix import download_stock_clip

        stock, _note = download_stock_clip(str(item.get("stock_query") or item["label"]), hd=True)
        if not stock:
            return None
        if not _make_4k_loop(stock, dest, duration=duration):
            return None

    return {
        "id": sid,
        "label": item["label"],
        "icon": item.get("icon", "🎬"),
        "prompt": item["prompt"],
        "file": f"showcase/{sid}.mp4",
        "resolution": "4K",
        "duration_sec": duration,
    }


def build_all_showcase(*, duration: int = 10, full_ia1: bool = False) -> list[dict]:
    manifest: list[dict] = []
    for item in showcase_items():
        row = generate_showcase_clip(item, duration=duration, full_ia1=full_ia1)
        if row:
            manifest.append(row)
    if manifest:
        save_manifest(manifest)
    return manifest


def _copy_file(src: Path, dst: Path) -> None:
    dst.parent.mkdir(parents=True, exist_ok=True)
    data = src.read_bytes()
    dst.write_bytes(data)


def sync_showcase_to_site(site_root: Path) -> int:
    """Kopiert showcase/ nach Site-Root. Returns Anzahl MP4s."""
    src = showcase_dir()
    dst = site_root / "showcase"
    dst.mkdir(parents=True, exist_ok=True)
    n = 0
    for mp4 in src.glob("*.mp4"):
        _copy_file(mp4, dst / mp4.name)
        n += 1
    manifest = src / "manifest.json"
    if manifest.is_file():
        _copy_file(manifest, dst / "manifest.json")
    return n


def ensure_showcase_built(*, min_count: int = 8, duration: int = 10) -> int:
    """Showcase-Videos erzeugen falls noch zu wenig vorhanden."""
    d = showcase_dir()
    d.mkdir(parents=True, exist_ok=True)
    n = len(list(d.glob("*.mp4")))
    if n >= min_count:
        return n
    built = build_all_showcase(duration=duration)
    return len(built) if built else n


def showcase_video_path(item_id: str) -> Path | None:
    fp = showcase_dir() / f"{item_id}.mp4"
    return fp if fp.is_file() else None


def demo_gallery_items() -> list[dict]:
    ensure_showcase_built(min_count=1)
    manifest = load_manifest()
    if not manifest:
        manifest = [
            {
                "id": t["id"],
                "label": t["label"],
                "icon": t.get("icon", "🎬"),
                "prompt": t["prompt"],
                "resolution": "4K",
            }
            for t in showcase_items()
        ]
    out: list[dict] = []
    for d in manifest:
        sid = str(d["id"])
        if not showcase_video_path(sid):
            continue
        out.append(
            {
                "id": f"demo-{sid}",
                "prompt": d.get("prompt", ""),
                "label": d.get("label", ""),
                "icon": d.get("icon", "🎬"),
                "status": "done",
                "demo": True,
                "resolution": d.get("resolution", "4K"),
                "video_url": f"/api/showcase/{sid}.mp4",
            }
        )
    return out


def gallery_api_payload(user_jobs: list[dict] | None = None) -> dict:
    demos = demo_gallery_items()
    mine: list[dict] = []
    for j in user_jobs or []:
        jid = str(j.get("id") or "")
        has_video = j.get("status") == "done" and bool(j.get("local_video"))
        mine.append(
            {
                "id": jid,
                "prompt": j.get("prompt", ""),
                "label": "",
                "icon": "🎬",
                "status": j.get("status", "?"),
                "demo": False,
                "resolution": j.get("resolution") or ("4K" if j.get("hd") else "1080p"),
                "duration_sec": j.get("duration_sec"),
                "video_url": f"/api/video/{jid}" if has_video else "",
            }
        )
    return {
        "ok": True,
        "demos": demos,
        "mine": mine,
        "total": len(demos) + sum(1 for m in mine if m.get("video_url")),
    }


def studio_gallery_css() -> str:
    return """
.gal-panel { overflow: hidden; }
.gal-sub { font-size: .85rem; color: var(--muted); margin: -.2rem 0 1rem; }
.gal-stage {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
  height: min(520px, 52vh);
  margin-bottom: 1.25rem;
  position: relative;
}
.gal-stage::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at center, rgba(168,85,247,.1), transparent 72%);
  pointer-events: none;
  z-index: 1;
}
.gal-col {
  overflow: hidden;
  border-radius: 14px;
  border: 1px solid var(--line);
  background: rgba(0,0,0,.28);
  mask-image: linear-gradient(to bottom, transparent, #000 5%, #000 95%, transparent);
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 5%, #000 95%, transparent);
}
.gal-track { display: flex; flex-direction: column; gap: .75rem; padding: .4rem; will-change: transform; }
.gal-col-l .gal-track { animation: gal-up 36s linear infinite; }
.gal-col-r .gal-track { animation: gal-down 42s linear infinite; }
@keyframes gal-up { from { transform: translateY(0); } to { transform: translateY(-50%); } }
@keyframes gal-down { from { transform: translateY(-50%); } to { transform: translateY(0); } }
.gal-card {
  position: relative;
  flex-shrink: 0;
  aspect-ratio: 9/16;
  border-radius: 12px;
  overflow: hidden;
  cursor: pointer;
  border: 1px solid rgba(255,255,255,.1);
  transition: border-color .2s, box-shadow .2s;
  background: #12101a;
}
.gal-col-l .gal-card:nth-child(odd) { transform: rotate(-1.5deg); }
.gal-col-l .gal-card:nth-child(even) { transform: rotate(1deg); }
.gal-col-r .gal-card:nth-child(odd) { transform: rotate(1.5deg); }
.gal-col-r .gal-card:nth-child(even) { transform: rotate(-1deg); }
.gal-card:hover {
  border-color: rgba(168,85,247,.55);
  box-shadow: 0 8px 28px rgba(168,85,247,.25);
  z-index: 2;
}
.gal-card video { width: 100%; height: 100%; object-fit: cover; display: block; pointer-events: none; }
.gal-ph {
  height: 100%; min-height: 140px;
  display: grid; place-items: center; font-size: 1.4rem;
  background: linear-gradient(160deg,#1a1030,#0a0810); color: var(--muted);
}
.gal-badge {
  position: absolute; top: .4rem; left: .4rem; z-index: 2;
  font-size: .58rem; font-weight: 700; letter-spacing: .04em;
  padding: .15rem .4rem; border-radius: 999px;
  background: rgba(52,211,153,.92); color: #041;
}
.gal-cap {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2;
  padding: 1.5rem .45rem .45rem;
  background: linear-gradient(transparent, rgba(5,5,8,.92));
  font-size: .62rem; line-height: 1.35; color: #e8e4ff;
  pointer-events: none;
}
.gal-cap strong { display: block; color: #fff; font-size: .68rem; margin-bottom: .12rem; }
.gal-mine-wrap { margin-top: .5rem; }
.gal-mine-title {
  font-size: .72rem; font-weight: 600; color: var(--muted);
  text-transform: uppercase; letter-spacing: .06em; margin-bottom: .55rem;
}
.gal-mine-scroll {
  overflow: hidden;
  mask-image: linear-gradient(to right, transparent, #000 4%, #000 96%, transparent);
  -webkit-mask-image: linear-gradient(to right, transparent, #000 4%, #000 96%, transparent);
}
.gal-mine-track {
  display: flex; gap: .65rem; width: max-content;
  animation: gal-marquee 55s linear infinite;
}
@keyframes gal-marquee {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}
.gal-mine-scroll .gal-card { width: 118px; animation: gal-float 4s ease-in-out infinite; }
.gal-mine-scroll .gal-card:nth-child(2n) { animation-delay: .8s; }
.gal-mine-scroll .gal-card:nth-child(3n) { animation-delay: 1.6s; }
@keyframes gal-float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-4px); }
}
@media (max-width: 720px) {
  .gal-stage { height: min(380px, 45vh); gap: .5rem; }
  .gal-cap { font-size: .55rem; }
}
@media (prefers-reduced-motion: reduce) {
  .gal-track, .gal-mine-track { animation: none !important; }
  .gal-mine-scroll .gal-card { animation: none !important; }
}
"""


def _esc(s: str) -> str:
    return (
        str(s)
        .replace("&", "&amp;")
        .replace("<", "&lt;")
        .replace(">", "&gt;")
        .replace('"', "&quot;")
    )


def showcase_css() -> str:
    return """
.showcase-wrap {
  position: relative;
  overflow: hidden;
  min-height: clamp(420px, 72vh, 680px);
  margin: 0 0 1rem;
}
.showcase-wrap::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse 55% 70% at 50% 45%, rgba(168,85,247,.14), transparent 70%);
  pointer-events: none;
  z-index: 1;
}
.showcase-col {
  position: absolute;
  top: 0;
  bottom: 0;
  width: clamp(108px, 16vw, 200px);
  overflow: hidden;
  z-index: 0;
  mask-image: linear-gradient(to bottom, transparent, #000 8%, #000 92%, transparent);
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 8%, #000 92%, transparent);
}
.showcase-col-left { left: 0; padding-left: .35rem; }
.showcase-col-right { right: 0; padding-right: .35rem; }
.showcase-track {
  display: flex;
  flex-direction: column;
  gap: .85rem;
  will-change: transform;
}
.showcase-col-left .showcase-track { animation: vp-scroll-up 38s linear infinite; }
.showcase-col-right .showcase-track { animation: vp-scroll-down 44s linear infinite; }
@keyframes vp-scroll-up {
  from { transform: translateY(0); }
  to { transform: translateY(-50%); }
}
@keyframes vp-scroll-down {
  from { transform: translateY(-50%); }
  to { transform: translateY(0); }
}
.showcase-card {
  position: relative;
  flex-shrink: 0;
  border-radius: 14px;
  overflow: hidden;
  aspect-ratio: 9/16;
  background: #12101a;
  border: 1px solid rgba(255,255,255,.12);
  box-shadow: 0 12px 40px rgba(0,0,0,.45);
  transform: translateZ(0);
}
.showcase-col-left .showcase-card:nth-child(odd) { transform: rotate(-2deg) scale(.98); }
.showcase-col-left .showcase-card:nth-child(even) { transform: rotate(1.5deg) scale(1.02); }
.showcase-col-right .showcase-card:nth-child(odd) { transform: rotate(2deg) scale(1.01); }
.showcase-col-right .showcase-card:nth-child(even) { transform: rotate(-1deg) scale(.97); }
.showcase-card video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.showcase-badge {
  position: absolute;
  top: .45rem;
  left: .45rem;
  font-size: .62rem;
  font-weight: 700;
  letter-spacing: .06em;
  padding: .2rem .45rem;
  border-radius: 999px;
  background: rgba(52,211,153,.92);
  color: #041;
}
.showcase-cap {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: 1.6rem .45rem .45rem;
  background: linear-gradient(transparent, rgba(5,5,8,.92));
  font-size: .58rem;
  line-height: 1.35;
  color: #e8e4ff;
}
.showcase-cap strong {
  display: block;
  font-size: .65rem;
  color: #fff;
  margin-bottom: .15rem;
}
.showcase-hero-inner {
  position: relative;
  z-index: 2;
  max-width: 720px;
  margin: 0 auto;
  padding: 3rem 1.25rem 2rem;
  text-align: center;
}
@media (max-width: 720px) {
  .showcase-col { width: 72px; opacity: .85; }
  .showcase-cap { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .showcase-track { animation: none !important; }
}
"""


def _card_html(item: dict, *, base: str = "") -> str:
    src = _esc(f"{base}{item.get('file', '')}")
    label = _esc(str(item.get("label", "")))
    icon = _esc(str(item.get("icon", "🎬")))
    prompt = _esc(str(item.get("prompt", ""))[:120])
    if len(str(item.get("prompt", ""))) > 120:
        prompt += "…"
    res = _esc(str(item.get("resolution", "4K")))
    return f"""<article class="showcase-card">
  <span class="showcase-badge">{res}</span>
  <video src="{src}" autoplay muted loop playsinline preload="metadata"></video>
  <div class="showcase-cap"><strong>{icon} {label}</strong>{prompt}</div>
</article>"""


def showcase_html(*, manifest: list[dict] | None = None, hero_inner: str) -> str:
    items = manifest or load_manifest()
    if not items:
        items = [
            {
                "id": t["id"],
                "label": t["label"],
                "icon": t.get("icon", "🎬"),
                "prompt": t["prompt"],
                "file": f"showcase/{t['id']}.mp4",
                "resolution": "4K",
            }
            for t in showcase_items()
        ]

    left_src = items[::2]
    right_src = items[1::2] or items
    if len(left_src) < 3:
        left_src = items * 2
    if len(right_src) < 3:
        right_src = items * 2

    def track(cards: list[dict]) -> str:
        doubled = cards + cards
        return "".join(_card_html(c) for c in doubled)

    return f"""<div class="showcase-wrap">
  <div class="showcase-col showcase-col-left" aria-hidden="true"><div class="showcase-track">{track(left_src)}</div></div>
  <div class="showcase-hero-inner">{hero_inner}</div>
  <div class="showcase-col showcase-col-right" aria-hidden="true"><div class="showcase-track">{track(right_src)}</div></div>
</div>"""
