/* global React, ReactDOM, Hero, StatsRibbon, FeaturesGrid, Showcase, CtaFinal, Foot, SignInGate, Aionios, AioniosBrush, Nav */
const { useState, useEffect, useCallback, useRef } = React;

async function landingApi(path, opts = {}) {
  const init = { credentials: 'include', ...opts };
  init.headers = { 'Content-Type': 'application/json', ...(opts.headers || {}) };
  if (init.body && typeof init.body !== 'string') init.body = JSON.stringify(init.body);
  const res = await fetch(path, init);
  let data = null;
  try { data = await res.json(); } catch {}
  if (!res.ok) {
    const err = new Error((data && data.error) || `http_${res.status}`);
    err.status = res.status;
    err.data = data;
    throw err;
  }
  return data;
}

function App() {
  const [authState, setAuthState] = useState('checking'); // 'checking' | 'guest' | 'authed'
  const [user, setUser] = useState(null);
  const [gateOpen, setGateOpen] = useState(false);
  const [welcome, setWelcome] = useState(null);

  // Aionios mode — purely additive overlay. landing → V2 is a brush
  // curtain (~2.9s); V2 → landing is instant.
  const [aionios, setAionios] = useState(false);
  const [transitioning, setTransitioning] = useState(false);
  // Warp curtain for V2 → dashboard. Paints a pastel sky bloom that
  // bridges the navigation so the user never sees the destination
  // page's auth-check splash. The destination page (dashboard.html)
  // repaints the same curtain inline in <head> via a sessionStorage
  // handshake, then peels it off once content is ready.
  const [warping, setWarping] = useState(false);
  const warpFillRef = useRef(null);
  const warpPctRef = useRef(null);
  const AIONIOS_SWAP_MS = 1100;   // page state flips during full-coverage hold
  const AIONIOS_TOTAL_MS = 2900;  // curtain peels fully off-screen
  const WARP_COVER_MS = 520;      // curtain reaches full coverage
  const WARP_LANDING_TARGET = 30; // % reached on landing side before nav

  // Hydrate Google Client ID for the gate
  useEffect(() => {
    let cancelled = false;
    landingApi('/api/config')
      .then(cfg => {
        if (cancelled) return;
        window.JOVANA_CONFIG = { ...(window.JOVANA_CONFIG || {}), ...cfg };
        window.dispatchEvent(new CustomEvent('jovana:config'));
      })
      .catch(() => {});
    return () => { cancelled = true; };
  }, []);

  // Auth check on mount. Signed-in visitors stay on the landing page —
  // they enter the app explicitly via the "Dashboard →" CTA.
  useEffect(() => {
    let cancelled = false;
    landingApi('/api/me')
      .then(data => {
        if (cancelled) return;
        if (data && data.user) {
          setUser(data.user);
          setAuthState('authed');
        } else {
          setAuthState('guest');
        }
      })
      .catch(() => { if (!cancelled) setAuthState('guest'); });
    return () => { cancelled = true; };
  }, []);

  // Reveal animations — re-observe each time the visible content tree mounts
  // (the design's useReveal helper observes once on first mount, which would
  // miss our content because it's hidden behind the auth check splash).
  useEffect(() => {
    if (authState === 'checking') return;
    const els = document.querySelectorAll('.reveal');
    if (els.length === 0) return;
    const io = new IntersectionObserver(
      (entries) => entries.forEach((e) => {
        if (e.isIntersecting) {
          e.target.classList.add('in');
          io.unobserve(e.target);
        }
      }),
      { threshold: 0.15 }
    );
    els.forEach((el) => io.observe(el));
    return () => io.disconnect();
  }, [authState]);

  const open = useCallback(() => setGateOpen(true), []);
  const close = useCallback(() => setGateOpen(false), []);
  const onSignedIn = useCallback((u) => {
    setWelcome((u && (u.email || u.name)) || 'GOOGLE_USER');
  }, []);

  const startToAionios = useCallback(() => {
    setTransitioning(true);
    document.body.style.overflow = 'hidden';
    setTimeout(() => setAionios(true), AIONIOS_SWAP_MS);
    setTimeout(() => {
      setTransitioning(false);
      document.body.style.overflow = '';
    }, AIONIOS_TOTAL_MS);
  }, []);
  const onAioniosToggle = useCallback(() => {
    if (transitioning) return;
    if (!aionios) startToAionios();
    else setAionios(false);   // V2 → landing is instant per spec
  }, [aionios, transitioning, startToAionios]);
  const onAioniosEnter = useCallback(() => {
    // Persist the mode preference so the dashboard loads in Aionios skin
    // regardless of how the user gets there (direct, post-SignInGate, etc.).
    try { window.localStorage.setItem('jvn_mode', 'aionios'); } catch (e) {}
    if (user) {
      // Signed-in: ride the warp curtain into the dashboard so there's
      // no flash of the destination page nor "Loading channel…" splash.
      // The destination page paints the same curtain inline in <head>.
      try { window.sessionStorage.setItem('jvn_warp', '1'); } catch (e) {}
      setWarping(true);
      document.body.style.overflow = 'hidden';
      window.setTimeout(() => {
        // Snapshot the current progress so the dashboard side can pick
        // up the rainbow bar where we left off (instead of jumping back
        // to 0%). If the ref is missing for any reason, fall back to
        // the design target so the bar at least stays monotonic.
        let cur = WARP_LANDING_TARGET;
        if (warpFillRef.current && warpFillRef.current.style.width) {
          const parsed = parseInt(warpFillRef.current.style.width, 10);
          if (!isNaN(parsed)) cur = parsed;
        }
        try { window.sessionStorage.setItem('jvn_warp_progress', String(cur)); } catch (e) {}
        window.location.assign('/dashboard.html?aionios=1');
      }, WARP_COVER_MS);
    } else {
      // Signed-out: keep them on landing — open the sign-in modal. No
      // navigation, so no curtain needed.
      setGateOpen(true);
    }
  }, [user]);

  // Drive the rainbow progress bar on the landing side from 0% to
  // WARP_LANDING_TARGET (~30%) over WARP_COVER_MS, then hold until
  // navigation fires. rAF + easeOutQuad — no CSS transition on width
  // so the bar tracks the timer exactly. The dashboard side reads the
  // final value from sessionStorage and continues toward 100%.
  useEffect(() => {
    if (!warping) return;
    let raf = 0;
    const start = performance.now();
    const tick = (now) => {
      const t = Math.min(1, (now - start) / WARP_COVER_MS);
      const eased = t * (2 - t);
      const pct = Math.round(eased * WARP_LANDING_TARGET);
      if (warpFillRef.current) warpFillRef.current.style.width = pct + '%';
      if (warpPctRef.current) warpPctRef.current.textContent = pct + '%';
      if (t < 1) raf = window.requestAnimationFrame(tick);
    };
    raf = window.requestAnimationFrame(tick);
    return () => { if (raf) window.cancelAnimationFrame(raf); };
  }, [warping]);

  // bfcache back-button restoration: when the user clicks the browser
  // back button from /dashboard.html, the landing page comes back with
  // its React state preserved — including warping=true and a pending
  // setTimeout that already fired (firing into the void). Without this
  // handler the user sees the curtain still up over the V2 dreamland,
  // because state is intact but the navigation is gone. Reset the
  // transient state so the previous V2 view is what they actually see.
  useEffect(() => {
    const onPageShow = (e) => {
      if (!e.persisted) return;
      setWarping(false);
      document.body.style.overflow = '';
      try {
        window.sessionStorage.removeItem('jvn_warp');
        window.sessionStorage.removeItem('jvn_warp_progress');
      } catch (err) {}
    };
    window.addEventListener('pageshow', onPageShow);
    return () => window.removeEventListener('pageshow', onPageShow);
  }, []);

  // Esc closes gate
  useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') close(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [close]);

  // Brief splash while /api/me is in flight so the page doesn't render the
  // signed-out nav for a frame before swapping to the signed-in one.
  if (authState === 'checking') {
    return (
      <div style={{
        position: 'fixed', inset: 0, background: 'var(--bg-0)',
        display: 'grid', placeItems: 'center', zIndex: 100,
      }}>
        <div style={{
          fontFamily: 'var(--ff-mono)', color: 'var(--ink-2)',
          fontSize: 11, letterSpacing: '.3em', textTransform: 'uppercase',
        }}>
          Establishing channel…
        </div>
      </div>
    );
  }

  // Read Aionios / AioniosBrush off `window` instead of as bare globals.
  // The scripts are loaded async with type="text/babel"; a stray 401 / CDN
  // hiccup would otherwise let a ReferenceError escape from JSX render and
  // unmount the whole tree to a blank page. Falling back to `null` lets the
  // landing page render normally and just hides the Aionios toggle.
  const AioniosComp = (typeof window !== 'undefined' && window.Aionios) || null;
  const AioniosBrushComp = (typeof window !== 'undefined' && window.AioniosBrush) || null;
  const onAioniosToggleSafe = AioniosComp ? onAioniosToggle : undefined;

  return (
    <>
      {!aionios && (
        <>
          <Nav user={user} onSignIn={open} onAioniosToggle={onAioniosToggleSafe} />
          <Hero user={user} onSignIn={open} />
          <StatsRibbon />
          <FeaturesGrid />
          <Showcase />
          <CtaFinal user={user} onSignIn={open} />
          <Foot user={user} onSignIn={open} />
        </>
      )}
      {(aionios || transitioning) && AioniosComp && (
        <div
          style={{ visibility: aionios ? 'visible' : 'hidden' }}
          aria-hidden={!aionios}
        >
          <AioniosComp onEnter={onAioniosEnter} onBack={onAioniosToggle} />
        </div>
      )}
      {transitioning && AioniosBrushComp && <AioniosBrushComp />}
      {warping && (
        <div className="aionios-warp is-entering" aria-hidden="true">
          <div className="warp-progress">
            <div className="warp-progress-label">Entering the Aionios</div>
            <div className="warp-progress-pct" ref={warpPctRef}>0%</div>
            <div className="warp-progress-bar">
              <div className="warp-progress-fill" ref={warpFillRef} style={{ width: '0%' }} />
            </div>
          </div>
        </div>
      )}
      {gateOpen && <SignInGate onClose={close} onSignedIn={onSignedIn} />}
      {welcome && (
        <div style={{
          position: 'fixed', bottom: 24, left: '50%',
          transform: 'translateX(-50%)',
          background: 'linear-gradient(135deg, var(--neon-cyan), var(--neon-violet))',
          color: '#0a0a23',
          padding: '12px 22px', borderRadius: 4,
          fontFamily: 'var(--ff-mono)', fontSize: 12,
          letterSpacing: '.18em', textTransform: 'uppercase',
          boxShadow: '0 0 30px rgba(91,231,255,0.5)',
          zIndex: 300, fontWeight: 700,
        }}>
          ✓ ACCESS GRANTED · {welcome}
        </div>
      )}
    </>
  );
}

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