// tr-hooks.jsx — Auto-update hook + global settings.
// Exposes:
//   window.useAutoUpdate(key, fetcherFn, intervalMsOverride)
//   window.TR_SETTINGS  (reactive singleton backed by localStorage)
//   window.TRSettingsSheet  (slide-up panel React component)
//   window.TRGearButton  (tiny header button that opens the sheet)
//
// Settings shape (localStorage key "tr_settings"):
// {
//   keys: { coingecko, tradier, polygon, claude, alpaca, finnhub },
//   refresh: { header, historical, news, calendar, signals, impact, projected, recommend }  // seconds
//   sources: { stocks: 'yahoo'|'polygon'|'alpaca', options: 'tradier' }
// }

const TR_DEFAULT_SETTINGS = {
  keys: { coingecko: '', tradier: '', polygon: '', claude: '', openai: '', gemini: '', grok: '', perplexity: '', alpaca: '', finnhub: '', newsapi: '', newsdata: '', bitly: '', telegramBot: '', telegramChatId: '' },
  refresh: {
    header: 60, historical: 300, news: 180, calendar: 600,
    signals: 120, impact: 60, projected: 600, recommend: 600, prices: 30,
  },
  sources: { stocks: 'yahoo', options: 'tradier' },
};

function trLoadSettings() {
  try {
    const raw = localStorage.getItem('tr_settings');
    if (!raw) return JSON.parse(JSON.stringify(TR_DEFAULT_SETTINGS));
    const parsed = JSON.parse(raw);
    // merge with defaults so new fields pick up
    return {
      keys: { ...TR_DEFAULT_SETTINGS.keys, ...(parsed.keys || {}) },
      refresh: { ...TR_DEFAULT_SETTINGS.refresh, ...(parsed.refresh || {}) },
      sources: { ...TR_DEFAULT_SETTINGS.sources, ...(parsed.sources || {}) },
    };
  } catch { return JSON.parse(JSON.stringify(TR_DEFAULT_SETTINGS)); }
}
function trSaveSettings(s) {
  localStorage.setItem('tr_settings', JSON.stringify(s));
  window.dispatchEvent(new CustomEvent('tr:settings-changed', { detail: s }));
}

// Reactive settings singleton
window.TR_SETTINGS = trLoadSettings();

function useTRSettings() {
  const [s, setS] = React.useState(window.TR_SETTINGS);
  React.useEffect(() => {
    const h = (e) => { window.TR_SETTINGS = e.detail; setS(e.detail); };
    window.addEventListener('tr:settings-changed', h);
    return () => window.removeEventListener('tr:settings-changed', h);
  }, []);
  return [s, (next) => { trSaveSettings(next); }];
}

// Generic auto-update hook. intervalMs comes from settings if `refreshKey` passed.
function useAutoUpdate(key, fetcher, { refreshKey = 'header', manualMs = null, enabled = true } = {}) {
  const [data, setData] = React.useState(null);
  const [loading, setLoading] = React.useState(false);
  const [error, setError] = React.useState(null);
  const [lastFetch, setLastFetch] = React.useState(null);
  const [tick, setTick] = React.useState(0);
  const fetcherRef = React.useRef(fetcher);
  fetcherRef.current = fetcher;

  // Resolve interval from settings or override (seconds → ms)
  const [settings] = useTRSettings();
  const intervalMs = manualMs ?? (settings.refresh[refreshKey] || 60) * 1000;

  React.useEffect(() => {
    if (!enabled) return;
    let active = true;
    let timer = null;
    const run = async () => {
      setLoading(true);
      try {
        const res = await fetcherRef.current();
        if (!active) return;
        setData(res); setError(null); setLastFetch(new Date());
      } catch (e) {
        if (!active) return;
        setError(e.message || String(e));
      } finally {
        if (active) setLoading(false);
      }
    };
    run();
    if (intervalMs > 0 && intervalMs < 3_600_000) {
      timer = setInterval(run, intervalMs);
    }
    return () => { active = false; if (timer) clearInterval(timer); };
  }, [key, intervalMs, enabled, tick]);

  const refresh = React.useCallback(() => setTick(t => t + 1), []);
  return { data, loading, error, lastFetch, refresh, intervalMs };
}

window.useAutoUpdate = useAutoUpdate;
window.useTRSettings = useTRSettings;

// ───── Watchlist (localStorage-backed) ─────
// Shape: { tickers: [{sym, name, kind}], options: [{symbol, underlying,
// strike, expiration, optionType, bid, ask, volume, oi, added}] }
function trLoadWatchlist() {
  try {
    const raw = localStorage.getItem('tr_watchlist');
    if (!raw) return { tickers: [], options: [] };
    const p = JSON.parse(raw);
    return { tickers: p.tickers || [], options: p.options || [] };
  } catch { return { tickers: [], options: [] }; }
}
function trSaveWatchlist(w) {
  localStorage.setItem('tr_watchlist', JSON.stringify(w));
  window.dispatchEvent(new CustomEvent('tr:watchlist-changed', { detail: w }));
}
window.TR_WATCHLIST = trLoadWatchlist();

function useTRWatchlist() {
  const [w, setW] = React.useState(window.TR_WATCHLIST);
  React.useEffect(() => {
    const h = (e) => { window.TR_WATCHLIST = e.detail; setW(e.detail); };
    window.addEventListener('tr:watchlist-changed', h);
    return () => window.removeEventListener('tr:watchlist-changed', h);
  }, []);
  return {
    watchlist: w,
    isTickerSaved: (sym) => !!w.tickers.find(t => t.sym === sym),
    isOptionSaved: (symbol) => !!w.options.find(o => o.symbol === symbol),
    toggleTicker: (ticker) => {
      const exists = w.tickers.find(t => t.sym === ticker.sym);
      const next = {
        ...w,
        tickers: exists ? w.tickers.filter(t => t.sym !== ticker.sym)
                        : [...w.tickers, { sym: ticker.sym, name: ticker.name, kind: ticker.kind, id: ticker.id, stooq: ticker.stooq }],
      };
      trSaveWatchlist(next);
    },
    toggleOption: (opt) => {
      const exists = w.options.find(o => o.symbol === opt.symbol);
      const next = {
        ...w,
        options: exists ? w.options.filter(o => o.symbol !== opt.symbol)
                        : [...w.options, { ...opt, added: Date.now() }],
      };
      trSaveWatchlist(next);
    },
    clearAll: () => trSaveWatchlist({ tickers: [], options: [] }),
  };
}
window.useTRWatchlist = useTRWatchlist;

// ───── Settings sheet ─────
// Per-provider test — returns { ok, ms, detail } or throws.
async function trTestProvider(k, key) {
  if (!key) return { ok: false, detail: 'no key' };
  const t0 = Date.now();
  try {
    if (k === 'claude') {
      const r = await fetch('https://api.anthropic.com/v1/messages', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json', 'x-api-key': key,
          'anthropic-version': '2023-06-01',
          'anthropic-dangerous-direct-browser-access': 'true',
        },
        body: JSON.stringify({ model: 'claude-haiku-4-5-20251001', max_tokens: 8, messages: [{ role: 'user', content: 'ping' }] }),
      });
      return { ok: r.ok, ms: Date.now() - t0, detail: r.ok ? 'ok' : `HTTP ${r.status}` };
    }
    if (k === 'openai') {
      const r = await fetch('https://api.openai.com/v1/models', { headers: { Authorization: `Bearer ${key}` } });
      return { ok: r.ok, ms: Date.now() - t0, detail: r.ok ? 'ok' : `HTTP ${r.status}` };
    }
    if (k === 'gemini') {
      const r = await fetch(`https://generativelanguage.googleapis.com/v1beta/models?key=${key}`);
      return { ok: r.ok, ms: Date.now() - t0, detail: r.ok ? 'ok' : `HTTP ${r.status}` };
    }
    if (k === 'grok') {
      const r = await fetch('https://api.x.ai/v1/models', { headers: { Authorization: `Bearer ${key}` } });
      return { ok: r.ok, ms: Date.now() - t0, detail: r.ok ? 'ok' : `HTTP ${r.status}` };
    }
    if (k === 'perplexity') {
      const r = await fetch('https://api.perplexity.ai/chat/completions', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${key}` },
        body: JSON.stringify({ model: 'sonar', messages: [{ role: 'user', content: 'ping' }], max_tokens: 5 }),
      });
      return { ok: r.ok, ms: Date.now() - t0, detail: r.ok ? 'ok' : `HTTP ${r.status}` };
    }
    if (k === 'finnhub') {
      const r = await fetch(`https://finnhub.io/api/v1/quote?symbol=AAPL&token=${key}`);
      const j = await r.json();
      return { ok: !!(j && typeof j.c === 'number' && j.c > 0), ms: Date.now() - t0, detail: j.c > 0 ? `AAPL $${j.c}` : 'empty' };
    }
    if (k === 'tradier') {
      const mode = (window.TR_SETTINGS && window.TR_SETTINGS.meta && window.TR_SETTINGS.meta.tradierMode) || 'sandbox';
      const base = mode === 'live' ? 'https://api.tradier.com/v1' : 'https://sandbox.tradier.com/v1';
      const r = await fetch(`${base}/markets/quotes?symbols=SPY`, { headers: { Authorization: `Bearer ${key}`, Accept: 'application/json' } });
      return { ok: r.ok, ms: Date.now() - t0, detail: r.ok ? `${mode} ok` : `HTTP ${r.status}` };
    }
    if (k === 'newsapi') {
      const r = await fetch(`https://newsapi.org/v2/top-headlines?country=us&pageSize=1&apiKey=${key}`);
      return { ok: r.ok, ms: Date.now() - t0, detail: r.ok ? 'ok' : `HTTP ${r.status}` };
    }
    if (k === 'newsdata') {
      const r = await fetch(`https://newsdata.io/api/1/news?apikey=${key}&q=bitcoin&size=1`);
      return { ok: r.ok, ms: Date.now() - t0, detail: r.ok ? 'ok' : `HTTP ${r.status}` };
    }
    if (k === 'bitly') {
      const r = await fetch('https://api-ssl.bitly.com/v4/user', { headers: { Authorization: `Bearer ${key}` } });
      return { ok: r.ok, ms: Date.now() - t0, detail: r.ok ? 'ok' : `HTTP ${r.status}` };
    }
    if (k === 'telegramBot') {
      const r = await fetch(`https://api.telegram.org/bot${key}/getMe`);
      const j = await r.json();
      return { ok: !!j.ok, ms: Date.now() - t0, detail: j.ok ? `@${j.result.username}` : (j.description || 'failed') };
    }
    if (k === 'telegramChatId') {
      // Try to send a test message using stored bot + this chat id
      const botToken = (window.TR_SETTINGS?.keys?.telegramBot) || '';
      if (!botToken) return { ok: false, detail: 'bot token missing' };
      const r = await fetch(`https://api.telegram.org/bot${botToken}/sendMessage`, {
        method: 'POST', headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ chat_id: key, text: '✅ TradeRadar alert test — if you see this, you are wired up.' }),
      });
      const j = await r.json();
      return { ok: !!j.ok, ms: Date.now() - t0, detail: j.ok ? 'sent' : (j.description || 'failed') };
    }
    if (k === 'coingecko') {
      const r = await fetch(`https://api.coingecko.com/api/v3/ping?x_cg_demo_api_key=${key}`);
      return { ok: r.ok, ms: Date.now() - t0, detail: r.ok ? 'ok' : `HTTP ${r.status}` };
    }
    if (k === 'polygon') {
      const r = await fetch(`https://api.polygon.io/v2/aggs/ticker/AAPL/prev?apiKey=${key}`);
      return { ok: r.ok, ms: Date.now() - t0, detail: r.ok ? 'ok' : `HTTP ${r.status}` };
    }
    if (k === 'alpaca') {
      const [id, secret] = key.split(':');
      const r = await fetch('https://paper-api.alpaca.markets/v2/account', {
        headers: { 'APCA-API-KEY-ID': id || '', 'APCA-API-SECRET-KEY': secret || '' },
      });
      return { ok: r.ok, ms: Date.now() - t0, detail: r.ok ? 'paper ok' : `HTTP ${r.status}` };
    }
    return { ok: false, detail: 'unknown provider' };
  } catch (e) {
    return { ok: false, ms: Date.now() - t0, detail: e.message };
  }
}
window.trTestProvider = trTestProvider;

function TRSettingsSheet({ open, onClose }) {
  const T = {
    ink000: '#07090C', ink100: '#0B0E13', ink200: '#10141B', ink300: '#171C24',
    edge: 'rgba(255,255,255,0.06)', edgeHi: 'rgba(255,255,255,0.10)',
    text: '#ffffff', textMid: 'rgba(180,188,200,0.75)', textDim: 'rgba(130,138,150,0.55)',
    signal: '#c9a227',
    mono: '"JetBrains Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace',
  };
  const [s, save] = useTRSettings();
  const [testResults, setTestResults] = React.useState({});   // { k: {ok, ms, detail, testing} }
  const [advancedOpen, setAdvancedOpen] = React.useState(false);

  const runTest = async (k) => {
    setTestResults(prev => ({ ...prev, [k]: { ...(prev[k] || {}), testing: true } }));
    const res = await trTestProvider(k, s.keys[k]);
    setTestResults(prev => ({ ...prev, [k]: { ...res, testing: false } }));
  };

  if (!open) return null;

  const updateKey = (k, v) => save({ ...s, keys: { ...s.keys, [k]: v } });
  const updateRefresh = (k, v) => save({ ...s, refresh: { ...s.refresh, [k]: v } });
  const updateSource = (k, v) => save({ ...s, sources: { ...s.sources, [k]: v } });

  const refreshLabel = (sec) => sec <= 0 ? 'Off' : sec < 60 ? `${sec}s` : `${Math.round(sec / 60)}m`;
  const refreshOptions = [15, 30, 60, 120, 300, 600, 0];

  // Core keys — shown always. Cover 90% of app features.
  const coreKeys = [
    { k: 'claude',   label: 'Anthropic (Claude) API Key', hint: 'Primary LLM for predictions + rationale' },
    { k: 'openai',   label: 'OpenAI (ChatGPT) API Key',   hint: 'Secondary LLM · dual-consensus' },
    { k: 'finnhub',  label: 'Finnhub API Key',            hint: 'Stock prices + econ calendar (free tier)' },
    { k: 'tradier',  label: 'Tradier Token',              hint: 'Options chains + trading layer' },
  ];
  // Advanced keys — collapsed by default. Optional LLMs, alt providers, integrations.
  const advancedKeys = [
    { k: 'gemini',         label: 'Google AI Studio (Gemini) Key', hint: 'Optional 3rd LLM opinion' },
    { k: 'grok',           label: 'xAI (Grok) API Key',            hint: 'Optional — X/Twitter angle' },
    { k: 'perplexity',     label: 'Perplexity API Key',            hint: 'Optional — web-search-grounded LLM' },
    { k: 'alpaca',         label: 'Alpaca Keys (id:secret)',       hint: 'Paper trading (alt to Tradier)' },
    { k: 'polygon',        label: 'Polygon.io API Key',            hint: 'Alt stock/options provider' },
    { k: 'coingecko',      label: 'CoinGecko API Key',             hint: 'Optional · higher rate limits' },
    { k: 'telegramBot',    label: 'Telegram Bot Token',            hint: 'From @BotFather — pushes alerts to Telegram' },
    { k: 'telegramChatId', label: 'Telegram Chat ID',              hint: 'DM your bot, then test to get your id' },
    { k: 'newsapi',        label: 'NewsAPI Key (newsapi.org)',     hint: 'Aggregated global news' },
    { k: 'newsdata',       label: 'NewsData Key (newsdata.io)',    hint: 'Alt news aggregator' },
    { k: 'bitly',          label: 'Bitly API Key',                 hint: 'Auto-shorten shared links' },
  ];

  const refreshRows = [
    { k: 'header',     label: 'Header strip (BTC · F&G)' },
    { k: 'historical', label: 'Historical chart series' },
    { k: 'news',       label: 'News feeds (RSS)' },
    { k: 'signals',    label: 'Signals dashboard' },
    { k: 'impact',     label: 'Impact tab (stocks + options)' },
    { k: 'projected',  label: 'Projected (AI narrative)' },
    { k: 'calendar',   label: 'Calendar events' },
    { k: 'recommend',  label: 'Recommend (AI consensus)' },
    { k: 'prices',     label: 'Prices tab (stocks + futures + crypto)' },
  ];

  return (
    <div
      onClick={onClose}
      style={{
        position: 'fixed', inset: 0, background: 'rgba(7,9,12,0.72)',
        backdropFilter: 'blur(14px) saturate(150%)', WebkitBackdropFilter: 'blur(14px) saturate(150%)',
        display: 'flex', alignItems: 'stretch', justifyContent: 'flex-end',
        zIndex: 100,
      }}>
      <div
        onClick={(e) => e.stopPropagation()}
        style={{
          width: 520, background: T.ink100, borderLeft: `1px solid ${T.edgeHi}`,
          overflowY: 'auto', padding: '28px 32px',
          fontFamily: '"Inter Tight", system-ui, sans-serif', color: T.text,
          boxShadow: '-20px 0 60px rgba(0,0,0,0.5)',
        }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 24 }}>
          <div style={{ fontSize: 10, letterSpacing: 1.2, color: T.textDim, textTransform: 'uppercase', fontWeight: 600 }}>
            TradeRadar
          </div>
          <div style={{ fontSize: 22, fontWeight: 500, letterSpacing: -0.3, color: T.text }}>Settings</div>
          <div
            onClick={onClose}
            style={{
              marginLeft: 'auto', width: 28, height: 28, borderRadius: 7,
              background: T.ink300, border: `1px solid ${T.edge}`,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              cursor: 'pointer', color: T.textMid, fontSize: 13,
            }}>✕</div>
        </div>

        {/* REFRESH */}
        <div style={{
          fontSize: 10, letterSpacing: 1.2, color: T.signal,
          textTransform: 'uppercase', fontWeight: 600, marginBottom: 10,
        }}>Auto-refresh frequency</div>
        <div style={{ fontSize: 12.5, color: T.textMid, lineHeight: 1.55, marginBottom: 14 }}>
          Per-screen poll interval. Set to Off to freeze data on that page.
        </div>
        <div style={{
          background: T.ink200, border: `1px solid ${T.edge}`, borderRadius: 10,
          padding: '12px 14px', display: 'flex', flexDirection: 'column', gap: 10, marginBottom: 24,
        }}>
          {refreshRows.map(r => (
            <div key={r.k} style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
              <div style={{ fontSize: 12.5, color: T.text, flex: 1 }}>{r.label}</div>
              <div style={{ display: 'flex', gap: 3, padding: 2, background: T.ink000, borderRadius: 6, border: `1px solid ${T.edge}` }}>
                {refreshOptions.map(sec => {
                  const on = (s.refresh[r.k] || 0) === sec;
                  return (
                    <div key={sec}
                      onClick={() => updateRefresh(r.k, sec)}
                      style={{
                        padding: '3px 8px', fontFamily: T.mono, fontSize: 10, fontWeight: 500,
                        color: on ? T.ink000 : T.textMid,
                        background: on ? T.signal : 'transparent',
                        borderRadius: 4, cursor: on ? 'default' : 'pointer',
                      }}>{refreshLabel(sec)}</div>
                  );
                })}
              </div>
            </div>
          ))}
        </div>

        {/* DATA SOURCES */}
        <div style={{
          fontSize: 10, letterSpacing: 1.2, color: T.signal,
          textTransform: 'uppercase', fontWeight: 600, marginBottom: 10,
        }}>Data sources</div>
        <div style={{
          background: T.ink200, border: `1px solid ${T.edge}`, borderRadius: 10,
          padding: '12px 14px', display: 'flex', flexDirection: 'column', gap: 10, marginBottom: 24,
        }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
            <div style={{ fontSize: 12.5, color: T.text, flex: 1 }}>Tradier mode</div>
            {['sandbox', 'live'].map(v => {
              const on = (s.meta?.tradierMode || 'sandbox') === v;
              return (
                <div key={v}
                  onClick={() => save({ ...s, meta: { ...(s.meta || {}), tradierMode: v } })}
                  style={{
                    padding: '4px 10px', fontSize: 10.5, letterSpacing: 0.3,
                    fontFamily: T.mono, fontWeight: 600,
                    background: on ? T.signal : T.ink000,
                    color: on ? T.ink000 : T.textMid,
                    border: `1px solid ${on ? T.signal : T.edge}`, borderRadius: 5,
                    cursor: on ? 'default' : 'pointer',
                  }}>{v}</div>
              );
            })}
          </div>
          <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
            <div style={{ fontSize: 12.5, color: T.text, flex: 1 }}>Stock prices</div>
            {['yahoo', 'polygon', 'alpaca', 'finnhub'].map(v => {
              const on = s.sources.stocks === v;
              return (
                <div key={v}
                  onClick={() => updateSource('stocks', v)}
                  style={{
                    padding: '4px 10px', fontSize: 10.5, letterSpacing: 0.3,
                    fontFamily: T.mono, fontWeight: 600,
                    background: on ? T.signal : T.ink000,
                    color: on ? T.ink000 : T.textMid,
                    border: `1px solid ${on ? T.signal : T.edge}`, borderRadius: 5,
                    cursor: on ? 'default' : 'pointer',
                  }}>{v}</div>
              );
            })}
          </div>
        </div>

        {/* API KEYS */}
        <div style={{
          fontSize: 10, letterSpacing: 1.2, color: T.signal,
          textTransform: 'uppercase', fontWeight: 600, marginBottom: 10,
        }}>API keys</div>
        <div style={{ fontSize: 12.5, color: T.textMid, lineHeight: 1.55, marginBottom: 14 }}>
          Stored locally in this browser only. Never sent to any TradeRadar server.
        </div>
        {/* KEY ROW RENDERER — reused for Core and Advanced */}
        {(() => {
          const renderRow = (f) => {
            const res = testResults[f.k];
            return (
              <div key={f.k}>
                <div style={{ display: 'flex', alignItems: 'baseline', marginBottom: 4 }}>
                  <div style={{ fontSize: 11.5, fontWeight: 500, color: T.text }}>{f.label}</div>
                  <div style={{ marginLeft: 'auto', fontSize: 10, color: T.textDim }}>{f.hint}</div>
                </div>
                <div style={{ display: 'flex', gap: 6 }}>
                  <input type="password"
                    value={s.keys[f.k] || ''}
                    onChange={(e) => updateKey(f.k, e.target.value)}
                    placeholder={s.keys[f.k] ? '•••• saved' : 'Paste key to enable live data'}
                    style={{
                      flex: 1, padding: '8px 12px', fontFamily: T.mono, fontSize: 12,
                      background: T.ink000, border: `1px solid ${T.edge}`, color: T.text,
                      borderRadius: 6, outline: 'none',
                    }}
                  />
                  <div onClick={() => s.keys[f.k] && !res?.testing && runTest(f.k)}
                    style={{
                      padding: '0 12px', display: 'flex', alignItems: 'center',
                      background: !s.keys[f.k] ? T.ink300
                        : res?.testing ? T.ink300
                        : res?.ok === true ? 'rgba(111,207,142,0.18)'
                        : res?.ok === false ? 'rgba(217,107,107,0.18)'
                        : T.ink200,
                      border: `1px solid ${!s.keys[f.k] ? T.edge
                        : res?.ok === true ? 'rgba(111,207,142,0.5)'
                        : res?.ok === false ? 'rgba(217,107,107,0.5)'
                        : T.edge}`,
                      borderRadius: 6, cursor: s.keys[f.k] ? 'pointer' : 'default',
                      fontSize: 11, fontWeight: 600, letterSpacing: 0.3,
                      color: !s.keys[f.k] ? T.textDim
                        : res?.ok === true ? '#6FCF8E'
                        : res?.ok === false ? '#D96B6B'
                        : T.textMid,
                      fontFamily: T.mono,
                    }}>
                    {res?.testing ? '…' : res ? (res.ok ? `✓ ${res.ms}ms` : '✕') : 'Test'}
                  </div>
                </div>
                {res && !res.testing && res.detail && (
                  <div style={{
                    fontSize: 9.5, marginTop: 4, color: res.ok ? '#6FCF8E' : '#D96B6B',
                    fontFamily: T.mono, letterSpacing: 0.3,
                  }}>{res.detail}</div>
                )}
              </div>
            );
          };
          return (
            <>
              {/* CORE block */}
              <div style={{
                background: T.ink200, border: `1px solid ${T.edge}`, borderRadius: 10,
                padding: '14px 16px', display: 'flex', flexDirection: 'column', gap: 14,
                marginBottom: 12,
              }}>
                <div style={{
                  fontSize: 9, letterSpacing: 1.2, color: T.textDim,
                  textTransform: 'uppercase', fontWeight: 600,
                }}>Core — covers 90% of features</div>
                {coreKeys.map(renderRow)}
              </div>

              {/* ADVANCED collapsible */}
              <div
                onClick={() => setAdvancedOpen(v => !v)}
                style={{
                  display: 'flex', alignItems: 'center', gap: 8,
                  padding: '10px 14px', cursor: 'pointer',
                  background: T.ink200, border: `1px solid ${T.edge}`, borderRadius: 10,
                  marginBottom: advancedOpen ? 10 : 24,
                }}>
                <div style={{ fontSize: 9, letterSpacing: 1.2, color: T.textMid, textTransform: 'uppercase', fontWeight: 600 }}>
                  Advanced — optional LLMs, alt providers, integrations
                </div>
                <div style={{ marginLeft: 'auto', fontFamily: T.mono, fontSize: 11, color: T.textDim }}>
                  {advancedOpen ? '▾' : '▸'} {advancedKeys.length}
                </div>
              </div>
              {advancedOpen && (
                <div style={{
                  background: T.ink200, border: `1px solid ${T.edge}`, borderRadius: 10,
                  padding: '14px 16px', display: 'flex', flexDirection: 'column', gap: 14,
                  marginBottom: 24,
                }}>
                  {advancedKeys.map(renderRow)}
                </div>
              )}
            </>
          );
        })()}

        {/* RUNTIME LOG */}
        <TRRuntimeLogSection T={T} />

        <div style={{ fontSize: 10.5, color: T.textDim, letterSpacing: 0.3, lineHeight: 1.55 }}>
          Tradier sandbox (delayed data) is free. Polygon.io starts at $29/mo. Finnhub has a generous free tier
          for US stock prices. Alpaca paper-trading keys are free. CoinGecko works without a key but has lower limits.
        </div>
      </div>
    </div>
  );
}

// ───── Runtime log panel (collapsible) ─────
// Polls window.TRLogger every 2s while open; renders last 50 entries.
function TRRuntimeLogSection({ T }) {
  const [open, setOpen] = React.useState(false);
  const [rows, setRows] = React.useState([]);

  React.useEffect(() => {
    if (!open) return;
    const read = () => {
      const src = (window.TRLogger && typeof window.TRLogger.entries === 'function')
        ? window.TRLogger.entries() : [];
      setRows(src.slice(0, 50));
    };
    read();
    const id = setInterval(read, 2000);
    return () => clearInterval(id);
  }, [open]);

  const clearLog = () => {
    if (window.TRLogger && typeof window.TRLogger.clear === 'function') {
      window.TRLogger.clear();
    }
    setRows([]);
  };

  const fmtTime = (ts) => {
    const d = new Date(ts);
    const hh = String(d.getHours()).padStart(2, '0');
    const mm = String(d.getMinutes()).padStart(2, '0');
    const ss = String(d.getSeconds()).padStart(2, '0');
    return `${hh}:${mm}:${ss}`;
  };

  const shortUrl = (u) => {
    if (!u) return '';
    try {
      const p = new URL(u);
      return `${p.host}${p.pathname}`;
    } catch { return u.length > 60 ? u.slice(0, 58) + '…' : u; }
  };

  return (
    <>
      <div
        onClick={() => setOpen(v => !v)}
        style={{
          display: 'flex', alignItems: 'center', gap: 8,
          padding: '10px 14px', cursor: 'pointer',
          background: T.ink200, border: `1px solid ${T.edge}`, borderRadius: 10,
          marginBottom: open ? 10 : 24,
        }}>
        <div style={{ fontSize: 9, letterSpacing: 1.2, color: T.signal, textTransform: 'uppercase', fontWeight: 600 }}>
          Runtime log
        </div>
        <div style={{ fontSize: 10.5, color: T.textMid, fontFamily: T.mono }}>
          last 50 fetches · live
        </div>
        <div style={{ marginLeft: 'auto', fontFamily: T.mono, fontSize: 11, color: T.textDim }}>
          {open ? '▾' : '▸'} {rows.length}
        </div>
      </div>
      {open && (
        <div style={{
          background: T.ink200, border: `1px solid ${T.edge}`, borderRadius: 10,
          padding: '10px 12px', marginBottom: 24,
        }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 8 }}>
            <div style={{ fontSize: 10, color: T.textDim, fontFamily: T.mono }}>
              {rows.length} entries · polling 2s
            </div>
            <div
              onClick={clearLog}
              style={{
                marginLeft: 'auto', padding: '4px 10px', fontSize: 10.5, letterSpacing: 0.3,
                fontFamily: T.mono, fontWeight: 600, color: T.textMid,
                background: T.ink000, border: `1px solid ${T.edge}`, borderRadius: 5,
                cursor: 'pointer',
              }}>Clear log</div>
          </div>
          <div style={{
            maxHeight: 320, overflowY: 'auto',
            border: `1px solid ${T.edge}`, borderRadius: 6, background: T.ink000,
          }}>
            <table style={{
              width: '100%', borderCollapse: 'collapse', fontFamily: T.mono, fontSize: 10.5,
            }}>
              <thead>
                <tr style={{ position: 'sticky', top: 0, background: T.ink100, zIndex: 1 }}>
                  <th style={{ textAlign: 'left', padding: '6px 8px', color: T.textDim, fontWeight: 600, borderBottom: `1px solid ${T.edge}` }}>TIME</th>
                  <th style={{ textAlign: 'left', padding: '6px 8px', color: T.textDim, fontWeight: 600, borderBottom: `1px solid ${T.edge}` }}>METHOD · URL</th>
                  <th style={{ textAlign: 'right', padding: '6px 8px', color: T.textDim, fontWeight: 600, borderBottom: `1px solid ${T.edge}` }}>STATUS</th>
                  <th style={{ textAlign: 'right', padding: '6px 8px', color: T.textDim, fontWeight: 600, borderBottom: `1px solid ${T.edge}` }}>MS</th>
                  <th style={{ textAlign: 'center', padding: '6px 8px', color: T.textDim, fontWeight: 600, borderBottom: `1px solid ${T.edge}` }}>CACHE</th>
                </tr>
              </thead>
              <tbody>
                {rows.length === 0 && (
                  <tr><td colSpan={5} style={{ padding: 14, textAlign: 'center', color: T.textDim }}>
                    No log entries yet. Fetches via <span style={{ color: T.textMid }}>window.trFetch</span> will appear here.
                  </td></tr>
                )}
                {rows.map((r, i) => {
                  const isErr = !!r.error || r.status === 0 || r.status >= 500;
                  const is429 = r.status === 429;
                  const rowBg = isErr ? 'rgba(217,107,107,0.12)'
                    : is429 ? 'rgba(201,162,39,0.12)'
                    : 'transparent';
                  const statusColor = isErr ? '#D96B6B'
                    : is429 ? T.signal
                    : r.status >= 400 ? '#D96B6B'
                    : r.status >= 200 ? '#6FCF8E'
                    : T.textMid;
                  return (
                    <tr key={`${r.ts}-${i}`} style={{ background: rowBg }}>
                      <td style={{ padding: '5px 8px', color: T.textMid, whiteSpace: 'nowrap' }}>{fmtTime(r.ts)}</td>
                      <td style={{ padding: '5px 8px', color: T.text, maxWidth: 280, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }} title={`${r.method} ${r.url}${r.error ? ' — ' + r.error : ''}`}>
                        <span style={{ color: T.textDim }}>{r.method}</span>{' '}{shortUrl(r.url)}
                      </td>
                      <td style={{ padding: '5px 8px', color: statusColor, textAlign: 'right', fontWeight: 600 }}>{r.status || '—'}</td>
                      <td style={{ padding: '5px 8px', color: T.textMid, textAlign: 'right' }}>{r.ms || 0}</td>
                      <td style={{ padding: '5px 8px', textAlign: 'center', color: r.cached ? T.signal : T.textDim }}>
                        {r.cached ? '✓' : '·'}
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        </div>
      )}
    </>
  );
}

window.TRSettingsSheet = TRSettingsSheet;

// Tiny gear button for the header — uses state local to caller.
function TRGearButton({ onClick }) {
  return (
    <div
      onClick={onClick}
      style={{
        width: 28, height: 28, borderRadius: 7,
        background: '#10141B', border: '1px solid rgba(255,255,255,0.06)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        cursor: 'pointer', color: 'rgba(180,188,200,0.75)', fontSize: 14,
      }}
      title="Settings · refresh frequency · API keys"
    >⚙</div>
  );
}
window.TRGearButton = TRGearButton;
