/* Equiti SuperApp — Web port root.
   Hosts all four screens permanently mounted as opacity-driven layers.
   Cross-fades between tabs via CSS opacity transitions. */

/* Page transition timing comes from window.TRANSITION (utils/transition.js)
   so home tabs, the trade subapp, and the voice overlay all share one
   tempo. 350 / 80 / 750 ms with cubic ease-in for OUT, expo ease-out for IN. */
const OUT_DURATION = window.TRANSITION.OUT_DURATION;
const BLANK_MS = window.TRANSITION.BLANK_MS;
const IN_DURATION = window.TRANSITION.IN_DURATION;
const TABS = ['home', 'wallet', 'news', 'performance'];
const OUT_TRANSITION = window.TRANSITION.OUT_TRANSITION;
const IN_TRANSITION = window.TRANSITION.IN_TRANSITION;

const App = () => {
  const [activeTab, setActiveTab] = React.useState('home');
  /* voiceMode drives the hybrid voice/chat surface (5 states):
       'idle'      — FAB only at bottom-center, 76×76. Voice off.
       'decision'  — Replica of voiceMini's FAB anchor (FAB at
                     bottom-center, 110×110, static low-opacity glow
                     halo). Text-input pill transitions in on the
                     LEFT of the FAB; "or press here to talk" hint
                     transitions in on the RIGHT. App stays
                     navigable — no scrim, no panel. User commits
                     by tapping the text box (→ chat) or the FAB
                     (→ voice).
       'chat'      — Chat panel slides up from the very bottom
                     (cash-flow-sheet design language). FAB animates
                     to the bottom-RIGHT corner at 76×76. Chat input
                     bar at the bottom of the panel (iOS Messages
                     style, full-width pill with send arrow).
       'voice'     — Same panel as 'chat'. FAB lifts up to panel
                     centre at 110×110. Glow rings centred on FAB
                     pulse only when transcription is active. X
                     next to FAB. No chat input bar.
       'voiceMini' — Voice still running, panel collapsed. FAB at
                     original bottom-center anchor 110×110, X next
                     to it, glow around FAB. App navigable.
     Transitions:
       idle → decision           (tap FAB)
       decision → chat           (tap text box)
       decision / chat → voice   (tap FAB)
       voiceMini → voice         (tap FAB)
       chat → idle               (close glyph / scrim)
       voice → voiceMini         (close glyph / scrim)
       voice / voiceMini → idle  (tap X)                      */
  const [voiceMode, setVoiceMode] = React.useState('idle');
  const [equitiPlusOpen, setEquitiPlusOpen] = React.useState(false);
  /* cashFlowMode is null|'deposit'|'withdraw'|'transfer' — controls
     the CashFlowSheet visibility. Triggered globally via
     window.openCashFlow(mode) so PortfolioCard / wallet hero pills
     can open the sheet without prop-drilling. */
  const [cashFlowMode, setCashFlowMode] = React.useState(null);
  React.useEffect(() => {
    /* Voice tool 'open_cash_flow' passes an optional amount via the
       second arg; the cash-flow-sheet reads window.__cashFlowAmount
       at mount time and pre-fills its input. */
    window.openCashFlow = (mode, amount) => {
      window.__cashFlowAmount = (amount != null && isFinite(amount)) ? String(amount) : '';
      setCashFlowMode(mode);
    };
    return () => { delete window.openCashFlow; };
  }, []);
  /* Expose openVoice so the cash-flow notification's "Talk to
     Equiti+" button can pop the assistant. Lands in 'decision'
     so the user immediately sees the type-vs-talk choice rather
     than landing inside an empty chat panel. */
  React.useEffect(() => {
    window.openVoice = () => setVoiceMode('decision');
    return () => { delete window.openVoice; };
  }, []);
  /* Top-level route — 'home' shows the 4-tab home experience;
     'leveraged-trade' swaps in the LeveragedTradeShell sub-app. Both
     route layers are PERMANENTLY MOUNTED with opacity-driven visibility
     so the cross-fade between them is symmetric — same tempo as tab
     swaps within either route. */
  const [route, setRoute] = React.useState('home');
  const routeRef = React.useRef('home');
  const [routeOpacities, setRouteOpacities] = React.useState({ home: 1, 'leveraged-trade': 0 });
  const [routeTransitions, setRouteTransitions] = React.useState({ home: '', 'leveraged-trade': '' });

  const tabRef = React.useRef('home');

  /* One opacity per tab. Home starts at 1 (visible on cold mount),
     others start at 0 (mounted but transparent). */
  const [opacities, setOpacities] = React.useState({ home: 1, wallet: 0, news: 0, performance: 0 });
  const [transitions, setTransitions] = React.useState({ home: '', wallet: '', news: '', performance: '' });

  const handleTabChange = React.useCallback((next) => {
    if (next === tabRef.current) return;
    const prev = tabRef.current;
    /* Update active immediately so the BottomNav pill springs in parallel
       with the page OUT phase (was previously gated behind the 430ms
       OUT+BLANK timeout, which made the pill feel like it was waiting
       for the page to leave). */
    tabRef.current = next;
    setActiveTab(next);
    setTransitions((t) => ({ ...t, [prev]: OUT_TRANSITION }));
    setOpacities((o) => ({ ...o, [prev]: 0 }));
    setTimeout(() => {
      setTransitions((t) => ({ ...t, [next]: IN_TRANSITION }));
      setOpacities((o) => ({ ...o, [next]: 1 }));
    }, OUT_DURATION + BLANK_MS);
  }, []);

  /* Route swap — same OUT/BLANK/IN sequence as tab swaps so the home
     ↔ leveraged-trade transition feels identical to an inner tab change. */
  const handleRouteChange = React.useCallback((next) => {
    if (next === routeRef.current) return;
    const prev = routeRef.current;
    setRouteTransitions((t) => ({ ...t, [prev]: OUT_TRANSITION }));
    setRouteOpacities((o) => ({ ...o, [prev]: 0 }));
    setTimeout(() => {
      routeRef.current = next;
      setRoute(next);
      setRouteTransitions((t) => ({ ...t, [next]: IN_TRANSITION }));
      setRouteOpacities((o) => ({ ...o, [next]: 1 }));
    }, OUT_DURATION + BLANK_MS);
  }, []);

  /* Expose tab state + navigation to window.equiti for the voice agent's
     get_current_screen + navigate client tools (see voice-overlay.jsx).
     Re-binds on every activeTab change so the getActiveTab closure always
     returns the current value. */
  React.useEffect(() => {
    window.equiti = window.equiti || {};
    window.equiti.getActiveTab = () => activeTab;
    window.equiti.navigateTo = handleTabChange;
    window.equiti.getRoute = () => routeRef.current;
    window.equiti.openLeveragedTrade = () => handleRouteChange('leveraged-trade');
    window.equiti.closeLeveragedTrade = () => handleRouteChange('home');
  }, [activeTab, handleTabChange, handleRouteChange]);

  return (
    <div className="app-frame" style={{
      /* Frameless 390×844 viewport — same logical dimensions as the RN
         iOS device. On mobile (≤600px viewport) the .app-frame CSS in
         Equiti SuperApp.html overrides width/height/margin/border-radius
         to fill the viewport (100vw × 100dvh, no margin auto). */
      position: 'relative',
      width: '390px',
      height: '844px',
      margin: '0 auto',
      overflow: 'hidden',
      backgroundColor: colors.bg,
    }}>
      {/* Home route layer — always mounted, opacity-driven.
         `tab-pane-inactive` (defined in the html <style>) recursively
         disables pointer events on this layer's descendants when the
         layer isn't the active route. Plain inline pointer-events:none
         on the wrapper is not enough — descendant buttons keep their
         default `auto` and would otherwise steal the cursor and wheel
         events from whatever's actually visible. */}
      <div
        className={route === 'home' ? '' : 'tab-pane-inactive'}
        style={{
          position: 'absolute',
          inset: 0,
          opacity: routeOpacities.home,
          transition: routeTransitions.home,
        }}
      >
        {TABS.map((name) => (
          <div
            key={name}
            className={route === 'home' && activeTab === name ? '' : 'tab-pane-inactive'}
            style={{
              position: 'absolute', top: 0, left: 0, right: 0, bottom: 0,
              opacity: opacities[name],
              transition: transitions[name],
            }}
          >
            {name === 'home'        && <HomeScreen onLeveraged={() => handleRouteChange('leveraged-trade')} onLearnMore={() => setEquitiPlusOpen(true)}/>}
            {name === 'wallet'      && <WalletScreen active={activeTab === 'wallet'}/>}
            {name === 'news'        && <NewsScreen/>}
            {name === 'performance' && <PerformanceScreen/>}
          </div>
        ))}
        <TopFade/>
        <BottomFade/>
        <BottomNav active={activeTab} onChange={handleTabChange} />
      </div>

      {/* Leveraged Trade route layer — always mounted, opacity-driven. */}
      <div
        className={route === 'leveraged-trade' ? '' : 'tab-pane-inactive'}
        style={{
          position: 'absolute',
          inset: 0,
          opacity: routeOpacities['leveraged-trade'],
          transition: routeTransitions['leveraged-trade'],
        }}
      >
        <LeveragedTradeShell
          onBack={() => handleRouteChange('home')}
          onVoice={() => setVoiceMode('decision')}
        />
      </div>

      {/* Single shared VoiceFAB — stays MOUNTED across home ↔ trade nav
         swaps (so the smoke video keeps playing) AND across voice
         state changes. VoiceOverlay paints the surrounding affordances
         (chat panel, text bar, voice hint, X, glow rings) around the
         FAB; the FAB itself drives its own size + position via the
         `mode` prop. The FAB wrapper sits above the chat panel scrim
         (z 9000) so the FAB stays the topmost element in every state. */}
      <div
        style={{
          position: 'absolute',
          inset: 0,
          /* Pick the larger of the two route opacities — that's the
             "currently visible" route. Produces a natural cross-fade
             through the OUT and IN phases. */
          opacity: (cashFlowMode || equitiPlusOpen) ? 0 : Math.max(routeOpacities.home, routeOpacities['leveraged-trade']),
          transition: routeTransitions[route] || routeTransitions.home,
          /* The wrapper is non-interactive — pointer-events:none here
             stops it from blocking clicks to the routes below. The
             VoiceFAB's inner ring + button override pointer-events
             back to auto. */
          pointerEvents: 'none',
          zIndex: 9200,
        }}
      >
        <VoiceFAB
          mode={voiceMode}
          /* Suppress all pointer events on the FAB while a modal popup
             is open (CashFlowSheet, EquitiPlusModal). The wrapper above
             is already pointer-events:none, but VoiceFAB's inner ring
             and button override that back to 'auto' so taps were still
             falling through the modal scrim onto the FAB beneath. */
          suppressed={!!cashFlowMode || equitiPlusOpen}
          onPress={() => {
            /* FAB tap routing:
                 idle → decision         (FAB grows in place, options transition in)
                 decision → voice        (commit to voice; panel + transcript appear)
                 chat → voice            (switch from typing to voice mid-conversation)
                 voiceMini → voice       (re-expand from mini)
                 voice → no-op           (use X or close glyph to leave) */
            if (voiceMode === 'idle') setVoiceMode('decision');
            else if (voiceMode === 'decision') setVoiceMode('voice');
            else if (voiceMode === 'chat') setVoiceMode('voice');
            else if (voiceMode === 'voiceMini') setVoiceMode('voice');
          }}
        />
      </div>
      <VoiceOverlay
        mode={voiceMode}
        onSetMode={setVoiceMode}
        onEnd={() => setVoiceMode('idle')}
        /* Hide voice glow + CancelX + decision pills while any modal
           is open so they don't visually sit on top of the popup. */
        suppressed={!!cashFlowMode || equitiPlusOpen}
      />
      {window.EquitiPlusModal ? (
        <window.EquitiPlusModal visible={equitiPlusOpen} onClose={() => setEquitiPlusOpen(false)} />
      ) : null}
      {window.CashFlowSheet ? (
        <window.CashFlowSheet mode={cashFlowMode} visible={!!cashFlowMode} onClose={() => setCashFlowMode(null)} />
      ) : null}
      {window.CashFlowNotification ? <window.CashFlowNotification /> : null}
      <window.ToastHost />
    </div>
  );
};

Object.assign(window, { App });

/* Wrap App in TradeStoreProvider — owns reactive balances, positions,
   and live symbol prices used across the trade subapp and (via
   useTradingNumbers) the home portfolio card and wallet trading row. */
ReactDOM.createRoot(document.getElementById('root')).render(
  <TradeStoreProvider>
    <ConversationProvider>
      <App/>
    </ConversationProvider>
  </TradeStoreProvider>
);
