// ============================================
// Shared UI components
// ============================================
const { useState, useEffect, useMemo, useRef } = React;

function ThemeToggle() {
  const [theme, setTheme] = useState(() => {
    try { return localStorage.getItem("cine-theme") || "dark"; }
    catch { return "dark"; }
  });

  useEffect(() => {
    document.documentElement.setAttribute("data-theme", theme);
    try { localStorage.setItem("cine-theme", theme); } catch {}
  }, [theme]);

  const isLight = theme === "light";
  return (
    <button
      type="button"
      className="theme-toggle"
      onClick={() => setTheme(isLight ? "dark" : "light")}
      aria-label={isLight ? "Switch to dark theme" : "Switch to light theme"}
      title={isLight ? "Dunkles Theme" : "Helles Theme"}
    >
      {isLight ? (
        /* Moon icon — click to go dark */
        <svg viewBox="0 0 20 20" width="16" height="16" aria-hidden="true">
          <path d="M15.5 12.5A7 7 0 0 1 7.5 4.5a1 1 0 0 0-1.4-1.1A8 8 0 1 0 16.6 13.9a1 1 0 0 0-1.1-1.4z"
            fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinejoin="round"/>
        </svg>
      ) : (
        /* Sun icon — click to go light */
        <svg viewBox="0 0 20 20" width="16" height="16" aria-hidden="true">
          <circle cx="10" cy="10" r="3.4" fill="none" stroke="currentColor" strokeWidth="1.4"/>
          <g stroke="currentColor" strokeWidth="1.4" strokeLinecap="round">
            <line x1="10" y1="2.5" x2="10" y2="4.5"/>
            <line x1="10" y1="15.5" x2="10" y2="17.5"/>
            <line x1="2.5" y1="10" x2="4.5" y2="10"/>
            <line x1="15.5" y1="10" x2="17.5" y2="10"/>
            <line x1="4.7" y1="4.7" x2="6.1" y2="6.1"/>
            <line x1="13.9" y1="13.9" x2="15.3" y2="15.3"/>
            <line x1="4.7" y1="15.3" x2="6.1" y2="13.9"/>
            <line x1="13.9" y1="6.1" x2="15.3" y2="4.7"/>
          </g>
        </svg>
      )}
    </button>
  );
}

function LanguageToggle() {
  const { lang, setLang } = useLang();
  const [open, setOpen] = useState(false);
  const rootRef = useRef(null);
  const current = LANGS.find((l) => l.code === lang) || LANGS[0];
  const others = LANGS.filter((l) => l.code !== lang);

  useEffect(() => {
    if (!open) return;
    const onDown = (e) => {
      if (rootRef.current && !rootRef.current.contains(e.target)) setOpen(false);
    };
    const onKey = (e) => { if (e.key === "Escape") setOpen(false); };
    document.addEventListener("pointerdown", onDown);
    document.addEventListener("keydown", onKey);
    return () => {
      document.removeEventListener("pointerdown", onDown);
      document.removeEventListener("keydown", onKey);
    };
  }, [open]);

  return (
    <div
      ref={rootRef}
      className={"lang-pill " + (open ? "open" : "")}
      role="group"
      aria-label="Language"
    >
      <button
        type="button"
        className="lang-pill-current"
        onClick={() => setOpen((v) => !v)}
        aria-haspopup="listbox"
        aria-expanded={open}
        aria-label={current.long}
        title={current.long}
      >
        <span>{current.label}</span>
        <svg
          className="lang-pill-caret"
          viewBox="0 0 12 8"
          aria-hidden="true"
          width="10"
          height="7"
        >
          <path d="M1 6 L6 1 L11 6" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" />
        </svg>
      </button>
      <ul className="lang-pill-list" role="listbox" aria-hidden={!open}>
        {others.map((l) => (
          <li key={l.code}>
            <button
              type="button"
              className="lang-pill-opt"
              onClick={() => { setLang(l.code); setOpen(false); }}
              aria-label={l.long}
              title={l.long}
              role="option"
              aria-selected={false}
              tabIndex={open ? 0 : -1}
            >
              {l.label}
            </button>
          </li>
        ))}
      </ul>
    </div>
  );
}

function Nav({ route, onNavigate }) {
  const { t, lang, setLang } = useLang();
  const [filmsOpen, setFilmsOpen] = useState(false);
  const [mobileOpen, setMobileOpen] = useState(false);
  const [mobileFilmsOpen, setMobileFilmsOpen] = useState(false);
  const closeTimer = useRef(null);

  const openFilmsMenu = () => {
    if (closeTimer.current) clearTimeout(closeTimer.current);
    setFilmsOpen(true);
  };
  const scheduleClose = () => {
    if (closeTimer.current) clearTimeout(closeTimer.current);
    closeTimer.current = setTimeout(() => setFilmsOpen(false), 450);
  };

  const activeKey = route.name === "film" ? "films" : route.name;

  // Close mobile menu on route change
  useEffect(() => {
    setMobileOpen(false);
    setMobileFilmsOpen(false);
  }, [route.name, route.id]);

  // Lock body scroll while mobile menu is open
  useEffect(() => {
    if (mobileOpen) {
      const prev = document.body.style.overflow;
      document.body.style.overflow = "hidden";
      return () => { document.body.style.overflow = prev; };
    }
  }, [mobileOpen]);

  // Header-Hoehe live in CSS-Variable --header-h schreiben.
  // Hero-Carousel und Film-Hero nutzen das fuer ihre margin-top/padding-top Tricks,
  // damit Hero den ganzen Viewport fuellt ohne schwarzen Streifen unten — egal
  // ob Header auf Desktop 72px oder auf Mobile 57px hoch ist.
  useEffect(() => {
    const header = document.querySelector("header.nav");
    if (!header) return;
    const sync = () => {
      const h = header.offsetHeight || 72;
      document.documentElement.style.setProperty("--header-h", h + "px");
    };
    sync();
    let ro;
    try {
      ro = new ResizeObserver(sync);
      ro.observe(header);
    } catch (_) {
      window.addEventListener("resize", sync);
    }
    window.addEventListener("orientationchange", sync);
    return () => {
      if (ro) ro.disconnect();
      window.removeEventListener("resize", sync);
      window.removeEventListener("orientationchange", sync);
    };
  }, []);

  const go = (r) => {
    setMobileOpen(false);
    setMobileFilmsOpen(false);
    onNavigate(r);
  };

  return (
    <header className={"nav " + (mobileOpen ? "nav-mobile-open" : "")}>
      <div className="shell nav-inner">
        <button className="brand" onClick={() => go({ name: "home" })}>

          <span>Cinephiles Films</span>
        </button>
        <nav className="nav-links">
          <button
            className={"nav-link " + (activeKey === "home" ? "active" : "")}
            onClick={() => onNavigate({ name: "home" })}
          >
            {t("nav.home")}
          </button>

          <div
            className="nav-films-wrap"
            onMouseEnter={openFilmsMenu}
            onMouseLeave={scheduleClose}
          >
            <button
              className={"nav-link " + (activeKey === "films" ? "active" : "")}
              onClick={() => onNavigate({ name: "films" })}
              aria-haspopup="true"
              aria-expanded={filmsOpen}
            >
              {t("nav.films")}
              <span className="nav-caret" aria-hidden="true">▾</span>
            </button>
            {filmsOpen && (
              <div
                className="films-dropdown"
                onMouseEnter={openFilmsMenu}
                onMouseLeave={scheduleClose}
              >
                <div className="films-dropdown-grid">
                  {FILMS_ORDERED.map((f) => (
                    <button
                      key={f.id}
                      className="films-dropdown-item"
                      onClick={() => {
                        setFilmsOpen(false);
                        onNavigate({ name: "film", id: f.id });
                      }}
                      style={{ "--film-accent": f.accent || "#f4f1ec" }}
                    >
                      <div className="films-dropdown-poster">
                        {f.poster ? (
                          <img src={f.poster} alt={`${f.title} poster`} />
                        ) : (
                          <div className="films-dropdown-poster-ph">
                            <span
                              className="films-dropdown-poster-accent"
                              style={{ background: f.accent || "var(--border-strong)" }}
                            />
                            <span className="films-dropdown-poster-label">
                              poster · {f.slug}
                            </span>
                          </div>
                        )}
                      </div>
                      <div className="films-dropdown-meta">
                        <div className="films-dropdown-title">{f.title}</div>
                        <div className="films-dropdown-sub">
                          {[f.year, translateGenres(f.genre, t)].filter(Boolean).join(" · ") ||
                            (f.status === "coming-soon" ? t("misc.comingSoonShort") : "")}
                        </div>
                      </div>
                    </button>
                  ))}
                </div>
              </div>
            )}
          </div>

          <button
            className={"nav-link " + (activeKey === "festivals" ? "active" : "")}
            onClick={() => onNavigate({ name: "festivals" })}
          >
            {t("nav.festivals")}
          </button>

          <button
            className={"nav-link " + (activeKey === "about" ? "active" : "")}
            onClick={() => onNavigate({ name: "about" })}
          >
            {t("nav.about")}
          </button>
        </nav>

        <div className="nav-right">
          <LanguageToggle />
          <ThemeToggle />
          <button
            className={"nav-burger " + (mobileOpen ? "is-open" : "")}
            onClick={() => setMobileOpen((v) => !v)}
            aria-label="Menu"
            aria-expanded={mobileOpen}
          >
            <span /><span /><span />
          </button>
        </div>
      </div>

      {mobileOpen && (
        <div className="nav-mobile" onClick={(e) => { if (e.target === e.currentTarget) setMobileOpen(false); }}>
          <div className="nav-mobile-inner">
            <button
              className={"nav-mobile-link " + (activeKey === "home" ? "active" : "")}
              onClick={() => go({ name: "home" })}
            >
              {t("nav.home")}
            </button>

            <div className="nav-mobile-group">
              <button
                className={"nav-mobile-link " + (activeKey === "films" ? "active" : "")}
                onClick={() => go({ name: "films" })}
              >
                {t("nav.films")}
              </button>
              <button
                className="nav-mobile-sub-toggle"
                onClick={() => setMobileFilmsOpen((v) => !v)}
                aria-expanded={mobileFilmsOpen}
                aria-label="Toggle films list"
              >
                <span className={"nav-mobile-caret " + (mobileFilmsOpen ? "open" : "")}>▾</span>
              </button>
            </div>
            {mobileFilmsOpen && (
              <ul className="nav-mobile-sublist">
                {FILMS_ORDERED.map((f) => (
                  <li key={f.id}>
                    <button
                      className="nav-mobile-sublink"
                      onClick={() => go({ name: "film", id: f.id })}
                      style={{ "--film-accent": f.accent || "#f4f1ec" }}
                    >
                      <span className="nav-mobile-sublink-title">{f.title}</span>
                      {f.year && <span className="nav-mobile-sublink-year">{f.year}</span>}
                    </button>
                  </li>
                ))}
              </ul>
            )}

            <button
              className={"nav-mobile-link " + (activeKey === "festivals" ? "active" : "")}
              onClick={() => go({ name: "festivals" })}
            >
              {t("nav.festivals")}
            </button>
            <button
              className={"nav-mobile-link " + (activeKey === "about" ? "active" : "")}
              onClick={() => go({ name: "about" })}
            >
              {t("nav.about")}
            </button>

            {/* Dezenter Sprach-Switcher im Mobil-Drawer */}
            <div className="nav-mobile-langs" role="group" aria-label={t("lang.label")}>
              <div className="nav-mobile-langs-label">{t("lang.label")}</div>
              <div className="nav-mobile-langs-list">
                {LANGS.map((l) => (
                  <button
                    key={l.code}
                    type="button"
                    className={"nav-mobile-lang " + (l.code === lang ? "active" : "")}
                    onClick={() => { setLang(l.code); setMobileOpen(false); }}
                    aria-label={l.long}
                    aria-current={l.code === lang ? "true" : undefined}
                    title={l.long}
                  >
                    {l.label}
                  </button>
                ))}
              </div>
            </div>
          </div>
        </div>
      )}
    </header>
  );
}

function legalUrls(lang) {
  if (lang === "de") return { imprint: "/impressum", privacy: "/datenschutz" };
  return { imprint: "/imprint", privacy: "/privacy" };
}

function Footer() {
  const { t, lang } = useLang();
  const u = legalUrls(lang);
  return (
    <footer className="footer">
      <div className="shell footer-inner">
        <div className="footer-col">
          <strong>Cinephiles Films</strong>
          {t("footer.production")}<br />
          Köln, Germany
        </div>
        <div className="footer-col">
          <strong>{t("footer.contact")}</strong>
          <a href="mailto:info@cinephiles.de" className="footer-link">info@cinephiles.de</a>
        </div>
        <div className="footer-col">
          <strong>{t("footer.legal")}</strong>
          <a href="#/festivals" className="footer-link">{t("footer.fa")}</a><br />
          <a href={u.imprint} className="footer-link">{t("footer.imprint")}</a><br />
          <a href={u.privacy} className="footer-link">{t("footer.privacy")}</a><br />
          {t("footer.rights")}
        </div>
      </div>
    </footer>
  );
}

// ============================================
// Asset slot primitives
//
// Each placeholder renders a visible, labeled box so Jonas can see
// exactly what file to drop in. When the matching `src` is filled,
// the real asset renders in place.
// ============================================

function Slot({ label, src, alt, aspect, className = "", style = {} }) {
  const filled = typeof src === "string" && src.trim().length > 0;
  return (
    <div
      className={`slot ${className} ${filled ? "slot-filled" : "slot-empty"}`}
      style={{ aspectRatio: aspect, ...style }}
    >
      {filled ? (
        <img src={src} alt={alt || label} className="slot-img" />
      ) : (
        <div className="slot-inner">
          <span className="slot-label">{label}</span>
        </div>
      )}
    </div>
  );
}

function PosterSlot({ film }) {
  return (
    <Slot
      label={`poster · ${film.slug}`}
      src={film.poster}
      alt={`${film.title} poster`}
      aspect="2 / 3"
      className="poster"
    />
  );
}

function HeroStillSlot({ film }) {
  return (
    <Slot
      label={`hero-still · ${film.slug}`}
      src={film.heroStill}
      alt={`${film.title} hero still`}
      className="hero-still-slot"
      style={{ position: "absolute", inset: 0, aspectRatio: "auto" }}
    />
  );
}

function StillsGrid({ film, count = 6, onOpen }) {
  const stills = film.stills || [];
  const slots = Array.from({ length: count }, (_, i) => stills[i] || "");
  return (
    <div className="stills-grid">
      {slots.map((src, i) => {
        const hasSrc = typeof src === "string" && src.trim().length > 0;
        const isClickable = hasSrc && typeof onOpen === "function";
        const filename = hasSrc ? `${film.slug}-still-${String(i + 1).padStart(2, "0")}.jpg` : "";
        return (
          <div
            key={i}
            className={"still-cell " + (isClickable ? "still-clickable" : "")}
            onClick={isClickable ? () => onOpen(i) : undefined}
            role={isClickable ? "button" : undefined}
            tabIndex={isClickable ? 0 : undefined}
            onKeyDown={isClickable ? (e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); onOpen(i); } } : undefined}
          >
            <Slot
              label={`still-${String(i + 1).padStart(2, "0")}`}
              src={src}
              alt={`${film.title} still ${i + 1}`}
              aspect="16 / 9"
              className="still"
            />
            {hasSrc && (
              <a
                className="still-dl"
                href={src}
                download={filename}
                onClick={(e) => e.stopPropagation()}
                aria-label={`Download still ${i + 1}`}
                title="Download"
              >
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
                  <path d="M12 4v12" />
                  <path d="M6 12l6 6 6-6" />
                  <path d="M5 20h14" />
                </svg>
              </a>
            )}
          </div>
        );
      })}
    </div>
  );
}

function LaurelsStrip({ film }) {
  const laurels = (film.laurels || []).filter(Boolean);
  if (laurels.length === 0) {
    // no real laurels yet — render 3 empty placeholder slots so the layout holds
    return (
      <div className="laurels-strip">
        {Array.from({ length: 3 }, (_, i) => (
          <Slot
            key={i}
            label={`laurel-${String(i + 1).padStart(2, "0")}`}
            src=""
            alt={`${film.title} laurel ${i + 1}`}
            aspect="3 / 2"
            className="laurel laurel-slot"
          />
        ))}
      </div>
    );
  }
  return (
    <div className="laurels-strip laurels-real">
      {laurels.map((l, i) => {
        const src = typeof l === "string" ? l : l.src;
        const caption = typeof l === "object" ? l.caption : "";
        const festival = typeof l === "object" ? l.festival : "";
        return (
          <div key={i} className="laurel-fig">
            <img
              src={src}
              alt={`${festival ? festival + " — " : ""}${caption || film.title}`}
              className="laurel-img"
              loading="lazy"
            />
          </div>
        );
      })}
    </div>
  );
}

function LogoStrip({ film, kind }) {
  const list = kind === "co" ? (film.coProducerLogos || []) : (film.funderLogos || []);
  const labelBase = kind === "co" ? "co-producer-logo" : "funder-logo";
  const min = kind === "co" ? 1 : 3;
  const slotCount = Math.max(list.length, min);
  const slots = Array.from({ length: slotCount }, (_, i) => list[i] || "");
  return (
    <div className="logo-strip">
      {slots.map((src, i) => (
        <Slot
          key={i}
          label={`${labelBase}-${String(i + 1).padStart(2, "0")}`}
          src={src}
          alt={`${film.title} ${labelBase} ${i + 1}`}
          aspect="3 / 2"
          className="logo-slot"
        />
      ))}
    </div>
  );
}

// ============================================
// Motion hooks
// ============================================
function useReveal(options = {}) {
  const ref = useRef(null);
  const { threshold = 0.15, once = true } = options;
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    if (typeof IntersectionObserver === "undefined") {
      el.classList.add("is-visible");
      return;
    }
    const io = new IntersectionObserver(
      (entries) => {
        entries.forEach((entry) => {
          if (entry.isIntersecting) {
            el.classList.add("is-visible");
            if (once) io.unobserve(el);
          } else if (!once) {
            el.classList.remove("is-visible");
          }
        });
      },
      { threshold, rootMargin: "0px 0px -40px 0px" }
    );
    io.observe(el);
    return () => io.disconnect();
  }, [threshold, once]);
  return ref;
}

function usePointerParallax(strength = 8) {
  const ref = useRef(null);
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const parent = el.parentElement;
    if (!parent) return;
    let raf = 0;
    let targetX = 0, targetY = 0, curX = 0, curY = 0;
    const onMove = (e) => {
      const rect = parent.getBoundingClientRect();
      const nx = (e.clientX - rect.left) / rect.width - 0.5;
      const ny = (e.clientY - rect.top) / rect.height - 0.5;
      targetX = nx * strength;
      targetY = ny * strength;
      if (!raf) tick();
    };
    const onLeave = () => {
      targetX = 0; targetY = 0;
      if (!raf) tick();
    };
    const tick = () => {
      curX += (targetX - curX) * 0.12;
      curY += (targetY - curY) * 0.12;
      el.style.setProperty("--px", curX.toFixed(2) + "px");
      el.style.setProperty("--py", curY.toFixed(2) + "px");
      if (Math.abs(targetX - curX) > 0.05 || Math.abs(targetY - curY) > 0.05) {
        raf = requestAnimationFrame(tick);
      } else {
        raf = 0;
      }
    };
    parent.addEventListener("pointermove", onMove);
    parent.addEventListener("pointerleave", onLeave);
    return () => {
      parent.removeEventListener("pointermove", onMove);
      parent.removeEventListener("pointerleave", onLeave);
      if (raf) cancelAnimationFrame(raf);
    };
  }, [strength]);
  return ref;
}

function Reveal({ as: Tag = "div", delay = 0, className = "", children, ...rest }) {
  const ref = useReveal();
  const delayClass = delay > 0 ? ` reveal-delay-${Math.min(3, delay)}` : "";
  return (
    <Tag ref={ref} className={`reveal${delayClass} ${className}`} {...rest}>
      {children}
    </Tag>
  );
}

function WordReveal({ text, className = "" }) {
  const ref = useReveal();
  const words = String(text || "").split(/\s+/).filter(Boolean);
  return (
    <span ref={ref} className={`word-reveal ${className}`}>
      {words.map((w, i) => (
        <span key={i} className="word" style={{ transitionDelay: `${i * 40}ms` }}>
          <span>{w}</span>
        </span>
      ))}
    </span>
  );
}

function BackLink({ onClick }) {
  const { t } = useLang();
  return (
    <button className="back-link" onClick={onClick}>
      {t("btn.backToFilms")}
    </button>
  );
}

Object.assign(window, {
  Nav,
  Footer,
  LanguageToggle,
  ThemeToggle,
  Slot,
  PosterSlot,
  HeroStillSlot,
  StillsGrid,
  LaurelsStrip,
  LogoStrip,
  BackLink,
  Reveal,
  WordReveal,
  useReveal,
  usePointerParallax,
  legalUrls,
});
