// App shell — topbar, tape, sidebar (tree), status, command palette
// Renders into #app-root. Exposes window.Shell.

(function() {
  const { useState, useEffect, useRef } = React;

  // Tree nav. Groups can have children (collapsible), leaves have no children.
  // id = route id; children nodes can be nested arbitrarily deep.
  const NAV = [
    { id: 'home-mkt', label: 'bankx.dev site', marker: '00', k: 'g h' },
    { id: 'home', label: 'Dashboard', marker: '01', k: 'g d' },
    { id: 'txns', label: 'Transactions', marker: '02', k: 'g t' },
    { id: 'budget', label: 'Budget & P&L', marker: '03', k: 'g b' },
    { id: '_reports', label: 'Reports', marker: '04', defaultOpen: true, children: [
      { id: 'reports/overview',  label: 'Overview' },
      { id: 'reports/pnl',       label: 'P&L statement' },
      { id: 'reports/cashflow',  label: 'Cash flow' },
      { id: 'reports/balance',   label: 'Balance sheet' },
      { id: 'reports/revenue',   label: 'Revenue' },
      { id: 'reports/burn',      label: 'Burn & runway' },
      { id: 'reports/treasury',  label: 'Treasury' },
      { id: 'reports/fx',        label: 'FX' },
      { id: 'reports/crypto',    label: 'Crypto' },
      { id: 'reports/customers', label: 'Customers' },
      { id: 'reports/cohorts',   label: 'Cohorts' },
      { id: 'reports/ar_ap',     label: 'AR / AP' },
      { id: 'reports/tax',       label: 'Tax summary' },
      { id: 'reports/audit',     label: 'Trial balance' },
    ]},
    { id: '_markets', label: 'Markets', marker: '05', defaultOpen: true, children: [
      { id: 'markets/overview', label: 'Overview' },
      { id: 'markets/watchlist', label: 'Watchlist' },
      { id: '_brokers', label: 'Brokers', children: [
        { id: 'markets/ib',       label: 'Interactive Brokers' },
        { id: 'markets/schwab',   label: 'Schwab' },
        { id: 'markets/fidelity', label: 'Fidelity' },
        { id: 'markets/robinhood',label: 'Robinhood' },
        { id: 'markets/etrade',   label: 'E*TRADE' },
        { id: 'markets/tasty',    label: 'tastytrade' },
      ]},
      { id: '_exchanges', label: 'Exchanges', children: [
        { id: 'markets/nyse',     label: 'NYSE' },
        { id: 'markets/nasdaq',   label: 'Nasdaq' },
        { id: 'markets/cme',      label: 'CME · Futures' },
        { id: 'markets/cboe',     label: 'CBOE · Options' },
        { id: 'markets/lse',      label: 'LSE · London' },
      ]},
      { id: '_crypto_ex', label: 'Crypto exchanges', children: [
        { id: 'markets/coinbase', label: 'Coinbase Pro' },
        { id: 'markets/binance',  label: 'Binance' },
        { id: 'markets/kraken',   label: 'Kraken' },
        { id: 'markets/onchain',  label: 'On-chain · wallets' },
      ]},
      { id: '_assets', label: 'Assets', children: [
        { id: 'markets/sym/AAPL',   label: 'AAPL · Apple' },
        { id: 'markets/sym/NVDA',   label: 'NVDA · Nvidia' },
        { id: 'markets/sym/SPY',    label: 'SPY · S&P 500' },
        { id: 'markets/sym/TSLA',   label: 'TSLA · Tesla' },
        { id: 'markets/sym/BTC',    label: 'BTC · Bitcoin' },
        { id: 'markets/sym/ETH',    label: 'ETH · Ethereum' },
        { id: 'markets/fx_eurusd',  label: 'EUR / USD' },
        { id: 'markets/futures_es', label: 'ES · S&P futures' },
        { id: 'markets/bonds_10y',  label: 'US 10Y · Treasury' },
      ]},
    ]},
    { id: 'rules', label: 'Automations', marker: '06', k: 'g r' },
    { id: 'tax',   label: 'Tax',         marker: '07', k: 'g x' },
    { id: '_dev', label: 'Developer', marker: '08', defaultOpen: true, children: [
      { id: 'term', label: 'Terminal', k: 'g .' },
      { id: 'api',  label: 'API & SDK', k: 'g a' },
      { id: 'mcp',  label: 'MCP', k: 'g m' },
    ]},
  ];

  // Flat walk of leaf items, used for palette/keyboard shortcuts
  function flatLeaves(nodes, acc=[]) {
    for (const n of nodes) {
      if (n.children) flatLeaves(n.children, acc);
      else if (n.id && !n.id.startsWith('_')) acc.push(n);
    }
    return acc;
  }
  const FLAT = flatLeaves(NAV);

  function Clock() {
    const [t, setT] = useState(new Date());
    useEffect(() => {
      const i = setInterval(() => setT(new Date()), 1000);
      return () => clearInterval(i);
    }, []);
    const pad = n => String(n).padStart(2, '0');
    return (
      <span>
        {t.getUTCFullYear()}-{pad(t.getUTCMonth()+1)}-{pad(t.getUTCDate())}{' '}
        {pad(t.getUTCHours())}:{pad(t.getUTCMinutes())}:{pad(t.getUTCSeconds())} UTC
      </span>
    );
  }

  function Topbar({ onCmd }) {
    return (
      <div className="topbar">
        <span className="lamp" />
        <span className="brand">bank<em>x</em><span className="dev">.dev</span></span>
        <span className="sep">│</span>
        <span>v1.4.2</span>
        <span className="sep">│</span>
        <span>live · us-east</span>
        <span className="sep">│</span>
        <span>audit #a12f8</span>
        <div className="search" onClick={onCmd} role="button" tabIndex={0}>
          <span className="ph">⌕ search ledgers, merchants, rules, api refs…</span>
          <kbd>⌘K</kbd>
        </div>
        <span className="clock mono"><Clock /></span>
      </div>
    );
  }

  function Tape() {
    const items = [...DATA.ticker, ...DATA.ticker];
    return (
      <div className="tape tape-strip">
        <div className="tape-inner">
          {items.map((t, i) => (
            <span className="tape-item" key={i}>
              <span className="sym">{t.sym}</span>
              <span style={{ color: 'var(--fg-0)' }}>{t.v}</span>
              <span style={{ color: t.d.startsWith('-') ? 'var(--neg)' : (t.d === '—' ? 'var(--fg-3)' : 'var(--pos)') }}>{t.d}</span>
            </span>
          ))}
        </div>
      </div>
    );
  }

  // --- Tree sidebar ---

  // Walk tree to determine initial open state — any group with defaultOpen, or
  // any ancestor of the current route.
  function pathTo(route, nodes, trail=[]) {
    for (const n of nodes) {
      if (n.id === route) return [...trail, n.id];
      if (n.children) {
        const hit = pathTo(route, n.children, [...trail, n.id]);
        if (hit) return hit;
      }
    }
    return null;
  }

  function TreeItem({ node, depth, route, goto, open, setOpen }) {
    const isGroup = !!node.children;
    const isOpen = open[node.id];
    const active = route === node.id;
    const onAncestor = node.children && pathTo(route, [node]) && !active;

    if (isGroup) {
      return (
        <div className="tree-node">
          <div
            className={'tree-row tree-group' + (onAncestor ? ' on-path' : '')}
            style={{ paddingLeft: 10 + depth*12 }}
            onClick={() => setOpen(o => ({ ...o, [node.id]: !o[node.id] }))}
          >
            <span className="tree-caret">{isOpen ? '▾' : '▸'}</span>
            {node.marker && <span className="marker">{node.marker}</span>}
            <span className="tree-label">{node.label}</span>
            {node.children && <span className="tree-count">{node.children.filter(c => !c.children).length || node.children.length}</span>}
          </div>
          {isOpen && (
            <div className="tree-children">
              {node.children.map(c => (
                <TreeItem key={c.id} node={c} depth={depth+1} route={route} goto={goto} open={open} setOpen={setOpen} />
              ))}
            </div>
          )}
        </div>
      );
    }
    return (
      <a
        className={'tree-row tree-leaf' + (active ? ' active' : '')}
        style={{ paddingLeft: 10 + depth*12 }}
        onClick={e => { e.preventDefault(); goto(node.id); }}
        href={'#' + node.id}
      >
        <span className="tree-caret tree-dot">{active ? '▸' : '·'}</span>
        {node.marker && <span className="marker">{node.marker}</span>}
        <span className="tree-label">{node.label}</span>
        {node.k && <span className="k">{node.k}</span>}
      </a>
    );
  }

  function Sidebar({ route, goto }) {
    const main = DATA.accounts[0];
    const initialOpen = (() => {
      try { return JSON.parse(localStorage.getItem('bankx.tree') || 'null') || null; }
      catch { return null; }
    })();
    const computeDefaults = () => {
      const o = {};
      const walk = (nodes) => {
        for (const n of nodes) {
          if (n.children) {
            o[n.id] = n.defaultOpen !== false;
            walk(n.children);
          }
        }
      };
      walk(NAV);
      return o;
    };
    const [open, setOpen] = useState(initialOpen || computeDefaults());
    // auto-open ancestor chain of current route
    useEffect(() => {
      const trail = pathTo(route, NAV);
      if (trail) {
        setOpen(o => {
          const n = { ...o };
          trail.forEach(id => { if (id.startsWith('_')) n[id] = true; });
          return n;
        });
      }
    }, [route]);
    useEffect(() => {
      localStorage.setItem('bankx.tree', JSON.stringify(open));
    }, [open]);

    return (
      <aside className="sidebar">
        <div className="account-chip">
          <span className="name">Operating · {main.mask}</span>
          <span className="bal">${main.balance.toLocaleString('en-US', { minimumFractionDigits: 2 })}</span>
          <span className="sub">
            <span style={{ color: 'var(--pos)' }}>+${main.delta.toLocaleString('en-US', { maximumFractionDigits: 2 })}</span>
            <span style={{ color: 'var(--fg-3)' }}> · 30d</span>
          </span>
        </div>

        <div className="tree">
          {NAV.map(n => (
            <TreeItem key={n.id} node={n} depth={0} route={route} goto={goto} open={open} setOpen={setOpen} />
          ))}
        </div>

        <div style={{ padding: '16px var(--pad-x) 24px', fontFamily: 'var(--font-mono)', fontSize: 10, color: 'var(--fg-3)', letterSpacing: '0.1em' }}>
          <div>MEMBER FDIC · via Stripe</div>
          <div>Treasury partner bank</div>
        </div>
      </aside>
    );
  }

  function Statusbar() {
    return (
      <div className="statusbar">
        <span className="seg ok"><span className="dot pulse" style={{color:'var(--pos)'}} /> connected · 11 sources</span>
        <span className="sep">│</span>
        <span className="seg">sync 2s ago</span>
        <span className="sep">│</span>
        <span className="seg">1,284 txns / 24h</span>
        <span className="sep">│</span>
        <span className="seg" style={{color:'var(--cat-amber)'}}>7 uncategorized</span>
        <span className="sep">│</span>
        <span className="seg">api 2.1.0</span>
        <span style={{ marginLeft: 'auto' }} className="seg">⌘K · palette</span>
        <span className="sep">│</span>
        <span className="seg">⌘, · tweaks</span>
      </div>
    );
  }

  // Command palette entries: all leaf routes + actions
  const NAV_CMDS = FLAT.map(n => ({ id: n.id, label: 'Go to ' + n.label, cat: 'nav' }));
  const ACTION_CMDS = [
    { id: '__cat',  label: 'Recategorize last 30 transactions', cat: 'action' },
    { id: '__xfer', label: 'Transfer · Operating → Tax Reserve', cat: 'action' },
    { id: '__rule', label: 'New automation rule',  cat: 'action' },
    { id: '__csv',  label: 'Import CSV / OFX / PDF statement', cat: 'action' },
    { id: '__api',  label: 'Mint API key (sandbox)', cat: 'action' },
    { id: '__buy',  label: 'Place order · buy 10 SPY', cat: 'action' },
  ];
  const CMDS = [...NAV_CMDS, ...ACTION_CMDS];

  function Cmdk({ onClose, goto }) {
    const [q, setQ] = useState('');
    const [i, setI] = useState(0);
    const ref = useRef();
    useEffect(() => { ref.current && ref.current.focus(); }, []);
    const filtered = CMDS.filter(c => c.label.toLowerCase().includes(q.toLowerCase()));
    const run = (cmd) => {
      if (cmd.id.startsWith('__')) { onClose(); return; }
      goto(cmd.id); onClose();
    };
    const onKey = (e) => {
      if (e.key === 'ArrowDown') { e.preventDefault(); setI(Math.min(filtered.length - 1, i + 1)); }
      else if (e.key === 'ArrowUp') { e.preventDefault(); setI(Math.max(0, i - 1)); }
      else if (e.key === 'Enter') { e.preventDefault(); filtered[i] && run(filtered[i]); }
      else if (e.key === 'Escape') { onClose(); }
    };
    return (
      <div className="cmdk-scrim" onMouseDown={onClose}>
        <div className="cmdk" onMouseDown={e => e.stopPropagation()}>
          <input
            ref={ref}
            className="cmdk-input"
            placeholder="› type a command, search a transaction, or paste a ticker…"
            value={q}
            onChange={e => { setQ(e.target.value); setI(0); }}
            onKeyDown={onKey}
          />
          <div className="cmdk-list">
            {filtered.map((c, idx) => (
              <div key={c.id} className={'cmdk-item' + (idx === i ? ' sel' : '')}
                   onMouseEnter={() => setI(idx)} onClick={() => run(c)}>
                <span className="arrow">{c.cat === 'nav' ? '→' : '▸'}</span>
                <span>{c.label}</span>
                <span className="cat">{c.cat}</span>
              </div>
            ))}
            {!filtered.length && <div className="cmdk-item">no matches</div>}
          </div>
        </div>
      </div>
    );
  }

  window.Shell = { Topbar, Tape, Sidebar, Statusbar, Cmdk };
})();
