// Prototype shell for Spencer Russell's portfolio.
// Direction A.01 (Sage paper) is committed; this turns the page mockups into a
// real, clickable site: a tiny router swaps between the index, the four category
// pages (generic CategoryPage), and the subcategory project-index pages. Page
// transitions are exposed as Tweaks so a few navigation feels can be compared.
//
// Routes:
//   { page: 'index' }
//   { page: 'category',    cat }
//   { page: 'subcategory', cat, sub }   // material/rd is intercepted → JournalIndex
//   { page: 'article',     slug }       // an r+d journal post

const { useTweaks, TweaksPanel, TweakSection, TweakRadio, TweakSlider } = window;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "transition": "fade",
  "speed": 580,
  "easing": "linear"
}/*EDITMODE-END*/;

const EASINGS = {
  soft:   'cubic-bezier(.33,1,.68,1)',
  snappy: 'cubic-bezier(.16,1,.3,1)',
  linear: 'linear',
};

function routeKey(r) {
  return r.page + ':' + (r.cat || '') + ':' + (r.sub || '') + ':' + (r.open || '') + ':' + (r.slug || '');
}

// Page entrance. The element's NORMAL (committed) style is opacity:1 /
// transform:none — so it is always visible, with no dependency on a JS timer or
// the animation clock ticking. The entrance comes from CSS @starting-style (see
// .pgt rules in index.html): the from-values apply only for the first rendered
// frame after the keyed remount, and a CSS transition animates to rest. If
// @starting-style/transitions don't run (frozen capture clock, old engine), the
// element simply shows its visible resting state — never blank.
function PageTransition({ mode, speed, easing, children }) {
  const instant = !mode || mode === 'instant';
  const ease = EASINGS[easing] || EASINGS.soft;
  const style = instant ? undefined : { transitionDuration: speed + 'ms', transitionTimingFunction: ease };
  return (
    <div className="pgt" data-mode={instant ? 'instant' : mode} style={style}>
      {children}
    </div>
  );
}

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [route, setRoute] = React.useState({ page: 'index' });
  const [stack, setStack] = React.useState([]); // simple back-history

  const navigate = React.useCallback((next) => {
    setRoute((cur) => {
      if (routeKey(cur) === routeKey(next)) return cur;
      setStack((s) => [...s, cur]);
      return next;
    });
  }, []);

  // Expose navigate globally so detail views (e.g. a project's "made with"
  // cross-link) can route without threading the prop through every wrapper.
  React.useEffect(() => { window.siteNavigate = navigate; }, [navigate]);

  // Reset scroll on every navigation (real-site feel) without scrollIntoView.
  React.useEffect(() => {
    window.scrollTo(0, 0);
    if (document.scrollingElement) document.scrollingElement.scrollTop = 0;
  }, [route]);

  // Hidden access route: visiting <site>/#access (the un-linked slug) opens the
  // password prompt. On success we clear the hash and jump to the architecture
  // page so the now-unlocked photography is immediately visible.
  React.useEffect(() => {
    const PG = window.PhotoGate;
    if (!PG || !PG.isAccessHash) return;
    const clearHash = () => {
      try { history.replaceState(null, '', window.location.pathname + window.location.search); } catch (e) {}
    };
    const check = () => {
      if (!PG.isAccessHash()) return;
      if (PG.isUnlocked()) { clearHash(); navigate({ page: 'category', cat: 'architecture' }); return; }
      if (window.openPasswordPrompt) {
        window.openPasswordPrompt({ onUnlock: () => navigate({ page: 'category', cat: 'architecture' }) });
      }
      clearHash();
    };
    check();
    window.addEventListener('hashchange', check);
    return () => window.removeEventListener('hashchange', check);
  }, [navigate]);

  let view = null;
  if (route.page === 'index')
    view = <window.DirectionA_Landing navigate={navigate} />;
  else if (route.page === 'category')
    view = <window.CategoryPage catWord={route.cat} navigate={navigate} openTarget={route.open} />;
  else if (route.page === 'subcategory' && route.cat === 'material' && route.sub === 'rd')
    // The r+d journal sub renders as a blog (JournalIndex), not the project-card grid.
    view = <window.JournalIndex navigate={navigate} />;
  else if (route.page === 'subcategory')
    view = <window.SubcategoryPage catWord={route.cat} subId={route.sub} navigate={navigate} />;
  else if (route.page === 'article')
    view = <window.ArticlePage slug={route.slug} navigate={navigate} />;

  return (
    <React.Fragment>
      <PageTransition key={routeKey(route)} mode={t.transition} speed={t.speed} easing={t.easing}>
        {view}
      </PageTransition>

      {window.Lightbox ? <window.Lightbox /> : null}
      {window.PhotoGateModal ? <window.PhotoGateModal /> : null}
      {window.ContactModal ? <window.ContactModal /> : null}

      <TweaksPanel>
        <TweakSection label="Page transition" />
        <TweakRadio label="Style" value={t.transition}
          options={['instant', 'fade', 'slide', 'push']}
          onChange={(v) => setTweak('transition', v)} />
        <TweakSlider label="Speed" value={t.speed} min={120} max={900} step={20} unit="ms"
          onChange={(v) => setTweak('speed', v)} />
        <TweakRadio label="Easing" value={t.easing}
          options={['soft', 'snappy', 'linear']}
          onChange={(v) => setTweak('easing', v)} />
      </TweaksPanel>
    </React.Fragment>
  );
}

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