/* =========================================================================
   App root — theme / lang / direction state, routing, tweaks
   ========================================================================= */
const { useState: uS, useEffect: uE, useRef: uRef, useCallback: uCb } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "direction": "stele",
  "serif": "source",
  "accent": "青花 · cobalt",
  "motif": "calm"
}/*EDITMODE-END*/;

const ACCENT_HUE = { "青花 · cobalt": 209, "黛 · indigo": 232, "松石 · teal": 195, "绀 · deep": 222 };
const SERIF_STACK = {
  source: "'Source Serif 4', 'Noto Serif SC', Georgia, serif",
  newsreader: "'Newsreader', 'Noto Serif SC', Georgia, serif",
  garamond: "'EB Garamond', 'Noto Serif SC', Georgia, serif",
};

function usePersisted(key, init) {
  const [v, setV] = uS(() => {
    try { const s = localStorage.getItem(key); if (s !== null) return JSON.parse(s); } catch (e) {}
    return typeof init === "function" ? init() : init;
  });
  uE(() => { try { localStorage.setItem(key, JSON.stringify(v)); } catch (e) {} }, [key, v]);
  return [v, setV];
}

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [theme, setTheme] = usePersisted("zx-theme", "dark");
  const [lang, setLang] = usePersisted("zx-lang", "zh");
  const [route, setRoute] = uS({ view: "home" });
  const [scrolled, setScrolled] = uS(false);
  const pendingAnchor = uRef(null);
  const dir = t.direction;
  const ui = SITE.ui[lang];

  /* apply theme + direction + tweak tokens to <html> */
  uE(() => {
    const r = document.documentElement;
    r.setAttribute("data-theme", theme);
    r.setAttribute("data-dir", dir);
    r.setAttribute("data-motif", t.motif);
    r.style.setProperty("--accent-h", ACCENT_HUE[t.accent] ?? 209);
    r.style.setProperty("--font-serif", SERIF_STACK[t.serif] || SERIF_STACK.source);
  }, [theme, dir, t.motif, t.accent, t.serif]);

  /* scroll state: brand reveal + section reveal (single proven listener) */
  uE(() => {
    const onScroll = () => {
      setScrolled(window.scrollY > window.innerHeight * 0.5);
      const vh = window.innerHeight || 800;
      document.querySelectorAll(".rise:not(.in)").forEach((e) => {
        if (e.getBoundingClientRect().top < vh * 0.92) e.classList.add("in");
      });
    };
    onScroll();
    requestAnimationFrame(onScroll);
    const t = setTimeout(onScroll, 400);
    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    return () => {
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onScroll);
      clearTimeout(t);
    };
  }, [route.view, dir, lang]);

  /* navigation with optional anchor */
  const go = uCb((next, anchor) => {
    pendingAnchor.current = anchor || null;
    setRoute(next);
    if (next.view !== "home" || !anchor) window.scrollTo({ top: 0, behavior: "auto" });
  }, []);

  uE(() => {
    if (route.view === "home" && pendingAnchor.current) {
      const id = pendingAnchor.current; pendingAnchor.current = null;
      requestAnimationFrame(() => {
        const el = document.getElementById(id);
        if (el) window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - 80, behavior: "smooth" });
      });
    }
  }, [route]);

  const brandShown = (dir === "observatory") || scrolled || route.view !== "home";

  return (
    <React.Fragment>
      <Cosmos />

      {/* top bar */}
      <div className="topbar">
        <a className={"brand " + (brandShown ? "show" : "")} onClick={() => go({ view: "home" })} role="button" tabIndex="0"
           onKeyDown={(e) => e.key === "Enter" && go({ view: "home" })}>
          <img className="brand__mark" src="assets/emblem.png" alt="" />
          <span className="brand__name">
            <span style={{ fontFamily: "'Noto Serif SC', serif", fontWeight: 600 }}>{SITE.identity.cjk}</span>
            <small>{SITE.identity.romaji}</small>
          </span>
        </a>
        <div className="controls">
          <LangToggle lang={lang} onToggle={() => setLang(lang === "zh" ? "en" : "zh")} t={ui} />
          <ThemeToggle theme={theme} onToggle={() => setTheme(theme === "dark" ? "light" : "dark")} />
        </div>
      </div>

      {/* routed views */}
      <main id="root-main">
        {route.view === "home" && (
          <div className="home" key={"home-" + dir + "-" + lang}>
            <Hero dir={dir} lang={lang} ui={ui} identity={SITE.identity} />
            <div className="stack" style={{ paddingTop: "var(--s9)", paddingBottom: "var(--s8)" }}>
              <About lang={lang} />
              <Writing lang={lang} go={go} />
              <Lab lang={lang} go={go} />
            </div>
            <Footer lang={lang} />
          </div>
        )}
        {route.view === "reader" && <Reader id={route.id} lang={lang} go={go} />}
        {route.view === "module" && <ModuleView id={route.id} lang={lang} go={go} />}
      </main>

      {/* concept direction switch (prototype affordance) */}
      <div className="dir-dock" role="group" aria-label="Concept direction">
        <span className="dir-dock__label mono">{lang === "zh" ? "方向" : "Dir"}</span>
        <div className="seg">
          <button aria-pressed={dir === "stele"} onClick={() => setTweak("direction", "stele")}>{lang === "zh" ? "碑" : "Stele"}</button>
          <button aria-pressed={dir === "observatory"} onClick={() => setTweak("direction", "observatory")}>{lang === "zh" ? "观星台" : "Observ."}</button>
        </div>
      </div>

      {/* Tweaks */}
      <TweaksPanel title="Tweaks">
        <TweakSection label="方向 · Direction" />
        <TweakRadio label="Layout" value={t.direction} options={["stele", "observatory"]}
          onChange={(v) => setTweak("direction", v)} />
        <TweakSection label="字体 · Type" />
        <TweakSelect label="Serif (标题/正文)" value={t.serif}
          options={[{ value: "source", label: "Source Serif 4" }, { value: "newsreader", label: "Newsreader" }, { value: "garamond", label: "EB Garamond" }]}
          onChange={(v) => setTweak("serif", v)} />
        <TweakSection label="色彩 · Accent" />
        <TweakRadio label="青花主色" value={t.accent} options={Object.keys(ACCENT_HUE)}
          onChange={(v) => setTweak("accent", v)} />
        <TweakSection label="母题 · Motif" />
        <TweakRadio label="星空纹理" value={t.motif} options={["off", "calm", "lush"]}
          onChange={(v) => setTweak("motif", v)} />
      </TweaksPanel>
    </React.Fragment>
  );
}

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