/* global React, ReactDOM, PORTFOLIO, Sidebar,
   ViewHome, ViewAbout, ViewNow, ViewSkills, ViewExperience, ViewEducation,
   ViewGallery, ViewTravel, ViewContact, ViewProjects,
   TweaksPanel, useTweaks, TweakSection, TweakRadio, TweakToggle, TweakColor */

/* ============================================================================
   app.jsx — the root React application (the conductor).
   ----------------------------------------------------------------------------
   Loads LAST, after data.js, the Tweaks panel, ui.jsx, sidebar.jsx and every
   section file — so all of those are already on `window` when this renders.

   Responsibilities:
     1. ROUTING   — which page ("home" | "projects") and, on home, which
                    section file ("readme", "about", …) is showing.
     2. LAYOUT    — the two-column shell: <Sidebar> + <main>.
     3. THEME     — turn the Tweaks (dark / accent / density) into CSS variables
                    on <html>; every section reads those variables.
     4. EXTRAS    — the page-transition wash and the optional custom cursor.
     5. TWEAKS    — the control panel the user opens from the toolbar.

   The page content itself lives in src/sections/*.jsx. This file just wires
   them together.
   ============================================================================ */

// ── Tweak defaults — editable in place via the toolbar ────────────────────────
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "dark": false,
  "accent": "slate",
  "density": "regular",
  "cursor": false
}/*EDITMODE-END*/;

// Curated quiet accents — all low-chroma so they read well on light and dark.
const ACCENTS = {
  slate: { light: "oklch(0.52 0.04 240)", dark: "oklch(0.74 0.05 240)" },
  olive: { light: "oklch(0.52 0.05 110)", dark: "oklch(0.74 0.06 110)" },
  clay:  { light: "oklch(0.55 0.06 50)",  dark: "oklch(0.76 0.07 50)"  },
  plum:  { light: "oklch(0.48 0.05 320)", dark: "oklch(0.74 0.05 320)" },
};

// The single theme, in light and dark. (No more per-direction themes.)
const THEMES = {
  light: { ink: "#1c1a17", paper: "#f7f4ee", panel: "#ece8df", rule: "rgba(28,26,23,.14)",   muted: "rgba(28,26,23,.55)" },
  dark:  { ink: "#dad6cc", paper: "#191713", panel: "#221f19", rule: "rgba(218,214,204,.14)", muted: "rgba(218,214,204,.55)" },
};

// Map each home-page section id (used by the sidebar) to its component.
const SECTIONS = {
  readme:  ViewHome,
  about:   ViewAbout,
  now:     ViewNow,
  skills:  ViewSkills,
  exp:     ViewExperience,
  edu:     ViewEducation,
  gallery: ViewGallery,
  travel:  ViewTravel,
  contact: ViewContact,
};

// ── Page-transition wash ──────────────────────────────────────────────────────
// Slides a panel up over the page, runs `cb` (the actual page swap) while it's
// covered, then slides it away. See .px-wash in styles.css.
function useWash() {
  const ref = React.useRef(null);
  const run = React.useCallback((label, cb) => {
    const el = ref.current;
    if (!el) { cb && cb(); return; }
    el.querySelector(".lbl").textContent = label || "";
    el.classList.add("on");
    setTimeout(() => {
      cb && cb();
      el.classList.add("out");
      setTimeout(() => { el.classList.remove("on"); el.classList.remove("out"); }, 600);
    }, 420);
  }, []);
  return [ref, run];
}

// ── Custom cursor ─────────────────────────────────────────────────────────────
// Optional caret cursor: a blinking bar + a ring that lags behind and grows
// over links. Reads an element's data-cursor-label to show a little label.
function CustomCursor({ enabled }) {
  const dotRef = React.useRef(null);
  const ringRef = React.useRef(null);
  const labelRef = React.useRef(null);
  const stateRef = React.useRef({ x: -100, y: -100, rx: -100, ry: -100, label: "" });

  React.useEffect(() => {
    if (!enabled) { document.body.dataset.cursor = "off"; return; }
    document.body.dataset.cursor = "on";

    const onMove = (e) => {
      stateRef.current.x = e.clientX;
      stateRef.current.y = e.clientY;
      const el = e.target.closest("[data-cursor-label]");
      const lbl = el ? el.getAttribute("data-cursor-label") : "";
      if (lbl !== stateRef.current.label) {
        stateRef.current.label = lbl;
        if (labelRef.current) labelRef.current.textContent = lbl;
      }
      if (labelRef.current) labelRef.current.style.transform = `translate(${e.clientX + 18}px, ${e.clientY + 18}px)`;
      const isLink = !!e.target.closest("a, button, [data-hov]");
      if (ringRef.current) ringRef.current.dataset.hot = isLink ? "1" : "0";
    };
    const onLeave = () => { stateRef.current.x = -100; stateRef.current.y = -100; };
    window.addEventListener("mousemove", onMove);
    window.addEventListener("mouseout", onLeave);

    let raf = 0;
    const tick = () => {
      const s = stateRef.current;
      s.rx += (s.x - s.rx) * 0.18;
      s.ry += (s.y - s.ry) * 0.18;
      if (dotRef.current)  dotRef.current.style.transform  = `translate(${s.x}px, ${s.y}px)`;
      if (ringRef.current) ringRef.current.style.transform = `translate(${s.rx}px, ${s.ry}px)`;
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("mouseout", onLeave);
    };
  }, [enabled]);

  if (!enabled) return null;
  return (
    <>
      <style>{`@keyframes blink { 50% { opacity: 0; } }`}</style>
      {/* the caret bar */}
      <div ref={dotRef} className="cur" style={{
        width: 2, height: 18, marginLeft: -1, marginTop: -9,
        background: "currentColor", transform: "translate(-100px,-100px)",
      }} />
      <div ref={ringRef} className="cur cur-ring" style={{ transform: "translate(-100px,-100px)" }} />
      <div ref={labelRef} className="cur-label" />
    </>
  );
}

// ── Root App ──────────────────────────────────────────────────────────────────
function App() {
  const data = window.PORTFOLIO;
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [page, setPage] = React.useState("home");      // "home" | "projects"
  const [active, setActive] = React.useState("readme"); // which home section
  const [washRef, runWash] = useWash();

  // Push theme + accent + density into CSS variables on <html>.
  React.useEffect(() => {
    const mode = t.dark ? "dark" : "light";
    const tokens = THEMES[mode];
    const acc = ACCENTS[t.accent] || ACCENTS.slate;
    const root = document.documentElement;
    Object.entries(tokens).forEach(([k, v]) => root.style.setProperty(`--${k}`, v));
    root.style.setProperty("--accent", t.dark ? acc.dark : acc.light);
    root.style.setProperty("--density", t.density === "compact" ? "0.9" : t.density === "comfy" ? "1.15" : "1");
  }, [t.dark, t.accent, t.density]);

  // Scroll to top when the home section changes.
  React.useEffect(() => { if (page === "home") window.scrollTo({ top: 0 }); }, [active, page]);

  // Switch page with the wash transition.
  const navigate = React.useCallback((target) => {
    if (target === page) return;
    runWash(target === "projects" ? "→ projects" : "→ home", () => {
      setPage(target);
      window.scrollTo({ top: 0, behavior: "instant" });
    });
  }, [page, runWash]);

  // Open the projects page and scroll to one project's card.
  const goToProject = React.useCallback((id) => {
    const scroll = () => {
      const el = document.getElementById(`prj-${id}`);
      if (el) window.scrollTo({ top: el.getBoundingClientRect().top + window.scrollY - 24, behavior: "smooth" });
    };
    if (page === "projects") setTimeout(scroll, 50);
    else { navigate("projects"); setTimeout(scroll, 750); }
  }, [page, navigate]);

  // Resolve the current home-page section component.
  const Section = SECTIONS[active] || ViewHome;

  return (
    <div className="rm-root">
      <div className="rm-layout">
        <Sidebar data={data} active={active} setActive={setActive} page={page} navigate={navigate} />

        <main className="rm-main">
          {page === "projects"
            ? <ViewProjects data={data} navigate={navigate} />
            : <Section data={data} goToProject={goToProject} tweaks={t} />}
        </main>
      </div>

      {/* Overlays */}
      <CustomCursor enabled={!!t.cursor} />
      <div ref={washRef} className="px-wash"><span className="lbl">…</span></div>

      {/* Tweaks panel (opened from the toolbar) */}
      <TweaksPanel title="Tweaks">
        <TweakSection label="Theme">
          <TweakToggle label="Dark mode" value={t.dark} onChange={(v) => setTweak("dark", v)} />
          <TweakColor label="Accent" value={t.accent} options={["slate", "olive", "clay", "plum"]} onChange={(v) => setTweak("accent", v)} />
        </TweakSection>

        <TweakSection label="Reading">
          <TweakRadio label="Density" value={t.density} options={["compact", "regular", "comfy"]} onChange={(v) => setTweak("density", v)} />
        </TweakSection>

        <TweakSection label="Interaction">
          <TweakToggle label="Custom cursor" value={t.cursor} onChange={(v) => setTweak("cursor", v)} />
        </TweakSection>
      </TweaksPanel>
    </div>
  );
}

// ── TweakColor adapter ────────────────────────────────────────────────────────
// We store accents as names ("slate"), but TweakColor wants real swatches. This
// wrapper maps the names to little palettes and back, so the panel shows colour
// chips while our state stays human-readable.
const _OriginalTweakColor = window.TweakColor;
window.TweakColor = function TweakColor(props) {
  if (Array.isArray(props.options) && props.options.every((o) => typeof o === "string" && !o.startsWith("#"))) {
    const palettes = {
      slate: ["#5b6b80", "#1a1816", "#eeece6"],
      olive: ["#6a7045", "#1a1816", "#eeece6"],
      clay:  ["#8a5a3b", "#1a1816", "#eeece6"],
      plum:  ["#6b4f6e", "#1a1816", "#eeece6"],
    };
    const opts = props.options.map((k) => palettes[k] || ["#888"]);
    const cur  = palettes[props.value] || opts[0];
    return _OriginalTweakColor({
      ...props,
      options: opts,
      value: cur,
      onChange: (pal) => {
        const k = Object.keys(palettes).find((kk) => palettes[kk][0] === pal[0]) || props.value;
        props.onChange(k);
      },
    });
  }
  return _OriginalTweakColor(props);
};

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