/* app.jsx — CS Tech operating-partner platform. Homepage hub routes into three
   tools (Validate · SWOT/Live · Price-It), each a strategist session over a
   shared living graph. Loads last, after every cst-*.jsx + frame + tweaks.
   Mobile-first, fixed 402×874 device, scaled to fit. */

const { useState, useEffect, useRef, useCallback } = React;
const C = window.CST;
const { FONT, INK, TEXT, HAZE, HAZE_DIM, HAIR, GLASS, AMBER, AMBER_TRIAD, OPERATING_OPTIONS, STATUS } = C;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "operating": ['#46DDC6', '#2BB6D9', '#ECEDF1'],
  "heroLayout": "graph",
  "toolDisplay": "cards",
  "swotLayout": "board",
  "priceLayout": "bars",
  "speed": 1,
  "density": 1,
  "reducedMotion": false,
  "plan": "free"
}/*EDITMODE-END*/;

// book-a-session screen (homepage CTA target)
function BookScreen({ active, accent, onExit }) {
  const [sent, setSent] = useState(false);
  useEffect(() => { if (!active) setSent(false); }, [active]);
  return (
    <StageCard active={active} dir="up" label="Book a session">
      <Scrim from={0.2} to={0.98} />
      <div style={{ position: 'relative', height: '100%', display: 'flex', flexDirection: 'column', justifyContent: 'flex-end', padding: '0 24px 28px', gap: 16 }}>
        <Eyebrow color={accent}>Work with CS Tech</Eyebrow>
        {!sent ? (
          <React.Fragment>
            <h2 style={{ margin: 0, fontFamily: FONT, fontWeight: 700, fontSize: 26, letterSpacing: -1, lineHeight: 1.15, color: TEXT }}>
              Book a working session.
            </h2>
            <p style={{ margin: 0, fontFamily: FONT, fontSize: 13, lineHeight: 1.55, color: HAZE }}>
              A CS Tech strategist sits with you, runs the tools live, and leaves you with decisions you can act on this week — not a slide deck.
            </p>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 9, marginTop: 4 }}>
              {['What you’re deciding', 'Your email'].map((ph) => (
                <input key={ph} placeholder={ph} style={{ width: '100%', boxSizing: 'border-box', background: GLASS, border: `1px solid ${HAIR}`, borderRadius: 11, padding: '13px 14px', color: TEXT, fontFamily: FONT, fontSize: 13.5, outline: 'none' }} />
              ))}
            </div>
            <PrimaryBtn accent={accent} onClick={() => setSent(true)}>Request a session →</PrimaryBtn>
            <button onClick={onExit} style={{ background: 'none', border: 'none', color: HAZE, fontFamily: FONT, fontSize: 12, cursor: 'pointer', padding: 4, alignSelf: 'center' }}>← Back to tools</button>
          </React.Fragment>
        ) : (
          <React.Fragment>
            <h2 style={{ margin: 0, fontFamily: FONT, fontWeight: 700, fontSize: 26, letterSpacing: -1, lineHeight: 1.15, color: TEXT }}>✓ We’ll be in touch.</h2>
            <p style={{ margin: 0, fontFamily: FONT, fontSize: 13, lineHeight: 1.55, color: HAZE }}>Expect a note within [1 business day] to find a time. Bring the decision that’s on your mind.</p>
            <PrimaryBtn accent={accent} onClick={onExit}>Back to tools</PrimaryBtn>
          </React.Fragment>
        )}
      </div>
    </StageCard>);
}

const FONT_TOOLS = ['validate', 'swot', 'price'];

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const canvasRef = useRef(null);
  const simRef = useRef(null);
  const [route, setRoute] = useState('home');
  const [shareSpec, setShareSpec] = useState(null);
  // shared "decision context" — the idea/business flows between tools (no login)
  const [decisionCtx, setDecisionCtxRaw] = useState(() => { try { return localStorage.getItem('cst_ctx') || ''; } catch (e) { return ''; } });
  const setDecisionCtx = useCallback((v) => { const s = (v || '').trim(); setDecisionCtxRaw(s); try { localStorage.setItem('cst_ctx', s); } catch (e) {} }, []);
  // richer cross-tool hints (e.g. the edge + score from an idea check)
  const [decisionMeta, setDecisionMetaRaw] = useState(() => { try { return JSON.parse(localStorage.getItem('cst_meta') || '{}'); } catch (e) { return {}; } });
  const setDecisionMeta = useCallback((m) => { const next = m || {}; setDecisionMetaRaw(next); try { localStorage.setItem('cst_meta', JSON.stringify(next)); } catch (e) {} }, []);
  const [usageKey, setUsageKey] = useState(0);   // bump to refresh the usage pill
  const refreshUsage = useCallback(() => setUsageKey((k) => k + 1), []);

  // ---- auth state ----
  const [user, setUser] = useState(null);
  const [profile, setProfile] = useState(null);
  const [showLogin, setShowLogin] = useState(false);
  const [loginIntent, setLoginIntent] = useState(null); // 'subscribe' | 'report'

  useEffect(() => {
    // Check session on load (e.g. returning from OAuth redirect)
    window.CST_AUTH.getSession().then((session) => {
      if (session) {
        setUser(session.user);
        window.CST_STATS.syncAuth(true);
        window.CST_AUTH.getProfile().then((p) => { if (p) setProfile(p); });
      }
      // Restore route after OAuth/Stripe redirect
      const saved = window.CST_AUTH.getRedirectRoute();
      if (saved && saved !== 'home') setRoute(saved);
    });
    // Listen for auth changes
    const { data: { subscription } } = window.CST_AUTH.onAuthStateChange((event, session) => {
      if (event === 'SIGNED_IN' && session) {
        setUser(session.user);
        window.CST_STATS.syncAuth(true);
        window.CST_AUTH.getProfile().then((p) => { if (p) setProfile(p); });
        setShowLogin(false);
        refreshUsage();
        // If they signed in from paywall and now have uses, go back to where they were
        const saved = window.CST_AUTH.getRedirectRoute();
        if (saved && saved !== 'home' && saved !== 'paywall') setRoute(saved);
      }
      if (event === 'SIGNED_OUT') { setUser(null); setProfile(null); window.CST_STATS.syncAuth(false); refreshUsage(); }
    });
    // Listen for auth-required events from child components (e.g. ReportUnlock)
    const onRequireAuth = (e) => {
      const intent = e.detail && e.detail.intent;
      setLoginIntent(intent || 'report');
      setShowLogin(true);
    };
    window.addEventListener('cst-require-auth', onRequireAuth);
    // Handle checkout return
    const params = new URLSearchParams(window.location.search);
    if (params.get('checkout') === 'success') {
      const type = params.get('type');
      if (type === 'subscription') {
        setTimeout(() => {
          window.CST_AUTH.getProfile().then((p) => { if (p) setProfile(p); });
        }, 2000);
      }
      window.history.replaceState({}, '', window.location.pathname);
    }
    return () => { subscription.unsubscribe(); window.removeEventListener('cst-require-auth', onRequireAuth); };
  }, []);

  // Gate: require login before payment
  const requireAuth = useCallback((intent) => {
    if (user) return true;
    setLoginIntent(intent);
    setShowLogin(true);
    return false;
  }, [user]);

  // per-tool accent triads
  const accentFor = useCallback((id) => {
    if (id === 'swot') return t.operating;
    if (id === 'price') return [STATUS.good, t.operating[0], '#ECEDF1'];
    return AMBER_TRIAD; // validate / home / book
  }, [t.operating]);
  // homepage wayfinding hue per tool
  const hueFor = (tool) => tool.id === 'validate' ? AMBER : tool.id === 'price' ? STATUS.good : t.operating[0];

  // ---- boot the shared graph sim ----
  useEffect(() => {
    const sim = new GraphSim(canvasRef.current);
    simRef.current = sim;
    window.__sim = sim;
    sim.resize();
    const prefersReduced = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    if (prefersReduced && !t.reducedMotion) setTweak('reducedMotion', true);
    sim.setTheme([AMBER, t.operating[0], STATUS.good]);
    sim.setParams({ speed: t.speed, density: t.density, reduced: t.reducedMotion || prefersReduced });
    sim.seedAmbient(26);
    sim.start();
    const onResize = () => sim.resize();
    window.addEventListener('resize', onResize);
    return () => { sim.stop(); window.removeEventListener('resize', onResize); };
  }, []);

  // recolor graph on route / accent change
  useEffect(() => {
    const s = simRef.current; if (!s) return;
    if (route === 'home' || route === 'book') s.setTheme([AMBER, t.operating[0], STATUS.good]);
    else s.setTheme(accentFor(route));
  }, [route, t.operating]);

  useEffect(() => {
    const s = simRef.current; if (!s) return;
    s.setParams({ speed: t.speed, density: t.density, reduced: t.reducedMotion });
  }, [t.speed, t.density, t.reducedMotion]);

  const goHome = useCallback(() => { setRoute('home'); refreshUsage(); const s = simRef.current; if (s) { s.seedAmbient(26); s.setMode('ambient'); s.setTheme([AMBER, t.operating[0], STATUS.good]); } }, [t.operating]);
  const openTool = useCallback((id) => {
    if (id === 'book') { setRoute('book'); return; }
    if (id === 'paywall') { setRoute('paywall'); return; }
    // freemium gate: 3 free decisions / 24h unless subscribed
    if (FONT_TOOLS.includes(id) && !window.CST_STATS.canRun()) { setRoute('paywall'); return; }
    setRoute(id);
  }, []);
  const subscribe = useCallback(async () => {
    if (!requireAuth('subscribe')) return;
    const url = await window.CST_AUTH.startCheckout('subscription');
    if (url) window.location.href = url;
    else { /* fallback: demo mode */ setTweak('plan', 'unlimited'); window.CST_STATS.setSubscribed(true); refreshUsage(); goHome(); }
  }, [goHome, requireAuth]);

  // sync the simulated plan (Tweaks) into the usage store
  useEffect(() => { window.CST_STATS.setSubscribed(t.plan === 'unlimited'); refreshUsage(); }, [t.plan]);
  // sync real profile plan into stats layer
  useEffect(() => { if (profile) { window.CST_STATS.syncPlan(profile.plan); refreshUsage(); } }, [profile]);

  // pointer parallax
  const onMove = (e) => {
    const s = simRef.current; if (!s || !canvasRef.current) return;
    const rect = canvasRef.current.getBoundingClientRect();
    s.pointer(e.clientX - rect.left, e.clientY - rect.top);
  };

  // Expose current route so cst-auth can save it before redirects
  useEffect(() => { window.__cst_current_route = route; }, [route]);

  const inTool = FONT_TOOLS.includes(route) || route === 'book' || route === 'paywall';

  return (
    <React.Fragment>
      <div className="cst-app">
        <canvas ref={canvasRef} className="cst-canvas" />
        <div className="cst-glow cst-glow-a" style={{ background: `radial-gradient(circle, ${(route === 'home' ? AMBER : accentFor(route)[1])}, transparent 70%)` }} />
        <div className="cst-glow cst-glow-b" style={{ background: `radial-gradient(circle, ${(route === 'home' ? t.operating[0] : accentFor(route)[0])}, transparent 70%)` }} />
        <div className="cst-vignette" />

        <div className="cst-frame" style={{ touchAction: 'none', userSelect: 'none' }} onPointerMove={onMove}>
          <TopBar onBack={inTool ? goHome : null} right={<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            <UsagePill refreshKey={usageKey} onUpgrade={() => openTool('paywall')} />
            {user ? <AvatarBtn user={user} subscribed={profile && profile.plan === 'unlimited'} />
                  : <SignInBtn onClick={() => { setLoginIntent(null); setShowLogin(true); }} />}
          </div>} />

          <HomeScreen active={route === 'home'} hueFor={hueFor} onPick={openTool}
            heroLayout={t.heroLayout} toolDisplay={t.toolDisplay}
            decisionCtx={decisionCtx} setDecisionCtx={setDecisionCtx} />
          <ValidateTool active={route === 'validate'} sim={simRef.current} accent={accentFor('validate')} onExit={goHome} onBook={() => openTool('book')} onShare={setShareSpec} onOpenTool={openTool} decisionCtx={decisionCtx} setDecisionCtx={setDecisionCtx} setDecisionMeta={setDecisionMeta} />
          <SwotTool active={route === 'swot'} sim={simRef.current} accent={accentFor('swot')} onExit={goHome} onBook={() => openTool('book')} onOpenTool={openTool} swotLayout={t.swotLayout} onShare={setShareSpec} decisionCtx={decisionCtx} setDecisionCtx={setDecisionCtx} decisionMeta={decisionMeta} />
          <PriceTool active={route === 'price'} sim={simRef.current} accent={accentFor('price')} onExit={goHome} onBook={() => openTool('book')} priceLayout={t.priceLayout} onShare={setShareSpec} onOpenTool={openTool} decisionCtx={decisionCtx} setDecisionCtx={setDecisionCtx} />
          <BookScreen active={route === 'book'} accent={AMBER} onExit={goHome} />
          <PaywallScreen active={route === 'paywall'} accent={AMBER} onSubscribe={subscribe} onBook={() => setRoute('book')} onExit={goHome}
            onSignIn={() => { setLoginIntent('more-uses'); setShowLogin(true); }} />
        </div>
      </div>

      {shareSpec && <ShareSheet spec={shareSpec} onClose={() => setShareSpec(null)} />}
      <LoginSheet open={showLogin} onClose={() => setShowLogin(false)} intent={loginIntent} />

      <TweaksPanel>
        <TweakSection label="Brand" />
        <TweakColor label="Operating accent" value={t.operating}
          options={[OPERATING_OPTIONS.Teal, OPERATING_OPTIONS.Blue, OPERATING_OPTIONS.Violet]}
          onChange={(v) => setTweak('operating', v)} />
        <TweakSection label="Homepage" />
        <TweakRadio label="Hero" value={t.heroLayout} options={[{ value: 'graph', label: 'Graph' }, { value: 'copy', label: 'Copy' }]} onChange={(v) => setTweak('heroLayout', v)} />
        <TweakRadio label="Tools shown as" value={t.toolDisplay} options={[{ value: 'cards', label: 'Cards' }, { value: 'list', label: 'List' }, { value: 'timeline', label: 'Timeline' }]} onChange={(v) => setTweak('toolDisplay', v)} />
        <TweakSection label="SWOT" />
        <TweakRadio label="Board layout" value={t.swotLayout} options={[{ value: 'board', label: 'Cards' }, { value: 'compact', label: 'Compact' }]} onChange={(v) => setTweak('swotLayout', v)} />
        <TweakSection label="Price-It" />
        <TweakRadio label="Results" value={t.priceLayout} options={[{ value: 'bars', label: 'Bars' }, { value: 'cards', label: 'Cards' }]} onChange={(v) => setTweak('priceLayout', v)} />
        <TweakSection label="Motion" />
        <TweakSlider label="Animation speed" value={t.speed} min={0.5} max={2} step={0.1} unit="×" onChange={(v) => setTweak('speed', v)} />
        <TweakSlider label="Graph density" value={t.density} min={0.6} max={1.8} step={0.1} unit="×" onChange={(v) => setTweak('density', v)} />
        <TweakToggle label="Reduced motion" value={t.reducedMotion} onChange={(v) => setTweak('reducedMotion', v)} />
        <TweakSection label="Access (demo)" />
        <TweakRadio label="Plan" value={t.plan} options={[{ value: 'free', label: 'Free' }, { value: 'unlimited', label: 'Unlimited' }]} onChange={(v) => setTweak('plan', v)} />
        <TweakButton label="Reset today’s free uses" secondary onClick={() => { try { localStorage.removeItem('cst_uses'); } catch (e) {} refreshUsage(); }} />
      </TweaksPanel>
    </React.Fragment>);
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
window.__APP_READY = true;
