// App shell: hash routing, tweaks state, layout

const { useState, useEffect } = React;

function useHashRoute() {
  const [route, setRoute] = useState(() => window.location.hash.replace(/^#/, "") || "/");
  useEffect(() => {
    const onHash = () => {
      setRoute(window.location.hash.replace(/^#/, "") || "/");
      window.scrollTo({ top: 0, behavior: "instant" });
    };
    window.addEventListener("hashchange", onHash);
    return () => window.removeEventListener("hashchange", onHash);
  }, []);
  const navigate = (path) => {
    if (path === route) return;
    window.location.hash = path;
  };
  return [route, navigate];
}

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "bgTone": "cream",
  "layout": "twoUp",
  "hoverStyle": "both",
  "typeScale": 1
}/*EDITMODE-END*/;

const BG_TONES = {
  cream:    { bg: "#EBE8DF", thumb: "#D8BE99", fg: "#E92D0F" },
  bone:     { bg: "rgb(244, 241, 234)", thumb: "rgb(214, 208, 195)", fg: "rgb(0,0,0)" },
  paper:    { bg: "rgb(250, 248, 244)", thumb: "rgb(220, 215, 205)", fg: "rgb(20,20,20)" },
  fog:      { bg: "rgb(228, 228, 226)", thumb: "rgb(198, 198, 196)", fg: "rgb(10,10,10)" },
  ink:      { bg: "rgb(20, 20, 20)",    thumb: "rgb(60, 58, 52)",    fg: "rgb(236,232,222)" },
  sage:     { bg: "rgb(228, 232, 222)", thumb: "rgb(198, 206, 188)", fg: "rgb(0,0,0)" },
};

function applyTone(tone) {
  const t = BG_TONES[tone] || BG_TONES.cream;
  document.documentElement.style.setProperty("--bg", t.bg);
  document.documentElement.style.setProperty("--thumb", t.thumb);
  document.documentElement.style.setProperty("--fg", t.fg);
}

function App() {
  const [route, navigate] = useHashRoute();
  const [tweaks, setTweak] = window.useTweaks(TWEAK_DEFAULTS);

  // apply tweaks live
  useEffect(() => { applyTone(tweaks.bgTone); }, [tweaks.bgTone]);
  useEffect(() => {
    document.documentElement.style.setProperty("--type-scale", tweaks.typeScale);
  }, [tweaks.typeScale]);
  useEffect(() => {
    document.documentElement.setAttribute("data-hover", tweaks.hoverStyle);
  }, [tweaks.hoverStyle]);

  // route parsing
  let view;
  const m = route.match(/^\/work\/([\w-]+)/);
  const isCase = !!m;
  useEffect(() => {
    document.documentElement.toggleAttribute("data-case", isCase);
  }, [isCase]);

  // Hide the header on scroll-down, reveal on scroll-up — only on index/about
  // (not case pages, which have their own locked layout).
  useEffect(() => {
    const header = document.querySelector(".site-header");
    if (!header) return;
    // Set transform synchronously — both CSS transitions and rAF are unreliable
    // in this preview environment, so the resting state must be applied directly.
    const show = () => { header.style.transform = "translateY(0)"; };
    const hide = () => { header.style.transform = "translateY(-100%)"; };

    if (isCase) { show(); return; }

    let lastY = window.scrollY;
    const onScroll = () => {
      const y = window.scrollY;
      if (y <= 80) show();                 // always show near top
      else if (y > lastY) hide();          // scrolling down
      else if (y < lastY) show();          // scrolling up
      lastY = y;
    };
    onScroll(); // sync on mount so the header never starts hidden
    window.addEventListener("scroll", onScroll, { passive: true });
    return () => {
      window.removeEventListener("scroll", onScroll);
      show();
    };
  }, [isCase, route]);

  // On case pages, redirect any wheel/touch scroll on the page to the media column,
  // so the gallery scrolls regardless of cursor position. DESKTOP ONLY — below
  // 1100px the layout is a single stacked column that scrolls as one document,
  // so hijacking touch there would break normal text scrolling.
  useEffect(() => {
    if (!isCase) return;
    const isDesktop = () => window.matchMedia("(min-width: 1101px)").matches;
    const getScroller = () => document.querySelector(".case-media-col");

    const onWheel = (e) => {
      if (!isDesktop()) return;
      const scroller = getScroller();
      if (!scroller) return;
      // If the wheel target is already inside the scroller, let native scroll happen.
      if (scroller.contains(e.target)) return;
      e.preventDefault();
      scroller.scrollBy({ top: e.deltaY, left: 0, behavior: "auto" });
    };

    let touchY = null;
    const onTouchStart = (e) => { touchY = e.touches[0]?.clientY ?? null; };
    const onTouchMove = (e) => {
      if (!isDesktop()) return;
      const scroller = getScroller();
      if (!scroller || touchY == null) return;
      if (scroller.contains(e.target)) return;
      const y = e.touches[0]?.clientY ?? touchY;
      const dy = touchY - y;
      touchY = y;
      e.preventDefault();
      scroller.scrollBy({ top: dy, left: 0, behavior: "auto" });
    };

    window.addEventListener("wheel", onWheel, { passive: false });
    window.addEventListener("touchstart", onTouchStart, { passive: true });
    window.addEventListener("touchmove", onTouchMove, { passive: false });
    return () => {
      window.removeEventListener("wheel", onWheel);
      window.removeEventListener("touchstart", onTouchStart);
      window.removeEventListener("touchmove", onTouchMove);
    };
  }, [isCase]);
  if (route === "/" || route === "") {
    view = <window.IndexView navigate={navigate} layout={tweaks.layout} />;
  } else if (m) {
    view = <window.CaseView id={m[1]} navigate={navigate} />;
  } else if (route.startsWith("/about")) {
    view = <window.AboutView navigate={navigate} />;
  } else {
    view = <window.IndexView navigate={navigate} layout={tweaks.layout} />;
  }

  return (
    <React.Fragment>
      <window.Header route={route} navigate={navigate} />
      <div key={route} className="route-fade fade-in">{view}</div>
      <window.Footer />

      <window.TweaksPanel title="Tweaks">
        <window.TweakSection label="Background tone" />
        <window.TweakSelect
          label="Tone"
          value={tweaks.bgTone}
          onChange={(v) => setTweak("bgTone", v)}
          options={[
            { value: "cream",  label: "Cream (Figma)" },
            { value: "bone",   label: "Bone" },
            { value: "paper",  label: "Paper" },
            { value: "fog",    label: "Fog" },
            { value: "sage",   label: "Sage" },
            { value: "ink",    label: "Ink (dark)" },
          ]}
        />

        <window.TweakSection label="Grid layout" />
        <window.TweakRadio
          label="Style"
          value={tweaks.layout}
          onChange={(v) => setTweak("layout", v)}
          options={[
            { value: "twoUp",      label: "2-up" },
            { value: "evenGrid",   label: "Even" },
            { value: "list",       label: "List" },
            { value: "asymmetric", label: "Asym." },
          ]}
        />

        <window.TweakSection label="Hover style" />
        <window.TweakRadio
          label="Effect"
          value={tweaks.hoverStyle}
          onChange={(v) => setTweak("hoverStyle", v)}
          options={[
            { value: "overlay",   label: "Overlay" },
            { value: "scale",     label: "Scale" },
            { value: "crossfade", label: "Diff." },
            { value: "caption",   label: "Caption" },
            { value: "both",      label: "Both" },
          ]}
        />

        <window.TweakSection label="Type scale" />
        <window.TweakSlider
          label="Multiplier"
          min={0.85} max={1.4} step={0.05}
          value={tweaks.typeScale}
          onChange={(v) => setTweak("typeScale", v)}
        />
      </window.TweaksPanel>
    </React.Fragment>
  );
}

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