/* shell.jsx — shared chrome for all pages (boot, nav, footer, tweaks, theming)
   Adapted for the live site:
     • default look = "arcade"
     • the design's topbar/statusbar is replaced by the site's existing nav
       style (★ Jakob Visic + Portfolio/Timeline/About), recoloured via theme
       tokens with the same scroll-aware light/dark contrast as before
     • adds a standalone Tweaks launcher (the panel needs a host message to open,
       which doesn't exist on a deployed site) */
const { useState: useStateS, useEffect: useEffectS, useRef: useRefS, useCallback: useCallbackS } = React;

// Lightweight local fallback — keeps tweak state in component memory (no panel needed).
function useLocalTweaks(defaults) {
  const [values, setValues] = useStateS(defaults);
  const setTweak = useCallbackS((keyOrEdits, val) => {
    const edits = typeof keyOrEdits === "object" && keyOrEdits !== null ? keyOrEdits : { [keyOrEdits]: val };
    setValues((prev) => ({ ...prev, ...edits }));
  }, []);
  return [values, setTweak];
}

const LOOK_META = {
  schedule: { label: "SCHEDULE", sys: "ZS-01 · BLUEPRINT" },
  catalog:  { label: "CATALOG",  sys: "ZS-02 · TECH MANUAL" },
  capsule:  { label: "CAPSULE",  sys: "ZS-03 · RISO POP" },
  arcade:   { label: "ARCADE",   sys: "ZS-04 · ARCADE" },
};
const BG_BY_LOOK = { schedule: "#FFFDF4", catalog: "#E8E7E1", capsule: "#F3EECC", arcade: "#F1F4F9" };

const SHELL_TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "look": "arcade",
  "grain": true,
  "grid": true,
  "halftone": true,
  "wildAnim": "random",
  "displayFont": "black"
}/*EDITMODE-END*/;

/* ---------------- BOOT OVERLAY ---------------- */
function Boot({ onDone }) {
  const [lines, setLines] = useStateS([]);
  const [pct, setPct] = useStateS(0);
  const [ready, setReady] = useStateS(false);
  const [closing, setClosing] = useStateS(false);
  const script = [
    "> VISIC DESIGNS OS v2.6",
    "> MOUNTING /JAKOB ......... OK",
    "> LOADING PROFILE ........ OK",
    "> DESIGN · CODE · SOUND .. OK",
  ];
  const finish = useCallbackS(() => { setClosing(true); setTimeout(onDone, 480); }, [onDone]);
  useEffectS(() => {
    let li = 0;
    const lineT = setInterval(() => { li++; setLines(script.slice(0, li)); if (li >= script.length) clearInterval(lineT); }, 230);
    const barT = setInterval(() => { setPct((p) => { const n = Math.min(100, p + 7 + Math.random() * 9); if (n >= 100) clearInterval(barT); return n; }); }, 90);
    const readyT = setTimeout(() => setReady(true), 1450);
    return () => { clearInterval(lineT); clearInterval(barT); clearTimeout(readyT); };
  }, [finish]);
  return (
    <div className={"boot" + (closing ? " done" : "")} onClick={ready ? finish : undefined} style={ready ? { cursor: "pointer" } : undefined}>
      <div className="boot-inner">
        <div className="boot-badge"><Badge>✕</Badge><span className="mono" style={{ color: "var(--ink-soft)" }}>VISIC DESIGNS · DESIGN LAB</span></div>
        <div className="boot-title">JAKOB<br/>VISIC.</div>
        <div className="boot-log">
          {lines.map((l, i) => (<div key={i}>{l.replace(" OK", " ")}<span className="ok">{l.endsWith("OK") ? "OK" : ""}</span></div>))}
        </div>
        <div className="boot-bar"><i style={{ width: pct + "%" }} /></div>
        {ready && (<div className="boot-press"><Chevron size={11} sw={3} /> {(typeof window !== "undefined" && window.matchMedia && window.matchMedia("(pointer: coarse)").matches) ? "Tap to start" : "Click to start"}</div>)}
      </div>
    </div>
  );
}

/* ---------------- NAV (kept in the site's existing style, recoloured) ---------------- */
function Nav({ active }) {
  // The landing hero is accent-coloured, so the nav starts light over it and
  // flips to ink once you scroll onto the light sections — same behaviour the
  // previous site had over its blue hero.
  const heroAccent = active === "home";
  const [light, setLight] = useStateS(heroAccent);
  useEffectS(() => {
    if (!heroAccent) { setLight(false); return; }
    const onScroll = () => setLight(window.scrollY < window.innerHeight * 0.62);
    onScroll();
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => window.removeEventListener("scroll", onScroll);
  }, [heroAccent]);

  const color = light ? "var(--on-accent)" : "var(--ink)";
  const items = [
    ["Portfolio", "portfolio.html", "portfolio"],
    ["Timeline", "timeline.html", "timeline"],
    ["About", "about.html", "about"],
  ];
  return (
    <div className="zt-nav" style={{
      position: "fixed", top: 0, left: 0, right: 0, zIndex: 50,
      display: "flex", alignItems: "center", justifyContent: "space-between",
      padding: "20px clamp(20px, 5vw, 80px)",
      fontFamily: '"IBM Plex Mono", monospace', fontSize: 11,
      letterSpacing: ".16em", textTransform: "uppercase",
      color, transition: "color .3s ease", pointerEvents: "auto",
    }}>
      <a href="index.html" style={{ color: "inherit", textDecoration: "none", fontWeight: 700, letterSpacing: ".2em" }}>★ Jakob Visic</a>
      <div style={{ display: "flex", gap: "clamp(18px, 2.4vw, 34px)" }}>
        {items.map(([label, href, key]) => (
          <a key={key} href={href} style={{
            color: active === key ? "var(--accent)" : "inherit",
            textDecoration: "none",
            opacity: active === key ? 1 : 0.72,
          }}>{label}</a>
        ))}
      </div>
    </div>
  );
}

/* ---------------- FOOTER ---------------- */
function Footer({ look }) {
  return (
    <footer className="footer">
      <div className="wrap">
        <div className="f-top">
          <div className="f-fields">
            <div><div className="k">Email</div><div className="v">jakob.visic@gmail.com</div></div>
            <div><div className="k">Based</div><div className="v">Toronto, Canada</div></div>
            <div><div className="k">LinkedIn</div><div className="v"><a href="https://www.linkedin.com/in/jakob-visic" target="_blank" rel="noopener noreferrer" style={{ color: "inherit", textDecoration: "none" }}>jakob-visic ↗</a></div></div>
          </div>
          <div className="brand" style={{ display: "flex", alignItems: "center", gap: 10 }}>
            <Badge>★</Badge><span className="mono" style={{ color: "var(--ink-soft)" }}>Jakob Visic</span>
          </div>
        </div>
        <div className="colophon">
          <span>© 2026 JAKOB VISIC — DESIGN · CODE · SOUND</span>
          <span>{LOOK_META[look].sys} · END OF DOCUMENT ◇</span>
        </div>
      </div>
    </footer>
  );
}

/* ---------------- SHELL WRAPPER ---------------- */
function Shell({ active, doc, children }) {
  const isLocal = typeof window !== "undefined" && (window.location.hostname === "127.0.0.1" || window.location.hostname === "localhost");
  const [t, setTweak] = useLocalTweaks(SHELL_TWEAK_DEFAULTS);
  const reduce = typeof window !== "undefined" && window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
  const alreadyBooted = typeof sessionStorage !== "undefined" && sessionStorage.getItem("jv_booted") === "1";
  // Boot intro always plays once per session (ignores prefers-reduced-motion by design).
  const [booting, setBooting] = useStateS(!alreadyBooted);
  const [booted, setBooted] = useStateS(alreadyBooted);
  const pageRef = useRefS(null);

  // hydrate look/texture choices from localStorage (carry between pages)
  useEffectS(() => {
    try { const s = JSON.parse(localStorage.getItem("jv_tweaks") || "null"); if (s) setTweak(s); } catch (e) {}
  }, []);
  useEffectS(() => {
    try { localStorage.setItem("jv_tweaks", JSON.stringify({ look: t.look, grain: t.grain, grid: t.grid, halftone: t.halftone, wildAnim: t.wildAnim, displayFont: t.displayFont })); } catch (e) {}
  }, [t.look, t.grain, t.grid, t.halftone, t.wildAnim, t.displayFont]);

  const onBootDone = useCallbackS(() => {
    try { sessionStorage.setItem("jv_booted", "1"); } catch (e) {}
    setBooting(false); requestAnimationFrame(() => setBooted(true));
  }, []);

  useEffectS(() => {
    if (booted) window.dispatchEvent(new CustomEvent("zt-ready"));
  }, [booted]);
  const replayBoot = () => { try { sessionStorage.removeItem("jv_booted"); } catch (e) {} setBooted(false); setBooting(true); };

  useEffectS(() => {
    if (reduce) return;
    const els = pageRef.current ? pageRef.current.querySelectorAll(".reveal") : [];
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => { if (e.isIntersecting) { e.target.classList.add("in"); io.unobserve(e.target); } });
    }, { threshold: 0.1, rootMargin: "0px 0px -6% 0px" });
    els.forEach((el) => io.observe(el));
    return () => io.disconnect();
  }, [reduce, booted, children]);

  // "WILD" — random variation on each hover (always) + on scroll-in (unless reduced-motion)
  useEffectS(() => {
    const POOL = ["wobble", "shake", "pop", "spin", "flip3d", "tumble", "swing", "punch", "unfold"];
    const wilds = pageRef.current ? pageRef.current.querySelectorAll(".wild") : [];
    if (!wilds.length) return;
    // give the word real 3D depth: clone it into stacked translateZ layers (front=bright, back=dark)
    const lerpHex = (a, b, k) => {
      const pa = [0, 2, 4].map((i) => parseInt(a.substr(i, 2), 16));
      const pb = [0, 2, 4].map((i) => parseInt(b.substr(i, 2), 16));
      return "#" + pa.map((v, i) => Math.round(v + (pb[i] - v) * k).toString(16).padStart(2, "0")).join("");
    };
    const extrude = (el) => {
      if (el.querySelector(".wild-layer")) return;   // already built (rebuild if React wiped it)
      const text = el.textContent.trim(), N = 20, DEPTH = 34, FRONT = "ffffff", DEEP = "0a1f33";
      const frag = document.createDocumentFragment();
      for (let i = N; i >= 1; i--) {
        const s = document.createElement("span");
        s.className = "wild-layer"; s.textContent = text; s.setAttribute("aria-hidden", "true");
        s.style.transform = "translateZ(" + (-(DEPTH * i / N)) + "px)";
        s.style.color = lerpHex(FRONT, DEEP, i / N);
        frag.appendChild(s);
      }
      el.insertBefore(frag, el.firstChild);
    };
    wilds.forEach((el) => extrude(el));
    const fire = (el) => {
      let next;
      if (t.wildAnim && t.wildAnim !== "random" && POOL.indexOf(t.wildAnim) !== -1) {
        next = t.wildAnim;                       // pinned to a specific variant
      } else {
        next = POOL[Math.floor(Math.random() * POOL.length)];
        if (POOL.length > 1 && next === el.dataset.wild) next = POOL[(POOL.indexOf(next) + 1) % POOL.length];
      }
      el.dataset.wild = next;
      el.classList.remove("wild-go"); void el.offsetWidth; el.classList.add("wild-go");
    };
    const onEnter = (ev) => fire(ev.currentTarget);
    wilds.forEach((el) => el.addEventListener("mouseenter", onEnter));
    // Scroll-in trigger runs regardless of prefers-reduced-motion — the wild
    // animation is an intentional signature effect the user wants everywhere.
    const io = new IntersectionObserver((entries) => {
      entries.forEach((e) => { if (e.isIntersecting) fire(e.target); else e.target.classList.remove("wild-go"); });
    }, { threshold: 0.45 });
    wilds.forEach((el) => io.observe(el));
    return () => { io.disconnect(); wilds.forEach((el) => el.removeEventListener("mouseenter", onEnter)); };
  }, [booted, children, t.wildAnim]);

  useEffectS(() => {
    document.documentElement.style.background = BG_BY_LOOK[t.look] || "#F1F4F9";
    document.body.style.background = BG_BY_LOOK[t.look] || "#F1F4F9";
  }, [t.look]);

  // Self-host the panel's open/close protocol (no editor host on the live/local site):
  // the launcher posts __activate_edit_mode; the panel's ✕ posts __edit_mode_dismissed,
  // which we echo back as __deactivate_edit_mode so it actually closes.
  useEffectS(() => {
    const onMsg = (e) => {
      if (e && e.data && e.data.type === "__edit_mode_dismissed") {
        window.postMessage({ type: "__deactivate_edit_mode" }, "*");
      }
    };
    window.addEventListener("message", onMsg);
    return () => window.removeEventListener("message", onMsg);
  }, []);

  return (
    <div ref={pageRef} className={"page" + (booted ? " booted" : "")} data-theme={t.look} data-font={t.displayFont} data-grain={t.grain ? "on" : "off"} data-grid={t.grid ? "on" : "off"}>
      <style>{`
        .zt-nav a {
          position: relative;
          transition: opacity .2s ease, color .2s ease, transform .18s ease;
          display: inline-block;
        }
        .zt-nav a::after {
          content: "";
          position: absolute;
          left: 0; bottom: -3px;
          width: 100%; height: 1.5px;
          background: currentColor;
          transform: scaleX(0);
          transform-origin: left center;
          transition: transform .22s ease;
        }
        .zt-nav a:hover { opacity: 1; transform: translateY(-1px); }
        .zt-nav a:hover::after { transform: scaleX(1); }
      `}</style>
      <div className="grain" />
      {booting && <Boot onDone={onBootDone} />}
      <Nav active={active} />
      <main style={active === "home" ? undefined : { paddingTop: "clamp(34px, 4.5vw, 52px)" }}>
        {typeof children === "function" ? children({ t, booted }) : children}
      </main>
      <Footer look={t.look} />
      {isLocal && typeof TweaksPanel !== "undefined" && (<>
        <button
          onClick={() => window.postMessage({ type: "__activate_edit_mode" }, "*")}
          style={{ position: "fixed", left: 16, bottom: 16, zIndex: 70,
            font: '700 11px/1 "IBM Plex Mono", monospace', letterSpacing: ".14em", textTransform: "uppercase",
            color: "var(--on-accent)", background: "var(--accent)", border: 0, borderRadius: 8,
            padding: "10px 14px", cursor: "pointer", boxShadow: "0 6px 18px rgba(0,0,0,.22)" }}
        >⚙ Tweaks</button>
        <TweaksPanel title="Tweaks">
          <TweakSection label="Look" />
          <TweakSelect label="Theme" value={t.look}
            options={[{ value: "arcade", label: "Arcade" }, { value: "schedule", label: "Schedule" }, { value: "catalog", label: "Catalog" }, { value: "capsule", label: "Capsule" }]}
            onChange={(v) => setTweak("look", v)} />
          <TweakSelect label="Display font" value={t.displayFont}
            options={[{ value: "black", label: "Archivo Black" }, { value: "wide", label: "Archivo Wide" }, { value: "anton", label: "Anton" }, { value: "grotesk", label: "Bricolage" }]}
            onChange={(v) => setTweak("displayFont", v)} />
          <TweakSection label="Texture" />
          <TweakToggle label="Grain" value={t.grain} onChange={(v) => setTweak("grain", v)} />
          <TweakToggle label="Grid" value={t.grid} onChange={(v) => setTweak("grid", v)} />
          <TweakToggle label="Halftone" value={t.halftone} onChange={(v) => setTweak("halftone", v)} />
          <TweakSection label="Wild animation" />
          <TweakSelect label="Variant" value={t.wildAnim}
            options={[{ value: "random", label: "Random (all)" }, { value: "wobble", label: "Wobble" }, { value: "shake", label: "Shake" }, { value: "pop", label: "Pop" }, { value: "spin", label: "Spin" }, { value: "flip3d", label: "Flip3D" }, { value: "tumble", label: "Tumble" }, { value: "swing", label: "Swing" }, { value: "punch", label: "Punch" }, { value: "unfold", label: "Unfold" }]}
            onChange={(v) => setTweak("wildAnim", v)} />
        </TweaksPanel>
      </>)}
    </div>
  );
}

Object.assign(window, { LOOK_META, Boot, Nav, Footer, Shell, SHELL_TWEAK_DEFAULTS });
