// site-ui.jsx — shared building blocks for the public site.

// ── Live data: read the SAME store the CMS writes, update on edits ───────────
const SiteData = React.createContext(null);
function SiteDataProvider({ children }) {
  const [data, setData] = React.useState(() => window.loadFestivalData());
  React.useEffect(() => {
    const reload = () => setData(window.loadFestivalData());
    const onVis = () => {if (!document.hidden) reload();};
    window.addEventListener("storage", reload); // edits from the CMS in another tab
    window.addEventListener("focus", reload); // returning to this tab
    document.addEventListener("visibilitychange", onVis);
    return () => {
      window.removeEventListener("storage", reload);
      window.removeEventListener("focus", reload);
      document.removeEventListener("visibilitychange", onVis);
    };
  }, []);
  return <SiteData.Provider value={data}>{children}</SiteData.Provider>;
}
function useSite() {return React.useContext(SiteData);}

// published-only helpers — current edition only
function usePublishedFilms() {
  const d = useSite();
  const currentYear = d.festival?.year;
  return d.films.filter((f) => f.status === "published" && String(f.year) === String(currentYear));
}
function usePublishedNews() {
  const d = useSite();
  return [...d.news.filter((n) => n.status === "published")].sort((a, b) => (b.date || "").localeCompare(a.date || ""));
}

// ── Icons ─────────────────────────────────────────────────────────────────
const SIcon = ({ name, style }) => {
  const p = {
    arrowR: "M5 12h14M13 6l6 6-6 6",
    arrowL: "M19 12H5M11 18l-6-6 6-6",
    arrowUpR: "M7 17L17 7M9 7h8v8",
    play: "M8 5v14l11-7z",
    menu: "M3 6h18M3 12h18M3 18h18",
    x: "M6 6l12 12M18 6L6 18",
    pin: "M12 21s-7-6.4-7-11a7 7 0 0114 0c0 4.6-7 11-7 11zM12 12a2.5 2.5 0 100-5 2.5 2.5 0 000 5z",
    clock: "M12 22a10 10 0 100-20 10 10 0 000 20zM12 7v5l3 2",
    check: "M5 12l5 5L20 7",
    plus: "M12 5v14M5 12h14",
    cal: "M4 5h16v16H4zM4 9h16M8 3v4M16 3v4",
    ticket: "M3 8a2 2 0 012-2h14a2 2 0 012 2 2 2 0 000 4 2 2 0 00-2 2H5a2 2 0 01-2-2 2 2 0 000-4zM13 6v12"
  };
  return (
    <svg viewBox="0 0 24 24" fill={name === "play" ? "currentColor" : "none"} stroke={name === "play" ? "none" : "currentColor"}
    strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round" style={style}>
      <path d={p[name] || ""} />
    </svg>);

};

// ── Grain frame (image placeholder) ─────────────────────────────────────────
const DUOS = ["d-ink", "d-pink", "d-cyan", "d-purple", "d-yellow"];
function hashStr(s) {let h = 0;for (let i = 0; i < (s || "").length; i++) h = h * 31 + s.charCodeAt(i) >>> 0;return h;}

// auto-derive a thematic image URL from the frame's seed.
// - kind="portrait" → portrait service (pravatar)
// - default → seeded random photo service (picsum, real Unsplash backend)
function autoSrc(seed, kind, ratio) {
  if (!seed) return null;
  const slug = String(seed).toLowerCase().replace(/[^a-z0-9]+/g, "-").slice(0, 48) || "x";
  if (kind === "portrait") {
    return `https://i.pravatar.cc/600?u=${encodeURIComponent(slug)}`;
  }
  // pick dimensions by ratio
  let w = 1200,h = 800;
  if (ratio) {
    const parts = String(ratio).split("/").map(Number);
    if (parts.length === 2 && parts[0] && parts[1]) {
      const base = 1200;
      const scale = base / Math.max(parts[0], parts[1]);
      w = Math.round(parts[0] * scale);
      h = Math.round(parts[1] * scale);
    }
  }
  return `https://picsum.photos/seed/${encodeURIComponent(slug)}/${w}/${h}`;
}

function Frame({ seed = "", src, video, kind, label, corner, play, ratio, className = "", style = {}, duo }) {
  const resolvedSrc = src !== undefined ? src : autoSrc(seed, kind, ratio);
  // When a media asset is present, use a neutral dark backdrop so brand-colour
  // flashes don't peek through while the image loads.
  const variant = duo || (resolvedSrc || video ? "d-ink" : DUOS[hashStr(seed) % DUOS.length]);
  const [vidOn, setVidOn] = React.useState(false);
  const vidRef = React.useRef(null);
  const startVideo = () => {
    setVidOn(true);
    requestAnimationFrame(() => {vidRef.current?.play?.().catch(() => {});});
  };
  const showPlay = play || video;
  return (
    <div className={`frame ${variant} ${className}`} style={{ aspectRatio: ratio, ...style }}>
      {video ?
      <video ref={vidRef} className="frame-video" preload="metadata" controls={vidOn} playsInline
      src={video} poster={resolvedSrc || undefined}
      style={{ display: vidOn ? "block" : "none" }} /> :
      null}
      {!vidOn && resolvedSrc &&
      <img className="frame-img" src={resolvedSrc} alt={label || ""} loading="lazy" />
      }
      {showPlay && !vidOn &&
      <button className="play" type="button" onClick={video ? startVideo : undefined} aria-label="Play">
          <span><SIcon name="play" style={{ width: 22, height: 22, marginLeft: 3 }} /></span>
        </button>
      }
      {corner && !vidOn && <div className="frame-corner">{corner}</div>}
      {label && !vidOn && <div className="frame-label"><span style={{ width: 6, height: 6, background: "var(--accent-bright)" }} />{label}</div>}
    </div>);

}

// ── Navigation ──────────────────────────────────────────────────────────────
const BASE_NAV = [
  ["home", "Home"],
  ["about", "About"],
  ["news", "News"],
  ["programme", "Programme"],
  ["jury", "Jury & Team"],
  ["archive", "Archive"],
  ["contact", "Contact"],
];

function Nav({ route, navigate }) {
  const d = useSite();
  const [open, setOpen] = React.useState(false);
  const top = route.split("/")[1] || "home";
  const go = (to) => {setOpen(false);navigate(to);};
  const awardsLive = !!(d.festival && d.festival.awardsPage && d.festival.awardsPage.live);
  const navItems = awardsLive
    ? [...BASE_NAV.slice(0, 6), ["awards", "Awards"], BASE_NAV[6]]
    : BASE_NAV;
  return (
    <header className="nav">
      <div className="wrap nav-inner">
        <div className="brand" onClick={() => go("/")}>
          {d.festival.logoImage
            ? <img src={d.festival.logoImage} alt={d.festival.name} className="nav-logo-img" style={{ maxHeight: "52px", height: "auto", display: "block" }} />
            : <span className="mk">{d.festival.logoText}</span>
          }
          {d.festival.logoSubtitle && (
            <span className="ed">{d.festival.logoSubtitle}</span>
          )}
        </div>
        <div className={`nav-backdrop ${open ? "open" : ""}`} onClick={() => setOpen(false)} />
        <nav className={`nav-links ${open ? "open" : ""}`}>
          {navItems.map(([id, label]) =>
          <button key={id} className={`nav-link ${top === id ? "active" : ""}`} onClick={() => { go("/" + id); setOpen(false); }}>{label}</button>
          )}
          <button className={`nav-cta ${top === "submit" ? "active" : ""}`} onClick={() => go("/submit")}>Submissions</button>
        </nav>
        <button className="nav-burger" onClick={() => setOpen((o) => !o)} aria-label="Menu">
          <SIcon name={open ? "x" : "menu"} style={{ width: 22, height: 22 }} />
        </button>
      </div>
    </header>);

}

// ── Footer ──────────────────────────────────────────────────────────────────
// ── Accent colour injector ──────────────────────────────────────────────────
function ColorInjector() {
  const d = useSite();
  React.useEffect(() => {
    const c1 = d.festival.accentColor  || "#ff5c70";
    const c2 = d.festival.accentColor2 || "#784c95";
    let el = document.getElementById("__site-accent");
    if (!el) { el = document.createElement("style"); el.id = "__site-accent"; document.head.appendChild(el); }
    el.textContent = `:root { --accent: ${c1}; --accent-bright: ${c1}; --coral: ${c1}; --coral-bright: ${c1}; --accent2: ${c2}; }`;
  }, [d.festival.accentColor, d.festival.accentColor2]);
  return null;
}

function Footer({ navigate }) {
  const d = useSite();
  const columns = d.festival.footer?.columns || [];

  const handleLink = (link) => {
    if (link.type === "url") {
      if (link.url) window.open(link.url, "_blank", "noopener");
    } else {
      if (link.page) navigate(link.page);
    }
  };

  return (
    <footer className="footer">
      <div className="wrap">
        <div className="footer-top">
          <div>
            <div className="bigmark">{d.festival.name}</div>
            <div className="muted" style={{ marginTop: 16, fontSize: 14, lineHeight: 1.7, maxWidth: 320 }}>
              {(d.festival.footer?.address || "Liverpool John Moores University · MA Film\nRedmonds Building, Brownlow Hill, Liverpool L3 5UG").split("\n").map((line, i, arr) => (
                <React.Fragment key={i}>{line}{i < arr.length - 1 && <br />}</React.Fragment>
              ))}
              {d.festival.module && <><br /><br /><span style={{ color: "var(--paper)" }}>Module:</span> {d.festival.module}</>}
            </div>
            <div style={{ marginTop: 20 }}><span className="tag" style={{ borderColor: "var(--line-d)", color: "var(--muted-d)" }}>{d.festival.dates}</span></div>
          </div>
          {columns.map(col => (
            <div key={col.id} className="fcol">
              <h4>{col.title}</h4>
              {(col.links || []).map(link => (
                <a key={link.id}
                  onClick={() => handleLink(link)}
                  style={{ cursor: (link.url || link.page) ? "pointer" : "default" }}>
                  {link.label}
                </a>
              ))}
            </div>
          ))}
        </div>
        <div className="footer-bottom">
          <span>© {d.festival.year} {d.festival.name}</span>
          <span>{d.festival.edition} Edition · {d.festival.location}</span>
          <span>{d.festival.footer?.credit || "Site by the MA Editorial Collective"}</span>
        </div>
      </div>
    </footer>
  );
}

// ── Kicker row (section label with rule) ─────────────────────────────────────
function Kicker({ children, n, color }) {
  // Replace § with ▸ (right-facing triangle) globally
  const label = n ? String(n).replace(/§/g, "▸") : n;
  return (
    <div className="kicker-row">
      {label && <span className="label" style={{ color: color || "var(--coral)" }}>{label}</span>}
      <span className="eyebrow" style={color ? { color } : {}}>{children}</span>
      <span className="ln" />
    </div>);

}

// ── Page transition wrapper ───────────────────────────────────────────────
function Page({ children, k }) {
  return <main key={k} className="fade-in">{children}</main>;
}

// ── YouTubeThumb ─────────────────────────────────────────────────────────────
// Click-to-load YouTube embed. Shows thumbnail + play button initially; on
// click swaps to a plain youtube.com/embed iframe with autoplay. Origin is
// only passed when the page has a real http(s) origin (never "null"), since
// passing origin=null is what triggers YouTube's 153 embed error.
function YouTubeThumb({ id, title, style }) {
  const [playing, setPlaying] = React.useState(false);
  if (!id) return <Frame seed={"reel-" + title} ratio="16/9" style={style} />;

  if (playing) {
    const o = typeof window !== "undefined" && window.location && /^https?:/.test(window.location.origin) ? window.location.origin : "";
    const embedSrc = "https://www.youtube.com/embed/" + id +
    "?autoplay=1&rel=0&modestbranding=1&playsinline=1" + (
    o ? "&origin=" + encodeURIComponent(o) : "");
    return (
      <div style={{ position: "relative", aspectRatio: "16/9", background: "#000", overflow: "hidden", ...style }}>
        <iframe
          src={embedSrc}
          title={title || "YouTube video"}
          frameBorder="0"
          referrerPolicy="strict-origin-when-cross-origin"
          allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share; fullscreen"
          allowFullScreen
          style={{ position: "absolute", inset: 0, width: "100%", height: "100%", border: "none" }} />
        
      </div>);

  }

  return (
    <button
      type="button"
      onClick={() => setPlaying(true)}
      style={{ display: "block", position: "relative", overflow: "hidden", background: "#000", aspectRatio: "16/9", cursor: "pointer", border: "none", padding: 0, width: "100%", textAlign: "left", ...style }}
      title={title || "Play video"}
      aria-label={"Play " + (title || "video")}>
      
      <img
        src={"https://i.ytimg.com/vi/" + id + "/hqdefault.jpg"}
        alt={title || ""}
        style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }} />
      
      <div style={{ position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", background: "linear-gradient(180deg, rgba(0,0,0,0.05) 0%, rgba(0,0,0,0.4) 100%)" }}>
        <div style={{ background: "var(--accent)", display: "flex", alignItems: "center", justifyContent: "center", gap: "clamp(6px,1vw,10px)", padding: "clamp(10px,1.5vw,16px) clamp(14px,2.5vw,24px)", boxShadow: "0 8px 30px rgba(0,0,0,0.35)", transition: "background 0.15s" }}>
          <span style={{ fontFamily: "var(--mono)", fontSize: "clamp(10px,1.4vw,14px)", letterSpacing: "0.12em", textTransform: "uppercase", color: "#fff", fontWeight: 700 }}>Play</span>
          <SIcon name="play" style={{ width: "clamp(12px,1.8vw,18px)", height: "clamp(12px,1.8vw,18px)", color: "#fff", marginLeft: 2 }} />
        </div>
      </div>
    </button>);

}

// ── Lightbox (global image viewer) ────────────────────────────────────────
// Call openLightbox(images, startIndex). images = array of { url, caption } or
// plain url strings. With >1 image you get next/prev arrows + keyboard nav.
function openLightbox(images, startIndex = 0) {
  const norm = (images || [])
    .map(im => (typeof im === "string" ? { url: im } : im))
    .filter(im => im && im.url);
  if (!norm.length) return;
  window.dispatchEvent(new CustomEvent("lightbox:open", { detail: { images: norm, index: startIndex } }));
}

function Lightbox() {
  const [state, setState] = React.useState(null); // { images, index }

  React.useEffect(() => {
    const onOpen = (e) => setState({ images: e.detail.images, index: e.detail.index || 0 });
    window.addEventListener("lightbox:open", onOpen);
    return () => window.removeEventListener("lightbox:open", onOpen);
  }, []);

  React.useEffect(() => {
    if (!state) return;
    const onKey = (e) => {
      if (e.key === "Escape") setState(null);
      else if (e.key === "ArrowRight") setState(s => s && { ...s, index: (s.index + 1) % s.images.length });
      else if (e.key === "ArrowLeft")  setState(s => s && { ...s, index: (s.index - 1 + s.images.length) % s.images.length });
    };
    document.addEventListener("keydown", onKey);
    const prevOverflow = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    return () => { document.removeEventListener("keydown", onKey); document.body.style.overflow = prevOverflow; };
  }, [state]);

  if (!state) return null;
  const { images, index } = state;
  const cur = images[index];
  const multi = images.length > 1;
  const go = (dir) => setState(s => s && { ...s, index: (s.index + dir + s.images.length) % s.images.length });

  return (
    <div className="lightbox" onClick={() => setState(null)}>
      <button className="lightbox-close" onClick={() => setState(null)} aria-label="Close">✕</button>
      {multi &&
        <button className="lightbox-nav prev" onClick={(e) => { e.stopPropagation(); go(-1); }} aria-label="Previous">‹</button>}
      <figure className="lightbox-stage" onClick={(e) => e.stopPropagation()}>
        <img src={cur.url} alt={cur.caption || ""} className="lightbox-img" />
        {(cur.caption || multi) &&
          <figcaption className="lightbox-caption">
            {cur.caption && <span>{cur.caption}</span>}
            {multi && <span className="lightbox-count">{index + 1} / {images.length}</span>}
          </figcaption>}
      </figure>
      {multi &&
        <button className="lightbox-nav next" onClick={(e) => { e.stopPropagation(); go(1); }} aria-label="Next">›</button>}
    </div>
  );
}

Object.assign(window, {
  SiteDataProvider, useSite, usePublishedFilms, usePublishedNews,
  SIcon, Frame, Nav, Footer, ColorInjector, Kicker, Page, hashStr, YouTubeThumb,
  Lightbox, openLightbox
});