/* App shell: nav, tweaks, scroll behaviour, mount. */

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "direction": "Theater",
  "accent": "#3b6dff",
  "grain": true,
  "letterbox": true,
  "hud": true,
  "trueBlack": true,
  "typeface": "Helvetica"
}/*EDITMODE-END*/;

const ACCENT_OPTIONS = ["#3b6dff", "#ff5c35", "#c8ff2e", "#ededeb"];

function Nav({ active, scrolled, onShowreel }) {
  const link = (id, label) => (
    <a href={"#" + id} className={active === id ? "active" : ""}>{label}</a>
  );
  return (
    <nav className={"nav" + (scrolled ? " scrolled" : "")}>
      <a className="brand" href="#top"><span className="blip" /> Ibby Onitolo</a>
      <div className="links">
        {link("work", "Work")}
        {link("about", "About")}
        <a href="#contact" className={"nav-contact " + (active === "contact" ? "active" : "")}>Contact</a>
        <a href="#" className="cta" onClick={(e) => { e.preventDefault(); onShowreel(); }}>Showreel ↗</a>
      </div>
    </nav>
  );
}

const SECTION_LABELS = { top: "Showreel", work: "Selected Work", about: "About", contact: "Contact" };

function HUD({ section, caseIdx }) {
  const c = section === "work" ? CASES[Math.max(0, caseIdx)] : null;
  return (
    <div className="hud" aria-hidden="true">
      <span>{SECTION_LABELS[section] || "Showreel"}</span>
      <span className="hud-c">{c ? <React.Fragment><b>{c.title}</b> · {c.client}</React.Fragment> : "Director / Editor / Colourist"}</span>
      <span>{c ? c.id + " — 04" : "LDN · 2026"}</span>
    </div>
  );
}

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [reelOpen, setReelOpen] = React.useState(false);
  const [scrolled, setScrolled] = React.useState(false);
  const [active, setActive] = React.useState("top");
  const [caseIdx, setCaseIdx] = React.useState(0);
  const progressRef = React.useRef(null);
  const cursorRef = React.useRef(null);

  // light / dark mode (persisted)
  const [light, setLight] = React.useState(() => {
    try { return localStorage.getItem("ibby-light") === "1"; } catch (e) { return false; }
  });
  React.useEffect(() => {
    document.body.classList.toggle("light", light);
    try { localStorage.setItem("ibby-light", light ? "1" : "0"); } catch (e) {}
  }, [light]);

  // nav background + left-edge scroll-progress fill
  React.useEffect(() => {
    const onScroll = () => {
      setScrolled(window.scrollY > 40);
      if (progressRef.current) {
        const max = document.documentElement.scrollHeight - window.innerHeight;
        const p = max > 0 ? window.scrollY / max : 0;
        progressRef.current.style.height = (p * 100).toFixed(2) + "%";
      }
    };
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll, { passive: true });
    return () => { window.removeEventListener("scroll", onScroll); window.removeEventListener("resize", onScroll); };
  }, []);

  // custom circle cursor (fine pointers only)
  React.useEffect(() => {
    if (!window.matchMedia || !window.matchMedia("(pointer: fine)").matches) return;
    const dot = cursorRef.current;
    if (!dot) return;
    document.body.classList.add("cursor-on");
    let raf = null, x = 0, y = 0;
    const move = (e) => {
      x = e.clientX; y = e.clientY;
      if (!raf) raf = requestAnimationFrame(() => {
        raf = null;
        dot.style.transform = "translate(" + x + "px," + y + "px) translate(-50%,-50%)";
        dot.classList.add("on");
      });
    };
    const over = (e) => {
      const tgt = e.target;
      const hot = tgt && tgt.closest && tgt.closest('a, button, [role="button"], .play-btn, input, label, .client-chip');
      dot.classList.toggle("hot", !!hot);
    };
    const leave = () => dot.classList.remove("on");
    window.addEventListener("mousemove", move, { passive: true });
    document.addEventListener("mouseover", over, true);
    document.documentElement.addEventListener("mouseleave", leave);
    return () => {
      document.body.classList.remove("cursor-on");
      window.removeEventListener("mousemove", move);
      document.removeEventListener("mouseover", over, true);
      document.documentElement.removeEventListener("mouseleave", leave);
      if (raf) cancelAnimationFrame(raf);
    };
  }, []);

  // active section + reveal-on-scroll
  React.useEffect(() => {
    document.body.classList.add("reveal-ready");

    let ioFired = false;
    const revealIO = new IntersectionObserver(
      (entries) => {
        ioFired = true;
        entries.forEach((en) => { if (en.isIntersecting) en.target.classList.add("in"); });
      },
      { threshold: 0.12, rootMargin: "0px 0px -8% 0px" }
    );
    document.querySelectorAll(".reveal").forEach((el) => revealIO.observe(el));

    // Watchdog: in throttled/embedded contexts IntersectionObserver may never
    // deliver entries — if it hasn't fired shortly after mount, reveal everything
    // so the page is never left invisible. (Healthy browsers fire IO on the first
    // frame, so the entrance animation is unaffected.)
    const watchdog = setTimeout(() => {
      if (!ioFired) {
        document.querySelectorAll(".reveal").forEach((el) => el.classList.add("in"));
      }
    }, 700);

    const ids = ["top", "work", "about", "contact"];
    const sectionIO = new IntersectionObserver(
      (entries) => {
        entries.forEach((en) => { if (en.isIntersecting) setActive(en.target.id); });
      },
      { rootMargin: "-45% 0px -45% 0px" }
    );
    ids.forEach((id) => { const el = document.getElementById(id); if (el) sectionIO.observe(el); });

    const caseEls = Array.from(document.querySelectorAll(".case"));
    const caseIO = new IntersectionObserver(
      (entries) => {
        entries.forEach((en) => { if (en.isIntersecting) setCaseIdx(caseEls.indexOf(en.target)); });
      },
      { rootMargin: "-40% 0px -40% 0px" }
    );
    caseEls.forEach((el) => caseIO.observe(el));

    return () => { clearTimeout(watchdog); revealIO.disconnect(); sectionIO.disconnect(); caseIO.disconnect(); };
  }, []);

  // film grain lives on <body>
  React.useEffect(() => {
    document.body.classList.toggle("grain", !!t.grain);
  }, [t.grain]);

  const dirClass = "dir-" + String(t.direction || "Theater").toLowerCase();
  const isMono = t.accent === "#ededeb";

  return (
    <div
      className={[
        dirClass,
        t.letterbox ? "letterbox" : "",
        isMono ? "accent-mono" : "",
        t.hud ? "hud-on" : "",
        (t.trueBlack && !light) ? "true-black" : "",
        t.typeface === "Mono" ? "type-mono" : "",
      ].join(" ").trim()}
      style={{ "--accent": t.accent }}
    >
      <Nav active={active} scrolled={scrolled} onShowreel={() => setReelOpen(true)} />
      <Hero onPlay={() => setReelOpen(true)} />
      <main>
        <Work />
        <About />
        <Contact />
      </main>
      <Footer />
      <ReelModal open={reelOpen} onClose={() => setReelOpen(false)} />

      <button
        className={"theme-toggle" + (light ? " on" : "")}
        onClick={() => setLight((v) => !v)}
        aria-label="Toggle light mode"
        title="Toggle light / dark"
      >
        <span className="tt-ico" aria-hidden="true"></span>
      </button>

      <div className="scroll-progress" aria-hidden="true"><span className="fill" ref={progressRef}></span></div>
      <div className="cursor" ref={cursorRef} aria-hidden="true"></div>

      {t.hud && <HUD section={active} caseIdx={caseIdx} />}

      <TweaksPanel>
        <TweakSection label="Visual direction" />
        <TweakRadio
          label="Layout"
          value={t.direction}
          options={["Theater", "Index", "Marquee"]}
          onChange={(v) => setTweak("direction", v)}
        />
        <TweakSection label="Look" />
        <TweakColor
          label="Accent"
          value={t.accent}
          options={ACCENT_OPTIONS}
          onChange={(v) => setTweak("accent", v)}
        />
        <TweakRadio
          label="Display type"
          value={t.typeface}
          options={["Helvetica", "Mono"]}
          onChange={(v) => setTweak("typeface", v)}
        />
        <TweakSection label="Camera HUD" />
        <TweakToggle label="HUD overlay" value={t.hud} onChange={(v) => setTweak("hud", v)} />
        <TweakToggle label="True black" value={t.trueBlack} onChange={(v) => setTweak("trueBlack", v)} />
        <TweakToggle label="Film grain" value={t.grain} onChange={(v) => setTweak("grain", v)} />
        <TweakToggle label="Letterbox bars" value={t.letterbox} onChange={(v) => setTweak("letterbox", v)} />
      </TweaksPanel>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
