// Vantage — shell: header, tabs, data loading. Quotes refresh every 60s
// while the tab is visible; everything else refreshes after a mutation.

function VantaMV() {
  return (
    <span className="vanta-m" style={{
      display: 'grid', placeItems: 'center', width: 28, height: 28,
      fontFamily: '"Newsreader", serif', fontWeight: 600, fontSize: 24,
      color: '#050505', lineHeight: 1, WebkitTextStroke: `0.8px ${VG.ink3}`,
    }}>V</span>
  );
}

function VantageApp() {
  const [tab, setTab] = React.useState('pulse');   // the read first; the book one tap away
  const [positions, setPositions] = React.useState([]);
  const [decisions, setDecisions] = React.useState([]);
  const [analytics, setAnalytics] = React.useState(null);
  const [quotes, setQuotes] = React.useState({});
  const [catalysts, setCatalysts] = React.useState(null);
  const [bookSignals, setBookSignals] = React.useState(null);
  const [watchlist, setWatchlist] = React.useState([]);
  const [researchSymbol, setResearchSymbol] = React.useState(null); // handed in by globe/other tabs
  const [globeFocus, setGlobeFocus] = React.useState(null);        // a hotspot handed up from the pulse globe
  const [loaded, setLoaded] = React.useState(false);

  // Any tab can send a symbol to research: switch tabs and open its dossier.
  const openResearch = React.useCallback(sym => { setResearchSymbol(sym); setTab('research'); }, []);

  // The pulse globe hands a clicked hotspot up here; carry it to the globe tab
  // so it opens on that hotspot instead of a blank planet. A bare call (the
  // "open the globe →" button passes a click event) just switches tabs.
  const openGlobe = React.useCallback(h => {
    setGlobeFocus(h && h.lat != null ? h : null);
    setTab('globe');
  }, []);

  const refresh = React.useCallback(() => {
    Promise.all([
      vgGet('/vantage/api/portfolio').catch(() => ({ positions: [] })),
      vgGet('/vantage/api/decisions').catch(() => ({ decisions: [] })),
      vgGet('/vantage/api/analytics').catch(() => null),
    ]).then(([p, d, a]) => {
      setPositions(p.positions || []);
      setDecisions(d.decisions || []);
      setAnalytics(a);
      setLoaded(true);
    });
  }, []);

  React.useEffect(refresh, [refresh]);

  // Catalysts (FOMC + prints + earnings for held symbols) — slow-moving,
  // refetched only when the set of holdings changes.
  const watchSyms = watchlist.map(w => w.symbol);
  const holdKey = [...new Set([...positions.map(p => p.symbol), ...watchSyms])].sort().join(',');
  React.useEffect(() => {
    if (!loaded) return;
    vgGet(`/vantage/api/catalysts${holdKey ? `?symbols=${encodeURIComponent(holdKey)}` : ''}`)
      .then(setCatalysts).catch(() => setCatalysts(null));
  }, [holdKey, loaded]);

  // Watchlist — the tier between researched and owned; its symbols join
  // quotes, catalysts, and signals coverage below.
  const loadWatchlist = React.useCallback(() => {
    vgGet('/vantage/api/watchlist').then(d => setWatchlist(d.watchlist || [])).catch(() => {});
  }, []);
  React.useEffect(loadWatchlist, [loadWatchlist]);

  // Book signals — per-holding character + threads through owned sectors.
  // Slow-moving (daily character, news threads), so it rides the holdings key
  // like catalysts rather than the 60s quote cadence.
  React.useEffect(() => {
    if (!loaded || !holdKey) { setBookSignals(null); return; }
    vgGet('/vantage/api/book-signals').then(setBookSignals).catch(() => setBookSignals(null));
  }, [holdKey, loaded]);

  // Quotes for every symbol in play (positions + journal), refreshed on a
  // 60s cadence — matches the server cache so we never hammer the source.
  const symbols = [...new Set([...positions.map(p => p.symbol), ...decisions.map(d => d.symbol), ...watchSyms])];
  const symKey = symbols.sort().join(',');
  React.useEffect(() => {
    if (!symKey) return;
    let alive = true;
    const load = () => vgGet(`/vantage/api/quotes?symbols=${encodeURIComponent(symKey)}`)
      .then(d => {
        if (!alive) return;
        const map = {};
        for (const q of d.quotes || []) map[q.symbol] = q;
        setQuotes(map);
      }).catch(() => {});
    load();
    const t = setInterval(() => { if (!document.hidden) load(); }, 60_000);
    return () => { alive = false; clearInterval(t); };
  }, [symKey]);

  const dueCount = decisions.filter(d => vgReviewState(d).due).length;
  const anyStale = symbols.length > 0 && symbols.every(s => quotes[s] && quotes[s].price == null);

  const tabBtn = (key, label, badge) => (
    <button onClick={() => setTab(key)} style={{
      appearance: 'none', border: 0, cursor: 'pointer',
      padding: '7px 16px', borderRadius: 999, fontSize: 13, fontWeight: 600,
      background: tab === key ? VG.tile2 : 'transparent',
      color: tab === key ? VG.ink : VG.ink3,
      display: 'inline-flex', alignItems: 'center', gap: 7,
    }}>
      {label}
      {badge > 0 && (
        <span style={{
          ...vgS.num, fontSize: 10, fontWeight: 700, background: VG.accent, color: '#0a0208',
          borderRadius: 999, padding: '1px 7px',
        }}>{badge}</span>
      )}
    </button>
  );

  return (
    <div style={{
      minHeight: '100%', boxSizing: 'border-box',
      background: `radial-gradient(1100px 700px at 30% -10%, ${VG.bg2}, ${VG.bg} 60%)`,
      color: VG.ink, fontSize: 13, lineHeight: 1.45, padding: '20px 26px 48px',
    }}>
      <header style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 18, flexWrap: 'wrap' }}>
        <VantaMV />
        <span style={{ ...vgS.serif, fontSize: 24, color: VG.ink }}>vantage</span>
        <span style={{ ...vgS.caps, marginLeft: 2 }}>research · portfolio · judgement</span>
        <span style={{ flex: 1 }} />
        <div style={{ display: 'flex', gap: 2, background: VG.chip, borderRadius: 999, padding: 3 }}>
          {tabBtn('pulse', 'pulse')}
          {tabBtn('portfolio', 'portfolio')}
          {tabBtn('globe', 'globe')}
          {tabBtn('research', 'research')}
          {tabBtn('journal', 'journal', dueCount)}
        </div>
        <a href="/" style={{ ...vgS.caps, color: VG.ink3, padding: '6px 12px', borderRadius: 999, background: VG.chip }}>garden</a>
        <a href="/agents" style={{ ...vgS.caps, color: VG.ink3, padding: '6px 12px', borderRadius: 999, background: VG.chip }}>agents</a>
      </header>

      {anyStale && (
        <div style={{
          background: VG.chip, borderRadius: 12, padding: '8px 14px', marginBottom: 12,
          fontSize: 12, color: VG.ink3,
        }}>
          market data is unreachable right now — showing cost basis; P&L and quotes return automatically.
        </div>
      )}

      {!loaded ? (
        <div style={{ ...vgS.serif, color: VG.ink3, fontSize: 16, padding: '40px 0' }}>opening the ledger…</div>
      ) : tab === 'portfolio' ? (
        <VantagePortfolio positions={positions} quotes={quotes} onRefresh={refresh} earnings={catalysts?.earnings ?? {}}
          signals={bookSignals?.signals ?? {}} signalsSummary={bookSignals?.summary ?? null}
          watchlist={watchlist} onWatchChanged={loadWatchlist} onResearchSym={openResearch} />
      ) : tab === 'pulse' ? (
        <VantagePulse positions={positions} catalysts={catalysts} onGlobe={openGlobe}
          onJournal={() => setTab('journal')} onResearchSym={openResearch} />
      ) : tab === 'globe' ? (
        <VantageGlobe onResearch={openResearch} initialPick={globeFocus}
          onConsumedInitial={() => setGlobeFocus(null)} />
      ) : tab === 'research' ? (
        <VantageResearch quotes={quotes} onRefresh={refresh}
          initialSymbol={researchSymbol} onConsumedInitial={() => setResearchSymbol(null)}
          watchlist={watchlist} onWatchChanged={loadWatchlist} />
      ) : (
        <VantageJournal decisions={decisions} analytics={analytics} quotes={quotes} onRefresh={refresh} />
      )}
    </div>
  );
}

window.VantageApp = VantageApp;
