// ── Backtester — full port of TBS backtester with Graphite Ledger skin ──
const TBS_BASE = 'https://tbs.dagevos.cloud';

const CRYPTO_ASSETS = [
  { v:'BTCUSDT',  l:'BTC — Bitcoin' },    { v:'ETHUSDT',  l:'ETH — Ethereum' },
  { v:'XRPUSDT',  l:'XRP — XRP' },        { v:'BNBUSDT',  l:'BNB — BNB' },
  { v:'SOLUSDT',  l:'SOL — Solana' },     { v:'DOGEUSDT', l:'DOGE — Dogecoin' },
  { v:'ADAUSDT',  l:'ADA — Cardano' },    { v:'AVAXUSDT', l:'AVAX — Avalanche' },
  { v:'LINKUSDT', l:'LINK — Chainlink' }, { v:'NEARUSDT', l:'NEAR — NEAR Protocol' },
  { v:'UNIUSDT',  l:'UNI — Uniswap' },   { v:'ARBUSDT',  l:'ARB — Arbitrum' },
  { v:'OPUSDT',   l:'OP — Optimism' },    { v:'SUIUSDT',  l:'SUI — Sui' },
  { v:'INJUSDT',  l:'INJ — Injective' },  { v:'ATOMUSDT', l:'ATOM — Cosmos' },
  { v:'PEPEUSDT', l:'PEPE — Pepe' },      { v:'TAOUSDT',  l:'TAO — Bittensor' },
  { v:'HYPEUSDT', l:'HYPE — Hyperliquid' },{ v:'DOTUSDT',  l:'DOT — Polkadot' },
  { v:'TRXUSDT',  l:'TRX — TRON' },       { v:'SHIBUSDT', l:'SHIB — Shiba Inu' },
  { v:'BCHUSDT',  l:'BCH — Bitcoin Cash' },{ v:'LTCUSDT',  l:'LTC — Litecoin' },
  { v:'APTUSDT',  l:'APT — Aptos' },      { v:'HBARUSDT', l:'HBAR — Hedera' },
  { v:'ICPUSDT',  l:'ICP — Internet Computer' },{ v:'SEIUSDT', l:'SEI — Sei' },
  { v:'WIFUSDT',  l:'WIF — Dogwifhat' },  { v:'FILUSDT',  l:'FIL — Filecoin' },
  { v:'STXUSDT',  l:'STX — Stacks' },     { v:'PENGUUSDT',l:'PENGU — Pudgy Penguins' },
  { v:'TONUSDT',  l:'TON — Toncoin' },
];
const TRADFI_ASSETS = [
  { v:'SPX',    l:'SPX — S&P 500' },  { v:'NASDAQ', l:'NASDAQ — Nasdaq' },
  { v:'GOLD',   l:'GOLD — Gold' },    { v:'SILVER', l:'SILVER — Silver' },
];
const ALL_ASSETS = [...CRYPTO_ASSETS, ...TRADFI_ASSETS];
const aLabel = v => { const a = ALL_ASSETS.find(x => x.v === v); return a ? a.l.split(' — ')[0] : v; };
const fmtPct  = v => (v >= 0 ? '+' : '') + v.toFixed(2) + '%';
const retColor = v => v >= 50 ? TH.green : v >= 0 ? TH.inkSoft : TH.red;
const retColorStr = v => v >= 500 ? TH.green : v >= 100 ? TH.green : v >= 0 ? TH.inkSoft : TH.red;

const METRIC_LABELS = { calmar:'Calmar', totalReturn:'Total Return', cagr:'CAGR', sharpe:'Sharpe', lowDD:'Max DD ↓' };

function scoreDisplayStr(r, metric) {
  if (!isFinite(r.score) || r.liquidated) return '—';
  switch (metric) {
    case 'totalReturn': return fmtPct(r.totalReturn);
    case 'cagr':        return fmtPct(r.cagr);
    case 'sharpe':      return r.sharpe?.toFixed(3) ?? '—';
    case 'lowDD':       return r.maxDrawdown?.toFixed(1) + '%';
    default:            return r.score?.toFixed(2) ?? '—';
  }
}

// ── Micro-components ─────────────────────────────────────────────
function SLabel({ children }) {
  return <div style={{ fontFamily: TH.mono, fontSize: 10, color: TH.muted, letterSpacing: 1.5, textTransform: 'uppercase', marginBottom: 5 }}>{children}</div>;
}
function SField({ label, style = {}, children }) {
  return <div style={{ marginBottom: 12, ...style }}>{label && <SLabel>{label}</SLabel>}{children}</div>;
}
function SSelect({ value, onChange, children, style = {} }) {
  return (
    <select value={value} onChange={e => onChange(e.target.value)} style={{
      width: '100%', background: TH.bg, border: `1px solid ${TH.rule}`, color: TH.ink,
      fontFamily: TH.mono, fontSize: 12, padding: '7px 10px', outline: 'none', cursor: 'pointer', ...style,
    }}>{children}</select>
  );
}
function SInput({ value, onChange, ...props }) {
  return (
    <input value={value} onChange={e => onChange(e.target.value)}
      style={{ width: '100%', background: TH.bg, border: `1px solid ${TH.rule}`, color: TH.ink, fontFamily: TH.mono, fontSize: 12, padding: '7px 10px', outline: 'none', boxSizing: 'border-box' }}
      onFocus={e => e.target.style.borderColor = TH.accent}
      onBlur={e => e.target.style.borderColor = TH.rule}
      {...props} />
  );
}
function AssetSelect({ value, onChange }) {
  return (
    <SSelect value={value} onChange={onChange}>
      <optgroup label="Crypto">{CRYPTO_ASSETS.map(a => <option key={a.v} value={a.v}>{a.l}</option>)}</optgroup>
      <optgroup label="Traditional">{TRADFI_ASSETS.map(a => <option key={a.v} value={a.v}>{a.l}</option>)}</optgroup>
    </SSelect>
  );
}
function MaTypeSel({ value, onChange }) {
  return <SSelect value={value} onChange={onChange}><option value="SMA">SMA</option><option value="EMA">EMA</option></SSelect>;
}
function DirSel({ value, onChange }) {
  return (
    <SSelect value={value} onChange={onChange}>
      <option value="long">Long only</option>
      <option value="short">Short only</option>
      <option value="both">Long &amp; Short</option>
    </SSelect>
  );
}
function MetricSel({ value, onChange }) {
  return (
    <SSelect value={value} onChange={onChange}>
      <option value="calmar">Calmar (CAGR / Max DD)</option>
      <option value="totalReturn">Total return</option>
      <option value="cagr">CAGR</option>
      <option value="sharpe">Sharpe ratio</option>
      <option value="lowDD">Lowest drawdown</option>
    </SSelect>
  );
}
function LevPair({ longVal, onLong, shortVal, onShort }) {
  return (
    <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginBottom: 12 }}>
      <SField label="Long Lev"><SInput type="number" value={longVal} onChange={onLong} min="0.1" max="10" step="0.1"/></SField>
      <SField label="Short Lev"><SInput type="number" value={shortVal} onChange={onShort} min="0.1" max="10" step="0.1"/></SField>
    </div>
  );
}
function AccentLabel({ children }) {
  return <div style={{ fontFamily: TH.mono, fontSize: 10, color: TH.accent, letterSpacing: 1.5, marginBottom: 8, marginTop: 4 }}>{children}</div>;
}
function RunBtn({ onClick, loading, disabled, children, style = {} }) {
  return (
    <button onClick={onClick} disabled={disabled || loading} style={{
      width: '100%', padding: '11px 0', background: loading ? TH.panelHi : TH.accent,
      color: loading ? TH.muted : TH.bg, fontFamily: TH.mono, fontSize: 11,
      fontWeight: 700, letterSpacing: 1.5, textTransform: 'uppercase', border: 'none',
      cursor: disabled || loading ? 'not-allowed' : 'pointer', marginBottom: 6, ...style,
    }}>
      {loading ? 'Running…' : children}
    </button>
  );
}
function SecBtn({ onClick, loading, disabled, children, style = {} }) {
  return (
    <button onClick={onClick} disabled={disabled || loading} style={{
      width: '100%', padding: '8px 0', background: 'transparent',
      color: disabled || loading ? TH.muted : TH.inkSoft, fontFamily: TH.mono, fontSize: 10,
      fontWeight: 500, letterSpacing: 1.2, textTransform: 'uppercase',
      border: `1px solid ${TH.rule}`, cursor: disabled || loading ? 'not-allowed' : 'pointer',
      marginBottom: 5, ...style,
    }}>
      {loading ? 'Scanning…' : children}
    </button>
  );
}
function MetricRow({ label, value, color }) {
  return (
    <div style={{ padding: '9px 0', borderBottom: `1px solid ${TH.rule}` }}>
      <div style={{ fontFamily: TH.mono, fontSize: 10, color: TH.muted, letterSpacing: 1, marginBottom: 3 }}>{label}</div>
      <div style={{ fontFamily: TH.mono, fontSize: 18, color: color || TH.ink }}>{value}</div>
    </div>
  );
}

// ── Optimize best-params grid ─────────────────────────────────────
function OptBestGrid({ cells }) {
  return (
    <div style={{ display: 'flex', flexWrap: 'wrap', gap: 1, background: TH.rule, marginBottom: 12 }}>
      {cells.map((c, i) => (
        <div key={i} style={{ flex: '1 1 110px', background: TH.panel, padding: '9px 12px' }}>
          <div style={{ fontFamily: TH.mono, fontSize: 10, color: TH.muted, letterSpacing: 1, marginBottom: 3 }}>{c.label}</div>
          <div style={{ fontFamily: TH.mono, fontSize: 14, color: c.color || TH.ink, fontWeight: 600 }}>{c.value}</div>
        </div>
      ))}
    </div>
  );
}

// ── Reusable result table ─────────────────────────────────────────
function ResultTable({ head, rows, activeIdx, onRowClick }) {
  return (
    <div style={{ overflowX: 'auto' }}>
      <table style={{ width: '100%', borderCollapse: 'collapse', fontFamily: TH.mono, fontSize: 11 }}>
        <thead>
          <tr style={{ borderBottom: `1px solid ${TH.rule}` }}>
            {head.map((h, i) => (
              <th key={i} style={{ padding: '7px 10px', textAlign: i === 0 ? 'left' : 'right', color: TH.muted, fontSize: 10, letterSpacing: 1.2, whiteSpace: 'nowrap', fontWeight: 400 }}>{h}</th>
            ))}
          </tr>
        </thead>
        <tbody>
          {rows.map((r, i) => (
            <tr key={i} onClick={() => onRowClick && onRowClick(i, r)}
              style={{ borderBottom: `1px solid ${TH.rule}`, cursor: onRowClick ? 'pointer' : 'default', background: i === activeIdx ? TH.panelHi : i === 0 ? 'rgba(212,168,75,0.04)' : 'transparent', transition: 'background .1s' }}
              onMouseEnter={e => { if (i !== activeIdx) e.currentTarget.style.background = TH.panelHi; }}
              onMouseLeave={e => { e.currentTarget.style.background = i === activeIdx ? TH.panelHi : i === 0 ? 'rgba(212,168,75,0.04)' : 'transparent'; }}>
              {r.cells.map((cell, j) => (
                <td key={j} style={{ padding: '8px 10px', textAlign: j === 0 ? 'left' : 'right', color: cell.color || TH.ink, fontWeight: j === 0 ? 600 : 400, whiteSpace: 'nowrap', borderLeft: j === 0 && i === activeIdx ? `2px solid ${TH.accent}` : j === 0 ? '2px solid transparent' : undefined }}>
                  {cell.text}
                  {i === 0 && j === 0 && <span style={{ marginLeft: 8, fontSize: 9, background: TH.accent, color: TH.bg, padding: '1px 5px', letterSpacing: 1 }}>BEST</span>}
                </td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

// ── Trade log ─────────────────────────────────────────────────────
function TradeLog({ trades }) {
  if (!trades || trades.length === 0) return null;
  const wins    = trades.filter(t => !t.open && t.returnPct > 0).length;
  const closed  = trades.filter(t => !t.open).length;
  const wr      = closed > 0 ? ((wins / closed) * 100).toFixed(0) + '%' : '—';
  const hardHits = trades.filter(t => t.stoppedOut && t.hardStop).length;
  const closeHits = trades.filter(t => t.stoppedOut && !t.hardStop).length;
  const slParts = [];
  if (hardHits > 0) slParts.push(`${hardHits} hard`);
  if (closeHits > 0) slParts.push(`${closeHits} close`);
  const slStr = slParts.length > 0 ? ` · ${slParts.join(', ')} SL` : '';

  const fmtPrice = v => {
    if (v == null) return '—';
    const n = Number(v);
    if (n === 0) return '$0';
    const dec = n < 0.0001 ? 8 : n < 0.01 ? 6 : n < 1 ? 4 : 2;
    return '$' + n.toLocaleString('en-US', { minimumFractionDigits: dec, maximumFractionDigits: dec });
  };

  return (
    <div style={{ border: `1px solid ${TH.rule}`, marginTop: 14, background: TH.panel }}>
      <div style={{ padding: '10px 14px', borderBottom: `1px solid ${TH.rule}`, display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 8 }}>
        <div style={{ fontFamily: TH.mono, fontSize: 10, color: TH.accent, letterSpacing: 2 }}>◉ TRADE LOG</div>
        <div style={{ fontFamily: TH.mono, fontSize: 10, color: TH.inkSoft, background: TH.bg, border: `1px solid ${TH.rule}`, padding: '3px 10px' }}>
          {trades.length} trades · {wr} WR{slStr}
        </div>
      </div>
      <div style={{ overflowX: 'auto' }}>
        <table style={{ width: '100%', borderCollapse: 'collapse', fontFamily: TH.mono, fontSize: 11 }}>
          <thead>
            <tr style={{ borderBottom: `1px solid ${TH.rule}` }}>
              {['#','Entry','Exit','Dir','Entry Px','Exit Px','Return','Days','Status'].map((h,i) => (
                <th key={i} style={{ padding: '7px 10px', textAlign: i < 4 ? 'left' : 'right', color: TH.muted, fontSize: 10, letterSpacing: 1, fontWeight: 400, whiteSpace: 'nowrap' }}>{h}</th>
              ))}
            </tr>
          </thead>
          <tbody>
            {[...trades].reverse().map((t, idx) => {
              const n = trades.length - idx;
              const ret = t.returnPct;
              const retStr = (ret >= 0 ? '+' : '') + ret.toFixed(2) + '%';
              const holdDays = t.entryDate && t.exitDate
                ? Math.round((new Date(t.exitDate) - new Date(t.entryDate)) / 86400000)
                : '—';
              let statusEl, statusColor;
              if (t.liquidated)              { statusEl = 'LIQUIDATED'; statusColor = TH.red; }
              else if (t.stoppedOut && t.hardStop) { statusEl = 'HARD STOP'; statusColor = TH.red; }
              else if (t.stoppedOut)          { statusEl = 'STOPPED OUT'; statusColor = '#f97316'; }
              else if (t.open)               { statusEl = 'OPEN'; statusColor = '#818cf8'; }
              else                           { statusEl = 'Closed'; statusColor = TH.muted; }
              return (
                <tr key={idx} style={{ borderBottom: `1px solid ${TH.rule}` }}>
                  <td style={{ padding: '7px 10px', color: TH.muted, fontSize: 10 }}>{n}</td>
                  <td style={{ padding: '7px 10px', color: TH.inkSoft }}>{t.entryDate || '—'}</td>
                  <td style={{ padding: '7px 10px', color: TH.inkSoft }}>{t.exitDate || '—'}</td>
                  <td style={{ padding: '7px 10px', color: t.direction === 'long' ? TH.green : TH.red, fontWeight: 600 }}>{(t.direction || '').toUpperCase()}</td>
                  <td style={{ padding: '7px 10px', textAlign: 'right', color: TH.ink }}>{fmtPrice(t.entryPrice)}</td>
                  <td style={{ padding: '7px 10px', textAlign: 'right', color: TH.ink }}>{fmtPrice(t.exitPrice)}</td>
                  <td style={{ padding: '7px 10px', textAlign: 'right', color: ret > 0 ? TH.green : ret < 0 ? TH.red : TH.ink, fontWeight: 600 }}>{retStr}</td>
                  <td style={{ padding: '7px 10px', textAlign: 'right', color: TH.muted }}>{holdDays}d</td>
                  <td style={{ padding: '7px 10px', textAlign: 'right', color: statusColor, fontWeight: 600 }}>{statusEl}</td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </div>
  );
}

// ── Main component ────────────────────────────────────────────────
function Backtester({ go }) {
  const wide = useWide();

  // Auth
  const [token, setToken]     = React.useState(null);
  const [authErr, setAuthErr] = React.useState(false);

  // Mode
  const [mode, setMode]           = React.useState('sweep');
  const [overallSub, setOverallSub] = React.useState('sweep');

  // Shared period
  const [period,   setPeriod]   = React.useState('5Y');
  const [dateFrom, setDateFrom] = React.useState('');
  const [dateTo,   setDateTo]   = React.useState('');

  // Sweep
  const [asset,      setAsset]      = React.useState('BTCUSDT');
  const [maType,     setMaType]     = React.useState('SMA');
  const [direction,  setDirection]  = React.useState('long');
  const [levLong,    setLevLong]    = React.useState(1.0);
  const [levShort,   setLevShort]   = React.useState(1.0);
  const [specificMA, setSpecificMA] = React.useState('');

  // Single
  const [sAsset,      setSAsset]      = React.useState('BTCUSDT');
  const [sMaType,     setSMaType]     = React.useState('SMA');
  const [sMaPeriod,   setSMaPeriod]   = React.useState(44);
  const [sTradeAsset, setSTradeAsset] = React.useState('ETHUSDT');
  const [sDir,        setSDir]        = React.useState('long');
  const [sLevLong,    setSLevLong]    = React.useState(1.0);
  const [sLevShort,   setSLevShort]   = React.useState(1.0);

  // Dual
  const [d1Asset,   setD1Asset]   = React.useState('ETHUSDT');
  const [d1MaType,  setD1MaType]  = React.useState('SMA');
  const [d1Period,  setD1Period]  = React.useState(34);
  const [d2Asset,   setD2Asset]   = React.useState('BTCUSDT');
  const [d2MaType,  setD2MaType]  = React.useState('SMA');
  const [d2Period,  setD2Period]  = React.useState(44);
  const [dTrade,    setDTrade]    = React.useState('BTCUSDT');
  const [dDir,      setDDir]      = React.useState('long');
  const [dLevLong,  setDLevLong]  = React.useState(1.0);
  const [dLevShort, setDLevShort] = React.useState(1.0);

  // Crossover
  const [xAsset,    setXAsset]    = React.useState('BTCUSDT');
  const [xMaType,   setXMaType]   = React.useState('SMA');
  const [xFast,     setXFast]     = React.useState(20);
  const [xSlow,     setXSlow]     = React.useState(50);
  const [xDir,      setXDir]      = React.useState('long');
  const [xLevLong,  setXLevLong]  = React.useState(1.0);
  const [xLevShort, setXLevShort] = React.useState(1.0);

  // Overall sweep params
  const [oAssets, setOAssets] = React.useState(() => {
    const on = new Set(['BTCUSDT','ETHUSDT','SOLUSDT','SPX','NASDAQ','GOLD','SILVER']);
    return Object.fromEntries(ALL_ASSETS.map(a => [a.v, on.has(a.v)]));
  });
  const [oPeriod,   setOPeriod]   = React.useState('5Y');
  const [oMaType,   setOMaType]   = React.useState('SMA');
  const [oDir,      setODir]      = React.useState('long');
  const [oLevLong,  setOLevLong]  = React.useState(1.0);
  const [oLevShort, setOLevShort] = React.useState(1.0);
  const [oMetric,   setOMetric]   = React.useState('calmar');

  // Overall dual params
  const [odMaType1, setOdMaType1] = React.useState('SMA');
  const [odPeriod1, setOdPeriod1] = React.useState(34);
  const [odMaType2, setOdMaType2] = React.useState('SMA');
  const [odPeriod2, setOdPeriod2] = React.useState(44);
  const [odDir,     setOdDir]     = React.useState('long');
  const [odMetric,  setOdMetric]  = React.useState('calmar');

  // SL / win-skip
  const [slType,       setSlType]       = React.useState('none');
  const [slPct,        setSlPct]        = React.useState(5);
  const [hardSlType,   setHardSlType]   = React.useState('none');
  const [hardSlPct,    setHardSlPct]    = React.useState(3);
  const [slOpen,       setSlOpen]       = React.useState(false);
  const [winSkipCount, setWinSkipCount] = React.useState(0);
  const [winSkipMinPct,setWinSkipMinPct]= React.useState(10);

  // Optimize metric
  const [optMetric, setOptMetric] = React.useState('calmar');

  // Loading
  const [loading,        setLoading]        = React.useState(false);
  const [optLoading,     setOptLoading]     = React.useState(false);
  const [slLoading,      setSlLoading]      = React.useState(false);
  const [skipLoading,    setSkipLoading]    = React.useState(false);
  const [overallLoading, setOverallLoading] = React.useState(false);

  // Status / error
  const [statusMsg, setStatusMsg] = React.useState('');
  const [error,     setError]     = React.useState('');

  // Results
  const [sweepData,     setSweepData]     = React.useState(null);
  const [metrics,       setMetrics]       = React.useState(null);
  const [chartTitle,    setChartTitle]    = React.useState('');
  const [chartSub,      setChartSub]      = React.useState('');
  const [sortCol,       setSortCol]       = React.useState('totalReturn');
  const [sortAsc,       setSortAsc]       = React.useState(false);
  const [activeRow,     setActiveRow]     = React.useState(null);
  const [trades,        setTrades]        = React.useState(null);
  const [optimizeResult,setOptimizeResult]= React.useState(null); // {data, mode, metric, activeIdx}
  const [slResult,      setSlResult]      = React.useState(null); // {data, metric, activeIdx}
  const [skipResult,    setSkipResult]    = React.useState(null); // {data, metric, activeIdx}

  // Refs for async-safe param snapshots
  const currentParamsRef    = React.useRef(null);
  const currentOptParamsRef = React.useRef(null);

  // Chart
  const canvasRef = React.useRef(null);
  const chartRef  = React.useRef(null);
  React.useEffect(() => () => { chartRef.current?.destroy(); }, []);

  // Auth
  React.useEffect(() => {
    fetch(`${TBS_BASE}/auth/guest`, { method: 'POST' })
      .then(r => r.json())
      .then(d => d.token ? setToken(d.token) : setAuthErr(true))
      .catch(() => setAuthErr(true));
  }, []);

  const authFetch = React.useCallback((path, opts = {}) =>
    fetch(`${TBS_BASE}${path}`, {
      ...opts,
      headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json', ...(opts.headers || {}) },
    }), [token]);

  // Draw chart
  const drawChart = React.useCallback((equityCurve, bah, label) => {
    if (!canvasRef.current || !window.Chart) return;
    chartRef.current?.destroy();
    chartRef.current = null;
    const ctx = canvasRef.current.getContext('2d');
    chartRef.current = new window.Chart(ctx, {
      type: 'line',
      data: {
        labels: equityCurve.map(p => p.date),
        datasets: [
          { label, data: equityCurve.map(p => p.equity), borderColor: TH.accent, backgroundColor: 'rgba(212,168,75,0.06)', borderWidth: 2, pointRadius: 0, fill: true, tension: 0.1 },
          { label: 'Buy & Hold', data: (bah || []).map(p => p.equity), borderColor: '#5f5a4e', borderDash: [5,5], borderWidth: 1.5, pointRadius: 0, fill: false, tension: 0.1 },
        ],
      },
      options: {
        responsive: true, maintainAspectRatio: false,
        interaction: { mode: 'index', intersect: false },
        plugins: {
          legend: { labels: { color: TH.inkSoft, font: { family: 'JetBrains Mono, monospace', size: 11 }, boxWidth: 12, padding: 16 } },
          tooltip: {
            backgroundColor: TH.panel, borderColor: TH.rule, borderWidth: 1,
            titleColor: TH.muted, bodyColor: TH.ink,
            titleFont: { family: 'JetBrains Mono, monospace', size: 10 },
            bodyFont:  { family: 'JetBrains Mono, monospace', size: 12 },
            callbacks: { label: ctx => ` ${ctx.dataset.label}: ${ctx.parsed.y >= 0 ? '+' : ''}${ctx.parsed.y.toFixed(1)}%` },
          },
        },
        scales: {
          x: { ticks: { color: TH.muted, font: { family: 'JetBrains Mono, monospace', size: 10 }, maxTicksLimit: 8 }, grid: { color: TH.rule } },
          y: { ticks: { color: TH.muted, font: { family: 'JetBrains Mono, monospace', size: 10 }, callback: v => (v >= 0 ? '+' : '') + v.toFixed(0) + '%' }, grid: { color: TH.rule } },
        },
      },
    });
  }, []);

  const getSLConfig = () => ({
    slType, slPct: slPct / 100, hardSlType, hardSlPct: hardSlPct / 100,
    winSkipCount, winSkipMinPct,
  });

  const buildMetrics = data => ({
    totalReturn: data.totalReturn, cagr: data.cagr, maxDrawdown: data.maxDrawdown,
    sharpe: data.sharpe, tradeCount: data.tradeCount, liquidated: data.liquidated,
    vsBah: data.totalReturn - (data.buyAndHold?.totalReturn ?? 0),
  });

  const metricsFromCurve = (curve, bah, trds) => {
    if (!curve?.length) return null;
    const start = curve[0].equity, end = curve[curve.length - 1].equity;
    const totalReturn = end - 100;
    let peak = start, maxDD = 0;
    for (const p of curve) {
      if (p.equity > peak) peak = p.equity;
      const dd = (peak - p.equity) / peak * 100;
      if (dd > maxDD) maxDD = dd;
    }
    const ms = new Date(curve[curve.length-1].date) - new Date(curve[0].date);
    const years = ms / (365.25 * 86400000);
    const cagr = years > 0 ? (Math.pow(end / start, 1 / years) - 1) * 100 : totalReturn;
    const bahReturn = (Array.isArray(bah) ? bah : [])?.slice(-1)[0]?.equity - 100 || 0;
    return { totalReturn, cagr, maxDrawdown: maxDD, sharpe: null, tradeCount: trds?.length ?? 0, liquidated: false, vsBah: totalReturn - bahReturn };
  };

  const clearResults = () => {
    setSweepData(null); setMetrics(null); setTrades(null);
    setOptimizeResult(null); setSlResult(null); setSkipResult(null);
    setChartTitle(''); setChartSub(''); setError(''); setStatusMsg('');
    setActiveRow(null); currentParamsRef.current = null; currentOptParamsRef.current = null;
  };

  const changeMode = m => { setMode(m); clearResults(); };

  // ── Run backtest ───────────────────────────────────────────────
  const run = async () => {
    if (!token) return;
    setLoading(true); setError(''); setStatusMsg('Running…');
    setOptimizeResult(null); setSlResult(null); setSkipResult(null);
    const t0 = Date.now();
    try {
      const sl = getSLConfig();
      if (mode === 'sweep') {
        const sp = parseInt(specificMA);
        if (sp >= 4 && sp <= 240) {
          const qp = new URLSearchParams({ asset, period, ma: sp, type: maType, leverageLong: levLong, leverageShort: levShort, direction, ...sl });
          if (period === 'CUSTOM') { qp.set('dateFrom', dateFrom); qp.set('dateTo', dateTo); }
          const res = await authFetch(`/api/backtest/equity?${qp}`);
          const d = await res.json(); if (!res.ok) throw new Error(d.error);
          const m = metricsFromCurve(d.equityCurve, d.buyAndHold, d.trades);
          currentParamsRef.current = { asset, period, dateFrom, dateTo, maType, leverageLong: +levLong, leverageShort: +levShort, direction, bestPeriod: sp, bahReturn: m?.vsBah != null ? m.totalReturn - m.vsBah : 0 };
          setChartTitle(`${maType} ${sp} — Equity Curve`);
          setChartSub(`${aLabel(asset)} · ${period === 'CUSTOM' ? `${dateFrom}→${dateTo}` : period} · L${levLong}x/S${levShort}x · ${direction}`);
          drawChart(d.equityCurve, d.buyAndHold, `${maType} ${sp}`);
          setMetrics(m); setTrades(d.trades);
          setSweepData(null);
          setStatusMsg(`Done ${((Date.now()-t0)/1000).toFixed(1)}s · ${d.trades?.length ?? 0} trades`);
        } else {
          const body = { asset, period, maType, leverageLong: +levLong, leverageShort: +levShort, direction, ...sl };
          if (period === 'CUSTOM') { body.dateFrom = dateFrom; body.dateTo = dateTo; }
          const res = await authFetch('/api/backtest', { method: 'POST', body: JSON.stringify(body) });
          const d = await res.json(); if (!res.ok) throw new Error(d.error);
          const best = d.results.find(r => r.period === d.bestPeriod);
          currentParamsRef.current = { asset, period, dateFrom, dateTo, maType, leverageLong: +levLong, leverageShort: +levShort, direction, bestPeriod: d.bestPeriod, bahReturn: d.buyAndHold.totalReturn };
          setSweepData(d);
          setChartTitle(`${maType} ${d.bestPeriod} — Best Equity Curve`);
          setChartSub(`${aLabel(asset)} · ${period === 'CUSTOM' ? `${dateFrom}→${dateTo}` : period} · L${levLong}x/S${levShort}x · ${direction} · best of ${d.results.length} periods`);
          drawChart(d.equityCurveBest, d.buyAndHold.equityCurve, `${maType} ${d.bestPeriod} (Best)`);
          setActiveRow(d.bestPeriod);
          setMetrics(buildMetrics({ ...best, buyAndHold: d.buyAndHold }));
          setTrades(d.tradesBest);
          setStatusMsg(`Done ${((Date.now()-t0)/1000).toFixed(1)}s · Best: ${maType} ${d.bestPeriod} · ${d.results.length} periods`);
        }
      } else if (mode === 'single') {
        const body = { signalAsset: sAsset, maType: sMaType, maPeriod: +sMaPeriod, tradeAsset: sTradeAsset, timePeriod: period, direction: sDir, leverageLong: +sLevLong, leverageShort: +sLevShort, ...sl };
        if (period === 'CUSTOM') { body.dateFrom = dateFrom; body.dateTo = dateTo; }
        const res = await authFetch('/api/backtest/single', { method: 'POST', body: JSON.stringify(body) });
        const d = await res.json(); if (!res.ok) throw new Error(d.error);
        const label = `${aLabel(sTradeAsset)} via ${aLabel(sAsset)} ${sMaType}${sMaPeriod}`;
        setChartTitle(label);
        setChartSub(`${period === 'CUSTOM' ? `${dateFrom}→${dateTo}` : period} · ${sDir} · L${sLevLong}x/S${sLevShort}x`);
        drawChart(d.equityCurve, d.buyAndHold.equityCurve, label);
        setMetrics(buildMetrics(d)); setTrades(d.trades);
        setStatusMsg(`Done ${((Date.now()-t0)/1000).toFixed(1)}s · ${d.tradeCount} trades`);
      } else if (mode === 'dual') {
        const body = { asset1: d1Asset, maType1: d1MaType, maPeriod1: +d1Period, asset2: d2Asset, maType2: d2MaType, maPeriod2: +d2Period, tradeAsset: dTrade, timePeriod: period, direction: dDir, leverageLong: +dLevLong, leverageShort: +dLevShort, ...sl };
        if (period === 'CUSTOM') { body.dateFrom = dateFrom; body.dateTo = dateTo; }
        const res = await authFetch('/api/backtest/dual', { method: 'POST', body: JSON.stringify(body) });
        const d = await res.json(); if (!res.ok) throw new Error(d.error);
        currentOptParamsRef.current = { asset1: d1Asset, maType1: d1MaType, maPeriod1: +d1Period, asset2: d2Asset, maType2: d2MaType, maPeriod2: +d2Period, tradeAsset: dTrade, timePeriod: period, dateFrom, dateTo, direction: dDir };
        const label = `${aLabel(dTrade)}: ${aLabel(d1Asset)} ${d1MaType}${d1Period} + ${aLabel(d2Asset)} ${d2MaType}${d2Period}`;
        setChartTitle(label);
        setChartSub(`${period === 'CUSTOM' ? `${dateFrom}→${dateTo}` : period} · ${dDir} · L${dLevLong}x/S${dLevShort}x`);
        drawChart(d.equityCurve, d.buyAndHold.equityCurve, label);
        setMetrics(buildMetrics(d)); setTrades(d.trades);
        setStatusMsg(`Done ${((Date.now()-t0)/1000).toFixed(1)}s · ${d.tradeCount} trades`);
      } else if (mode === 'crossover') {
        const body = { asset: xAsset, timePeriod: period, maType: xMaType, fastPeriod: +xFast, slowPeriod: +xSlow, direction: xDir, leverageLong: +xLevLong, leverageShort: +xLevShort, ...sl };
        if (period === 'CUSTOM') { body.dateFrom = dateFrom; body.dateTo = dateTo; }
        const res = await authFetch('/api/backtest/crossover', { method: 'POST', body: JSON.stringify(body) });
        const d = await res.json(); if (!res.ok) throw new Error(d.error);
        currentOptParamsRef.current = { asset: xAsset, maType: xMaType, timePeriod: period, dateFrom, dateTo, direction: xDir };
        const label = `${aLabel(xAsset)} ${xMaType} ${xFast}/${xSlow} Crossover`;
        setChartTitle(label);
        setChartSub(`${period === 'CUSTOM' ? `${dateFrom}→${dateTo}` : period} · ${xDir} · L${xLevLong}x/S${xLevShort}x`);
        drawChart(d.equityCurve, d.buyAndHold.equityCurve, label);
        setMetrics(buildMetrics(d)); setTrades(d.trades);
        setStatusMsg(`Done ${((Date.now()-t0)/1000).toFixed(1)}s · ${d.tradeCount} trades`);
      }
    } catch (e) {
      setError(e.message || 'Backtest failed');
    } finally {
      setLoading(false);
    }
  };

  // ── Sweep row click ────────────────────────────────────────────
  const loadRowCurve = async rowPeriod => {
    const p = currentParamsRef.current;
    if (!p || !token) return;
    const sl = getSLConfig();
    const qp = new URLSearchParams({ asset: p.asset, period: p.period, ma: rowPeriod, type: p.maType, leverageLong: p.leverageLong, leverageShort: p.leverageShort, direction: p.direction, ...sl });
    if (p.period === 'CUSTOM') { qp.set('dateFrom', p.dateFrom); qp.set('dateTo', p.dateTo); }
    try {
      const res = await authFetch(`/api/backtest/equity?${qp}`);
      const d = await res.json(); if (!res.ok) throw new Error(d.error);
      setChartTitle(`${p.maType} ${rowPeriod} — Equity Curve`);
      setChartSub(`${aLabel(p.asset)} · ${p.period === 'CUSTOM' ? `${p.dateFrom}→${p.dateTo}` : p.period} · L${p.leverageLong}x/S${p.leverageShort}x · ${p.direction}`);
      drawChart(d.equityCurve, d.buyAndHold, `${p.maType} ${rowPeriod}`);
      setActiveRow(rowPeriod);
      setMetrics(metricsFromCurve(d.equityCurve, d.buyAndHold, d.trades));
      setTrades(d.trades);
    } catch (e) { console.error('curve load failed', e); }
  };

  // ── Find Best (optimize) ───────────────────────────────────────
  const runOptimize = async () => {
    if (!token) return;
    setOptLoading(true); setError(''); setStatusMsg('Scanning…');
    const t0 = Date.now();
    try {
      let res, data, endpoint, body, optMode;
      if (mode === 'sweep') {
        endpoint = '/api/backtest/optimize';
        body = { asset, period, maType, direction, metric: optMetric };
        if (period === 'CUSTOM') { body.dateFrom = dateFrom; body.dateTo = dateTo; }
        optMode = 'sweep';
        currentOptParamsRef.current = { asset, period, dateFrom, dateTo, maType, direction };
      } else if (mode === 'dual') {
        endpoint = '/api/backtest/dual/optimize';
        body = { asset1: d1Asset, maType1: d1MaType, asset2: d2Asset, maType2: d2MaType, tradeAsset: dTrade, timePeriod: period, direction: dDir, metric: optMetric };
        if (period === 'CUSTOM') { body.dateFrom = dateFrom; body.dateTo = dateTo; }
        optMode = 'dual';
        currentOptParamsRef.current = { asset1: d1Asset, maType1: d1MaType, asset2: d2Asset, maType2: d2MaType, tradeAsset: dTrade, timePeriod: period, dateFrom, dateTo, direction: dDir };
      } else if (mode === 'crossover') {
        endpoint = '/api/backtest/crossover/optimize';
        body = { asset: xAsset, timePeriod: period, maType: xMaType, direction: xDir, metric: optMetric };
        if (period === 'CUSTOM') { body.dateFrom = dateFrom; body.dateTo = dateTo; }
        optMode = 'crossover';
        currentOptParamsRef.current = { asset: xAsset, maType: xMaType, timePeriod: period, dateFrom, dateTo, direction: xDir };
      } else return;

      res = await authFetch(endpoint, { method: 'POST', body: JSON.stringify(body) });
      data = await res.json(); if (!res.ok) throw new Error(data.error);

      const b = data.best;
      const elChartLabel = optMode === 'crossover' ? `Crossover F${b.fastPeriod}/S${b.slowPeriod} (Opt)` : optMode === 'dual' ? 'Dual Opt' : `${b.maType} ${b.period} (Opt)`;
      drawChart(b.equityCurve, data.buyAndHold.equityCurve, elChartLabel);
      setChartTitle(optMode === 'crossover' ? `${xMaType} Crossover — Fast ${b.fastPeriod} / Slow ${b.slowPeriod} (Opt)` : optMode === 'dual' ? `Trade ${aLabel(dTrade)} — Dual Optimized` : `${b.maType} ${b.period} — Optimized Best`);
      setChartSub(`${METRIC_LABELS[optMetric]} optimized · ${data.results.length} combinations`);
      setOptimizeResult({ data, mode: optMode, metric: optMetric, activeIdx: 0 });
      setStatusMsg(`Done ${((Date.now()-t0)/1000).toFixed(1)}s · Best: ${optMode === 'crossover' ? `F${b.fastPeriod}/S${b.slowPeriod}` : optMode === 'dual' ? `MA1=${b.maPeriod1} MA2=${b.maPeriod2}` : `${b.maType} ${b.period}`}`);
    } catch (e) {
      setError(e.message || 'Optimize failed');
    } finally {
      setOptLoading(false);
    }
  };

  // ── Find Best SL ───────────────────────────────────────────────
  const runFindBestSL = async () => {
    if (!token) return;
    setSlLoading(true); setError(''); setStatusMsg('Testing SL strategies…');
    const t0 = Date.now();
    try {
      let res, data, endpoint, body;
      if (mode === 'sweep') {
        const p = currentParamsRef.current;
        if (!p?.bestPeriod) throw new Error('Run a sweep first to identify the best MA period');
        endpoint = '/api/backtest/optimize-sl/sweep';
        body = { asset: p.asset, period: p.period, maType: p.maType, maPeriod: p.bestPeriod, leverageLong: p.leverageLong, leverageShort: p.leverageShort, direction: p.direction, metric: optMetric };
        if (p.period === 'CUSTOM') { body.dateFrom = p.dateFrom; body.dateTo = p.dateTo; }
      } else if (mode === 'dual') {
        endpoint = '/api/backtest/optimize-sl/dual';
        body = { asset1: d1Asset, maType1: d1MaType, maPeriod1: +d1Period, asset2: d2Asset, maType2: d2MaType, maPeriod2: +d2Period, tradeAsset: dTrade, timePeriod: period, direction: dDir, leverageLong: +dLevLong, leverageShort: +dLevShort, metric: optMetric };
        if (period === 'CUSTOM') { body.dateFrom = dateFrom; body.dateTo = dateTo; }
      } else if (mode === 'crossover') {
        endpoint = '/api/backtest/optimize-sl/crossover';
        body = { asset: xAsset, timePeriod: period, maType: xMaType, fastPeriod: +xFast, slowPeriod: +xSlow, direction: xDir, leverageLong: +xLevLong, leverageShort: +xLevShort, metric: optMetric };
        if (period === 'CUSTOM') { body.dateFrom = dateFrom; body.dateTo = dateTo; }
      } else return;

      res = await authFetch(endpoint, { method: 'POST', body: JSON.stringify(body) });
      data = await res.json(); if (!res.ok) throw new Error(data.error);

      const b = data.best;
      drawChart(b.equityCurve, data.buyAndHold.equityCurve, `SL: ${b.slLabel}`);
      setChartTitle(`Best Stop Loss: ${b.slLabel}`);
      setChartSub(`${METRIC_LABELS[optMetric]} optimized · ${data.results.length} SL types tested`);
      setSlResult({ data, metric: optMetric, activeIdx: 0 });
      setStatusMsg(`Done ${((Date.now()-t0)/1000).toFixed(1)}s · Best SL: ${b.slLabel}`);
    } catch (e) {
      setError(e.message || 'SL optimize failed');
    } finally {
      setSlLoading(false);
    }
  };

  // ── Find Best Skip ─────────────────────────────────────────────
  const runFindBestSkip = async () => {
    if (!token) return;
    const p = currentParamsRef.current;
    if (!p?.bestPeriod) { setError('Run a sweep first to get a best MA period'); return; }
    setSkipLoading(true); setError(''); setStatusMsg('Scanning win-skip…');
    const t0 = Date.now();
    try {
      const sl = getSLConfig();
      const body = { asset: p.asset, period: p.period, maType: p.maType, maPeriod: p.bestPeriod, leverageLong: p.leverageLong, leverageShort: p.leverageShort, direction: p.direction, metric: optMetric, ...sl };
      if (p.period === 'CUSTOM') { body.dateFrom = p.dateFrom; body.dateTo = p.dateTo; }
      const res = await authFetch('/api/backtest/optimize-skip/sweep', { method: 'POST', body: JSON.stringify(body) });
      const data = await res.json(); if (!res.ok) throw new Error(data.error);
      const b = data.best;
      drawChart(b.equityCurve, data.buyAndHold.equityCurve, `Skip: ${b.label}`);
      setChartTitle(`Best Win-Skip: ${b.label}`);
      setChartSub(`${METRIC_LABELS[optMetric]} optimized · ${data.results.length} combinations`);
      setSkipResult({ data, metric: optMetric, activeIdx: 0 });
      setStatusMsg(`Done ${((Date.now()-t0)/1000).toFixed(1)}s · Best: ${b.label}`);
    } catch (e) {
      setError(e.message || 'Skip optimize failed');
    } finally {
      setSkipLoading(false);
    }
  };

  // ── Find Overall Best ──────────────────────────────────────────
  const runOverall = async () => {
    if (!token) return;
    setOverallLoading(true); setError(''); setStatusMsg('Scanning all assets…');
    const t0 = Date.now();
    const excludeAssets = ALL_ASSETS.map(a => a.v).filter(v => !oAssets[v]);
    try {
      let res, data, optMode;
      if (overallSub === 'sweep') {
        res = await authFetch('/api/backtest/overall-optimize', {
          method: 'POST', body: JSON.stringify({ period: oPeriod, maType: oMaType, direction: oDir, metric: oMetric, excludeAssets }),
        });
        data = await res.json(); if (!res.ok) throw new Error(data.error);
        currentOptParamsRef.current = { period: oPeriod, maType: oMaType, direction: oDir };
        optMode = 'overall-sweep';
        const b = data.best;
        drawChart(b.equityCurve, data.buyAndHold.equityCurve, `${aLabel(b.asset)} ${b.maType} ${b.period}`);
        setChartTitle(`${aLabel(b.asset)} ${b.maType} ${b.period} — Global Best`);
        setChartSub(`${oPeriod} · L${b.leverageLong}x/S${b.leverageShort}x · ${oDir}`);
      } else {
        res = await authFetch('/api/backtest/dual/overall-optimize', {
          method: 'POST', body: JSON.stringify({ maType1: odMaType1, maPeriod1: +odPeriod1, maType2: odMaType2, maPeriod2: +odPeriod2, timePeriod: oPeriod, direction: odDir, metric: odMetric, excludeAssets }),
        });
        data = await res.json(); if (!res.ok) throw new Error(data.error);
        currentOptParamsRef.current = { maType1: odMaType1, maPeriod1: +odPeriod1, maType2: odMaType2, maPeriod2: +odPeriod2, timePeriod: oPeriod, direction: odDir };
        optMode = 'overall-dual';
        const b = data.best;
        drawChart(b.equityCurve, data.buyAndHold.equityCurve, 'Global Dual Opt');
        setChartTitle(`Trade ${aLabel(b.tradeAsset)} — Global Best Dual`);
        setChartSub(`${aLabel(b.asset1)}+${aLabel(b.asset2)} · L${b.leverageLong}x/S${b.leverageShort}x`);
      }
      setOptimizeResult({ data, mode: optMode, metric: overallSub === 'sweep' ? oMetric : odMetric, activeIdx: 0 });
      const b = data.best;
      setStatusMsg(`Done ${((Date.now()-t0)/1000).toFixed(1)}s · Best: ${optMode === 'overall-sweep' ? `${aLabel(b.asset)} ${b.maType} ${b.period}` : `${aLabel(b.asset1)}+${aLabel(b.asset2)}→${aLabel(b.tradeAsset)}`}`);
    } catch (e) {
      setError(e.message || 'Overall optimize failed');
    } finally {
      setOverallLoading(false);
    }
  };

  // ── Optimize result row click ──────────────────────────────────
  const handleOptRowClick = async (idx, r) => {
    setOptimizeResult(prev => prev ? { ...prev, activeIdx: idx } : prev);
    const p = currentOptParamsRef.current;
    if (!p || !token) return;
    try {
      let res, d;
      const optMode = optimizeResult?.mode;
      if (optMode === 'sweep') {
        const qs = new URLSearchParams({ asset: p.asset, period: p.period, ma: r.period, type: p.maType, leverageLong: r.leverageLong, leverageShort: r.leverageShort, direction: p.direction });
        res = await authFetch(`/api/backtest/equity?${qs}`);
        d = await res.json();
        drawChart(d.equityCurve, d.buyAndHold, `${p.maType} ${r.period}`);
      } else if (optMode === 'dual') {
        const body = { asset1: p.asset1, maType1: p.maType1, maPeriod1: r.maPeriod1, asset2: p.asset2, maType2: p.maType2, maPeriod2: r.maPeriod2, tradeAsset: p.tradeAsset, timePeriod: p.timePeriod, direction: p.direction, leverageLong: r.leverageLong, leverageShort: r.leverageShort };
        if (p.timePeriod === 'CUSTOM') { body.dateFrom = p.dateFrom; body.dateTo = p.dateTo; }
        res = await authFetch('/api/backtest/dual', { method: 'POST', body: JSON.stringify(body) });
        d = await res.json();
        drawChart(d.equityCurve, d.buyAndHold.equityCurve, `MA1=${r.maPeriod1} MA2=${r.maPeriod2}`);
      } else if (optMode === 'crossover') {
        const body = { asset: p.asset, timePeriod: p.timePeriod, maType: p.maType, fastPeriod: r.fastPeriod, slowPeriod: r.slowPeriod, direction: p.direction, leverageLong: r.leverageLong, leverageShort: r.leverageShort };
        if (p.timePeriod === 'CUSTOM') { body.dateFrom = p.dateFrom; body.dateTo = p.dateTo; }
        res = await authFetch('/api/backtest/crossover', { method: 'POST', body: JSON.stringify(body) });
        d = await res.json();
        drawChart(d.equityCurve, d.buyAndHold.equityCurve, `Crossover F${r.fastPeriod}/S${r.slowPeriod}`);
      } else if (optMode === 'overall-sweep') {
        const qs = new URLSearchParams({ asset: r.asset, period: p.period, ma: r.period, type: r.maType, leverageLong: r.leverageLong, leverageShort: r.leverageShort, direction: p.direction });
        res = await authFetch(`/api/backtest/equity?${qs}`);
        d = await res.json();
        drawChart(d.equityCurve, d.buyAndHold, `${aLabel(r.asset)} ${r.maType} ${r.period}`);
      } else if (optMode === 'overall-dual') {
        const body = { asset1: r.asset1, maType1: p.maType1, maPeriod1: r.maPeriod1, asset2: r.asset2, maType2: p.maType2, maPeriod2: r.maPeriod2, tradeAsset: r.tradeAsset, timePeriod: p.timePeriod, direction: p.direction, leverageLong: r.leverageLong, leverageShort: r.leverageShort };
        res = await authFetch('/api/backtest/dual', { method: 'POST', body: JSON.stringify(body) });
        d = await res.json();
        drawChart(d.equityCurve, d.buyAndHold.equityCurve, `${aLabel(r.asset1)}+${aLabel(r.asset2)}→${aLabel(r.tradeAsset)}`);
      }
    } catch (e) { console.error('opt row click failed', e); }
  };

  // ── SL result row click ────────────────────────────────────────
  const handleSLRowClick = async (idx, r) => {
    setSlResult(prev => prev ? { ...prev, activeIdx: idx } : prev);
    const p = currentParamsRef.current;
    const op = currentOptParamsRef.current;
    if (!token) return;
    try {
      let res, d;
      const slCfg = { slType: r.slType, slPct: r.slPct, hardSlType: r.hardSlType ?? 'none', hardSlPct: r.hardSlPct };
      if (mode === 'sweep' && p) {
        const qs = new URLSearchParams({ asset: p.asset, period: p.period, ma: p.bestPeriod, type: p.maType, leverageLong: p.leverageLong, leverageShort: p.leverageShort, direction: p.direction, ...slCfg });
        if (p.period === 'CUSTOM') { qs.set('dateFrom', p.dateFrom); qs.set('dateTo', p.dateTo); }
        res = await authFetch(`/api/backtest/equity?${qs}`);
        d = await res.json();
        drawChart(d.equityCurve, d.buyAndHold, `SL: ${r.slLabel}`);
        setTrades(d.trades);
      } else if (mode === 'dual') {
        const body = { asset1: d1Asset, maType1: d1MaType, maPeriod1: +d1Period, asset2: d2Asset, maType2: d2MaType, maPeriod2: +d2Period, tradeAsset: dTrade, timePeriod: period, direction: dDir, leverageLong: +dLevLong, leverageShort: +dLevShort, ...slCfg };
        if (period === 'CUSTOM') { body.dateFrom = dateFrom; body.dateTo = dateTo; }
        res = await authFetch('/api/backtest/dual', { method: 'POST', body: JSON.stringify(body) });
        d = await res.json();
        drawChart(d.equityCurve, d.buyAndHold.equityCurve, `Dual SL: ${r.slLabel}`);
        setTrades(d.trades);
      } else if (mode === 'crossover' && op) {
        const body = { asset: op.asset, timePeriod: op.timePeriod, maType: op.maType, fastPeriod: +xFast, slowPeriod: +xSlow, direction: op.direction, leverageLong: +xLevLong, leverageShort: +xLevShort, ...slCfg };
        if (op.timePeriod === 'CUSTOM') { body.dateFrom = op.dateFrom; body.dateTo = op.dateTo; }
        res = await authFetch('/api/backtest/crossover', { method: 'POST', body: JSON.stringify(body) });
        d = await res.json();
        drawChart(d.equityCurve, d.buyAndHold.equityCurve, `Crossover SL: ${r.slLabel}`);
        setTrades(d.trades);
      }
    } catch (e) { console.error('SL row click failed', e); }
  };

  // ── Skip result row click ──────────────────────────────────────
  const handleSkipRowClick = async (idx, r) => {
    setSkipResult(prev => prev ? { ...prev, activeIdx: idx } : prev);
    const p = currentParamsRef.current;
    if (!p || !token) return;
    const sl = getSLConfig();
    const qs = new URLSearchParams({ asset: p.asset, period: p.period, ma: p.bestPeriod, type: p.maType, leverageLong: p.leverageLong, leverageShort: p.leverageShort, direction: p.direction, ...sl, winSkipCount: r.skipCount, winSkipMinPct: r.minWinPct });
    if (p.period === 'CUSTOM') { qs.set('dateFrom', p.dateFrom); qs.set('dateTo', p.dateTo); }
    try {
      const res = await authFetch(`/api/backtest/equity?${qs}`);
      const d = await res.json();
      drawChart(d.equityCurve, d.buyAndHold, `Skip: ${r.label}`);
      setTrades(d.trades);
    } catch (e) { console.error('skip row click failed', e); }
  };

  // ── Save preset ────────────────────────────────────────────────
  const savePreset = () => {
    const sl = getSLConfig();
    let preset, filename;
    if (mode === 'sweep') {
      const p = currentParamsRef.current;
      if (!p?.bestPeriod) { alert('Run a backtest first so the best MA period is known.'); return; }
      preset = { version: 1, source: 'walkforward-backtester', mode: 'sweep', asset: p.asset, maType: p.maType, maPeriod: p.bestPeriod, direction: p.direction, leverageLong: p.leverageLong, leverageShort: p.leverageShort, slType: sl.slType, slPct: sl.slPct };
      filename = `${p.asset.replace('USDT','').toLowerCase()}-${p.maType.toLowerCase()}${p.bestPeriod}-preset.json`;
    } else if (mode === 'dual') {
      preset = { version: 1, source: 'walkforward-backtester', mode: 'dual', asset1: d1Asset, maType1: d1MaType, maPeriod1: +d1Period, asset2: d2Asset, maType2: d2MaType, maPeriod2: +d2Period, tradeAsset: dTrade, direction: dDir, leverageLong: +dLevLong, leverageShort: +dLevShort, slType: sl.slType, slPct: sl.slPct };
      filename = `dual-${d1Asset.replace('USDT','').toLowerCase()}-${d2Asset.replace('USDT','').toLowerCase()}-preset.json`;
    } else if (mode === 'crossover') {
      preset = { version: 1, source: 'walkforward-backtester', mode: 'crossover', asset: xAsset, maType: xMaType, fastPeriod: +xFast, slowPeriod: +xSlow, direction: xDir, leverageLong: +xLevLong, leverageShort: +xLevShort, slType: sl.slType, slPct: sl.slPct };
      filename = `${xAsset.replace('USDT','').toLowerCase()}-crossover-${xFast}-${xSlow}-preset.json`;
    } else return;
    preset.exportedAt = new Date().toISOString();
    const blob = new Blob([JSON.stringify(preset, null, 2)], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url; a.download = filename; a.click();
    URL.revokeObjectURL(url);
  };

  // ── Sorted sweep table ─────────────────────────────────────────
  const sortedResults = React.useMemo(() => {
    if (!sweepData?.results) return [];
    return [...sweepData.results.map(r => ({ ...r, vsBah: r.totalReturn - sweepData.buyAndHold.totalReturn }))]
      .sort((a, b) => { const va = a[sortCol] ?? 0, vb = b[sortCol] ?? 0; return sortAsc ? va - vb : vb - va; });
  }, [sweepData, sortCol, sortAsc]);

  const onSortCol = col => { if (sortCol === col) setSortAsc(a => !a); else { setSortCol(col); setSortAsc(false); } };

  // ── Build optimize result rows ─────────────────────────────────
  const buildOptRows = (results, optMode, metric) => {
    return results.map(r => {
      let firstText;
      if (optMode === 'crossover') firstText = `Fast ${r.fastPeriod} / Slow ${r.slowPeriod}`;
      else if (optMode === 'sweep') firstText = `${r.maType} ${r.period}`;
      else if (optMode === 'overall-sweep') firstText = `${aLabel(r.asset)} ${r.maType} ${r.period}`;
      else if (optMode === 'dual') firstText = `${r.maPeriod1} / ${r.maPeriod2}`;
      else firstText = `${aLabel(r.asset1)}+${aLabel(r.asset2)}→${aLabel(r.tradeAsset)}`;
      return {
        _raw: r,
        cells: [
          { text: firstText },
          { text: `${r.leverageLong}x / ${r.leverageShort}x`, color: TH.inkSoft },
          { text: fmtPct(r.totalReturn), color: retColorStr(r.totalReturn) },
          { text: fmtPct(r.cagr), color: retColorStr(r.cagr) },
          { text: r.maxDrawdown?.toFixed(1) + '%', color: TH.red },
          { text: scoreDisplayStr(r, metric), color: r.score >= 0.5 ? TH.green : r.score >= 0 ? TH.inkSoft : TH.red },
          { text: r.tradeCount + (r.liquidated ? ' ⚠' : ''), color: r.liquidated ? TH.red : TH.inkSoft },
        ],
      };
    });
  };

  // ── Build SL result rows ───────────────────────────────────────
  const buildSLRows = (results, metric) => results.map(r => ({
    _raw: r,
    cells: [
      { text: r.slLabel },
      { text: fmtPct(r.totalReturn), color: retColorStr(r.totalReturn) },
      { text: fmtPct(r.cagr), color: retColorStr(r.cagr) },
      { text: r.maxDrawdown?.toFixed(1) + '%', color: TH.red },
      { text: scoreDisplayStr(r, metric), color: r.score >= 0.5 ? TH.green : r.score >= 0 ? TH.inkSoft : TH.red },
      { text: r.tradeCount + (r.liquidated ? ' ⚠' : ''), color: r.liquidated ? TH.red : TH.inkSoft },
    ],
  }));

  // ── Build skip result rows ─────────────────────────────────────
  const buildSkipRows = (results, metric) => results.map(r => ({
    _raw: r,
    cells: [
      { text: r.label },
      { text: fmtPct(r.totalReturn), color: retColorStr(r.totalReturn) },
      { text: fmtPct(r.cagr), color: retColorStr(r.cagr) },
      { text: r.maxDrawdown?.toFixed(1) + '%', color: TH.red },
      { text: scoreDisplayStr(r, metric), color: r.score >= 0.5 ? TH.green : r.score >= 0 ? TH.inkSoft : TH.red },
      { text: r.tradeCount + (r.liquidated ? ' ⚠' : ''), color: r.liquidated ? TH.red : TH.inkSoft },
    ],
  }));

  // ── Controls ───────────────────────────────────────────────────
  const PeriodSection = (
    <>
      <SField label="Period">
        <SSelect value={period} onChange={setPeriod}>
          <option value="3Y">3 Years</option>
          <option value="5Y">5 Years</option>
          <option value="10Y">10 Years</option>
          <option value="CUSTOM">Custom range</option>
        </SSelect>
      </SField>
      {period === 'CUSTOM' && (
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginBottom: 12 }}>
          <SField label="From"><SInput type="date" value={dateFrom} onChange={setDateFrom}/></SField>
          <SField label="To"><SInput type="date" value={dateTo} onChange={setDateTo}/></SField>
        </div>
      )}
    </>
  );

  const SLSection = (
    <div style={{ marginTop: 12 }}>
      <div onClick={() => setSlOpen(o => !o)} style={{ fontFamily: TH.mono, fontSize: 10, color: TH.muted, letterSpacing: 1.5, cursor: 'pointer', display: 'flex', justifyContent: 'space-between', padding: '8px 0', borderTop: `1px solid ${TH.rule}` }}>
        <span>STOP-LOSS &amp; WIN-SKIP</span><span>{slOpen ? '▲' : '▼'}</span>
      </div>
      {slOpen && (
        <>
          <SField label="Trailing Stop">
            <SSelect value={slType} onChange={setSlType}>
              <option value="none">None</option>
              <option value="breakeven">Break-even</option>
              <option value="fixed">Fixed %</option>
            </SSelect>
          </SField>
          {slType === 'fixed' && <SField label="Trailing %"><SInput type="number" value={slPct} onChange={setSlPct} min="0.1" max="30" step="0.1"/></SField>}
          <SField label="Hard Stop">
            <SSelect value={hardSlType} onChange={setHardSlType}>
              <option value="none">None</option>
              <option value="fixed">Fixed %</option>
            </SSelect>
          </SField>
          {hardSlType === 'fixed' && <SField label="Hard Stop %"><SInput type="number" value={hardSlPct} onChange={setHardSlPct} min="0.1" max="30" step="0.1"/></SField>}
          <div style={{ borderTop: `1px solid ${TH.rule}`, paddingTop: 10, marginTop: 6 }}>
            <SLabel>Skip after win</SLabel>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr auto 1fr auto', gap: 6, alignItems: 'center', marginBottom: 12 }}>
              <SInput type="number" value={winSkipCount} onChange={setWinSkipCount} min="0" max="20" step="1"/>
              <span style={{ fontFamily: TH.mono, fontSize: 10, color: TH.muted }}>trades after</span>
              <SInput type="number" value={winSkipMinPct} onChange={setWinSkipMinPct} min="0" max="1000" step="1"/>
              <span style={{ fontFamily: TH.mono, fontSize: 10, color: TH.muted }}>% win</span>
            </div>
          </div>
        </>
      )}
    </div>
  );

  const showOptButtons = (mode === 'sweep' || mode === 'dual' || mode === 'crossover');
  const showSkipBtn = mode === 'sweep';
  const showRunBtn = mode !== 'overall';

  const controls = {
    sweep: (
      <>
        {PeriodSection}
        <SField label="Asset"><AssetSelect value={asset} onChange={setAsset}/></SField>
        <SField label="MA Type"><MaTypeSel value={maType} onChange={setMaType}/></SField>
        <LevPair longVal={levLong} onLong={v => setLevLong(+v||1)} shortVal={levShort} onShort={v => setLevShort(+v||1)}/>
        <SField label="Direction"><DirSel value={direction} onChange={setDirection}/></SField>
        <SField label="Specific Period (blank = sweep all)"><SInput type="number" value={specificMA} onChange={setSpecificMA} placeholder="4 – 240" min="4" max="240"/></SField>
        {SLSection}
      </>
    ),
    single: (
      <>
        {PeriodSection}
        <AccentLabel>SIGNAL ASSET</AccentLabel>
        <SField label="Asset"><AssetSelect value={sAsset} onChange={setSAsset}/></SField>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginBottom: 12 }}>
          <SField label="MA Type"><MaTypeSel value={sMaType} onChange={setSMaType}/></SField>
          <SField label="Period"><SInput type="number" value={sMaPeriod} onChange={setSMaPeriod} min="4" max="240"/></SField>
        </div>
        <AccentLabel>TRADE ASSET</AccentLabel>
        <SField label="Asset"><AssetSelect value={sTradeAsset} onChange={setSTradeAsset}/></SField>
        <SField label="Direction"><DirSel value={sDir} onChange={setSDir}/></SField>
        <LevPair longVal={sLevLong} onLong={v => setSLevLong(+v||1)} shortVal={sLevShort} onShort={v => setSLevShort(+v||1)}/>
        {SLSection}
      </>
    ),
    dual: (
      <>
        {PeriodSection}
        <AccentLabel>CONDITION 1</AccentLabel>
        <SField label="Asset"><AssetSelect value={d1Asset} onChange={setD1Asset}/></SField>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginBottom: 12 }}>
          <SField label="MA Type"><MaTypeSel value={d1MaType} onChange={setD1MaType}/></SField>
          <SField label="Period"><SInput type="number" value={d1Period} onChange={setD1Period} min="4" max="240"/></SField>
        </div>
        <div style={{ fontFamily: TH.mono, fontSize: 10, color: TH.muted, textAlign: 'center', padding: '5px 0', borderTop: `1px solid ${TH.rule}`, borderBottom: `1px solid ${TH.rule}`, marginBottom: 12, letterSpacing: 1.5 }}>— AND —</div>
        <AccentLabel>CONDITION 2</AccentLabel>
        <SField label="Asset"><AssetSelect value={d2Asset} onChange={setD2Asset}/></SField>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginBottom: 12 }}>
          <SField label="MA Type"><MaTypeSel value={d2MaType} onChange={setD2MaType}/></SField>
          <SField label="Period"><SInput type="number" value={d2Period} onChange={setD2Period} min="4" max="240"/></SField>
        </div>
        <AccentLabel>TRADE ASSET</AccentLabel>
        <SField label="Asset"><AssetSelect value={dTrade} onChange={setDTrade}/></SField>
        <SField label="Direction"><DirSel value={dDir} onChange={setDDir}/></SField>
        <LevPair longVal={dLevLong} onLong={v => setDLevLong(+v||1)} shortVal={dLevShort} onShort={v => setDLevShort(+v||1)}/>
        {SLSection}
      </>
    ),
    crossover: (
      <>
        {PeriodSection}
        <SField label="Asset"><AssetSelect value={xAsset} onChange={setXAsset}/></SField>
        <SField label="MA Type"><MaTypeSel value={xMaType} onChange={setXMaType}/></SField>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginBottom: 12 }}>
          <SField label="Fast Period"><SInput type="number" value={xFast} onChange={setXFast} min="2" max="239"/></SField>
          <SField label="Slow Period"><SInput type="number" value={xSlow} onChange={setXSlow} min="3" max="240"/></SField>
        </div>
        <SField label="Direction"><DirSel value={xDir} onChange={setXDir}/></SField>
        <LevPair longVal={xLevLong} onLong={v => setXLevLong(+v||1)} shortVal={xLevShort} onShort={v => setXLevShort(+v||1)}/>
        {SLSection}
      </>
    ),
    overall: (
      <>
        <div style={{ display: 'flex', gap: 0, marginBottom: 14, border: `1px solid ${TH.rule}` }}>
          {[['sweep','MA Sweep'],['dual','Dual Cond']].map(([k,l]) => (
            <div key={k} onClick={() => setOverallSub(k)} style={{ flex: 1, padding: '9px 0', textAlign: 'center', fontFamily: TH.mono, fontSize: 10, letterSpacing: 1.2, textTransform: 'uppercase', cursor: 'pointer', background: overallSub === k ? TH.accent : 'transparent', color: overallSub === k ? TH.bg : TH.inkSoft }}>{l}</div>
          ))}
        </div>

        <SField label="Include Assets">
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 5 }}>
            {ALL_ASSETS.map(a => (
              <label key={a.v} style={{ display: 'flex', alignItems: 'center', gap: 4, fontFamily: TH.mono, fontSize: 10, color: oAssets[a.v] ? TH.ink : TH.muted, cursor: 'pointer', background: oAssets[a.v] ? TH.panelHi : TH.bg, border: `1px solid ${oAssets[a.v] ? TH.accentDim : TH.rule}`, padding: '3px 7px' }}>
                <input type="checkbox" checked={!!oAssets[a.v]} onChange={e => setOAssets(prev => ({ ...prev, [a.v]: e.target.checked }))} style={{ display: 'none' }}/>
                {a.l.split(' — ')[0]}
              </label>
            ))}
          </div>
        </SField>

        <SField label="Period">
          <SSelect value={oPeriod} onChange={setOPeriod}>
            <option value="3Y">3 Years</option>
            <option value="5Y">5 Years</option>
            <option value="10Y">10 Years</option>
          </SSelect>
        </SField>

        {overallSub === 'sweep' ? (
          <>
            <SField label="MA Type"><MaTypeSel value={oMaType} onChange={setOMaType}/></SField>
            <SField label="Direction"><DirSel value={oDir} onChange={setODir}/></SField>
            <SField label="Optimize By"><MetricSel value={oMetric} onChange={setOMetric}/></SField>
          </>
        ) : (
          <>
            <AccentLabel>CONDITION 1</AccentLabel>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginBottom: 12 }}>
              <SField label="MA Type"><MaTypeSel value={odMaType1} onChange={setOdMaType1}/></SField>
              <SField label="Period"><SInput type="number" value={odPeriod1} onChange={setOdPeriod1} min="4" max="240"/></SField>
            </div>
            <AccentLabel>CONDITION 2</AccentLabel>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8, marginBottom: 12 }}>
              <SField label="MA Type"><MaTypeSel value={odMaType2} onChange={setOdMaType2}/></SField>
              <SField label="Period"><SInput type="number" value={odPeriod2} onChange={setOdPeriod2} min="4" max="240"/></SField>
            </div>
            <SField label="Direction"><DirSel value={odDir} onChange={setOdDir}/></SField>
            <SField label="Optimize By"><MetricSel value={odMetric} onChange={setOdMetric}/></SField>
          </>
        )}
        <RunBtn onClick={runOverall} loading={overallLoading} disabled={!token}>
          {overallLoading ? 'Scanning…' : 'Find Overall Best →'}
        </RunBtn>
      </>
    ),
  };

  // ── Metrics panel ──────────────────────────────────────────────
  const metricsPanel = metrics ? (
    <>
      <MetricRow label="TOTAL RETURN" value={fmtPct(metrics.totalReturn)} color={retColor(metrics.totalReturn)}/>
      <MetricRow label="CAGR"         value={fmtPct(metrics.cagr)}        color={retColor(metrics.cagr)}/>
      <MetricRow label="MAX DRAWDOWN" value={metrics.maxDrawdown.toFixed(2) + '%'} color={TH.red}/>
      <MetricRow label="SHARPE"       value={metrics.sharpe !== null && metrics.sharpe !== undefined ? metrics.sharpe.toFixed(3) : '—'} color={metrics.sharpe == null ? TH.muted : metrics.sharpe >= 1 ? TH.green : TH.inkSoft}/>
      <MetricRow label="VS BUY &amp; HOLD" value={fmtPct(metrics.vsBah)} color={retColor(metrics.vsBah)}/>
      <MetricRow label="TRADES"       value={metrics.tradeCount + (metrics.liquidated ? ' ⚠' : '')} color={metrics.liquidated ? TH.red : TH.ink}/>
    </>
  ) : (
    <div style={{ fontFamily: TH.mono, fontSize: 10, color: TH.muted, lineHeight: 2, marginTop: 12 }}>
      Run a backtest<br/>to see metrics
    </div>
  );

  // ── Sweep table ────────────────────────────────────────────────
  const SWEEP_COLS = [
    { k:'period',      l:'MA' },
    { k:'totalReturn', l:'Return' },
    { k:'vsBah',       l:'vs B&H' },
    { k:'cagr',        l:'CAGR' },
    { k:'maxDrawdown', l:'Max DD' },
    { k:'sharpe',      l:'Sharpe' },
    { k:'tradeCount',  l:'Trades' },
  ];

  const sweepTable = mode === 'sweep' && sweepData ? (
    <div style={{ marginTop: 14, border: `1px solid ${TH.rule}`, background: TH.panel }}>
      <div style={{ padding: '9px 14px', borderBottom: `1px solid ${TH.rule}`, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
        <div style={{ fontFamily: TH.mono, fontSize: 10, color: TH.accent, letterSpacing: 2 }}>◉ {sortedResults.length} PERIODS — click row to view curve</div>
      </div>
      <div style={{ overflowX: 'auto' }}>
        <table style={{ width: '100%', borderCollapse: 'collapse', fontFamily: TH.mono, fontSize: 11 }}>
          <thead>
            <tr style={{ borderBottom: `1px solid ${TH.rule}` }}>
              {SWEEP_COLS.map(c => (
                <th key={c.k} onClick={() => onSortCol(c.k)} style={{ padding: '7px 10px', textAlign: c.k === 'period' ? 'left' : 'right', color: sortCol === c.k ? TH.accent : TH.muted, fontSize: 10, letterSpacing: 1.2, cursor: 'pointer', whiteSpace: 'nowrap', fontWeight: 400, userSelect: 'none' }}>
                  {c.l}{sortCol === c.k ? (sortAsc ? ' ↑' : ' ↓') : ''}
                </th>
              ))}
            </tr>
          </thead>
          <tbody>
            {sortedResults.map(r => {
              const isBest = r.period === sweepData.bestPeriod;
              const isActive = r.period === activeRow;
              return (
                <tr key={r.period} onClick={() => loadRowCurve(r.period)}
                  style={{ borderBottom: `1px solid ${TH.rule}`, cursor: 'pointer', background: isActive ? TH.panelHi : isBest ? 'rgba(212,168,75,0.05)' : 'transparent' }}
                  onMouseEnter={e => { if (!isActive) e.currentTarget.style.background = TH.panelHi; }}
                  onMouseLeave={e => { e.currentTarget.style.background = isActive ? TH.panelHi : isBest ? 'rgba(212,168,75,0.05)' : 'transparent'; }}>
                  <td style={{ padding: '8px 10px', color: isBest ? TH.accent : TH.ink, fontWeight: 600, borderLeft: isActive ? `2px solid ${TH.accent}` : '2px solid transparent' }}>
                    {r.maType || maType} {r.period}
                    {isBest && <span style={{ marginLeft: 8, fontSize: 9, background: TH.accent, color: TH.bg, padding: '1px 5px', letterSpacing: 1 }}>BEST</span>}
                  </td>
                  <td style={{ padding: '8px 10px', textAlign: 'right', color: retColorStr(r.totalReturn) }}>{fmtPct(r.totalReturn)}</td>
                  <td style={{ padding: '8px 10px', textAlign: 'right', color: r.vsBah >= 0 ? TH.inkSoft : TH.red }}>{fmtPct(r.vsBah)}</td>
                  <td style={{ padding: '8px 10px', textAlign: 'right', color: retColorStr(r.cagr) }}>{fmtPct(r.cagr)}</td>
                  <td style={{ padding: '8px 10px', textAlign: 'right', color: TH.red }}>{r.maxDrawdown?.toFixed(2)}%</td>
                  <td style={{ padding: '8px 10px', textAlign: 'right', color: r.sharpe >= 1 ? TH.green : r.sharpe >= 0 ? TH.inkSoft : TH.red }}>{r.sharpe?.toFixed(3)}</td>
                  <td style={{ padding: '8px 10px', textAlign: 'right', color: TH.inkSoft }}>{r.tradeCount}</td>
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </div>
  ) : null;

  // ── Result cards ───────────────────────────────────────────────
  const ResultCard = ({ title, subtitle, badge, bestGrid, tableHead, tableRows, activeIdx, onRowClick }) => (
    <div style={{ border: `1px solid ${TH.rule}`, marginTop: 14, background: TH.panel }}>
      <div style={{ padding: '10px 14px', borderBottom: `1px solid ${TH.rule}`, display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexWrap: 'wrap', gap: 8 }}>
        <div>
          <div style={{ fontFamily: TH.mono, fontSize: 11, color: TH.accent, letterSpacing: 1.5, marginBottom: 2 }}>{title}</div>
          {subtitle && <div style={{ fontFamily: TH.mono, fontSize: 10, color: TH.muted }}>{subtitle}</div>}
        </div>
        {badge && <div style={{ fontFamily: TH.mono, fontSize: 9, letterSpacing: 1.5, background: TH.accentDim, color: TH.accent, padding: '3px 9px' }}>{badge}</div>}
      </div>
      <div style={{ padding: '10px 14px' }}>
        {bestGrid}
      </div>
      <ResultTable head={tableHead} rows={tableRows} activeIdx={activeIdx} onRowClick={onRowClick}/>
    </div>
  );

  const MODES = [['sweep','MA Sweep'],['single','Single MA'],['dual','Dual Condition'],['crossover','Crossover'],['overall','Find Overall Best']];

  return (
    <div style={{ background: TH.bg, color: TH.ink, minHeight: '100vh' }}>
      <Ticker/>

      {/* Breadcrumb / status */}
      <div style={{ padding: '10px 20px', borderBottom: `1px solid ${TH.rule}`, display: 'flex', justifyContent: 'space-between', alignItems: 'center', fontFamily: TH.mono, fontSize: 11, color: TH.muted, letterSpacing: 1, flexWrap: 'wrap', gap: 8 }}>
        <span>TOOLS / <span style={{ color: TH.ink }}>BACKTESTER</span></span>
        <div style={{ display: 'flex', gap: 14, alignItems: 'center' }}>
          {statusMsg && <span style={{ color: TH.accent }}>{statusMsg}</span>}
          {error     && <span style={{ color: TH.red }}>⚠ {error}</span>}
          {authErr   && <span style={{ color: TH.red }}>⚠ Cannot reach API</span>}
        </div>
      </div>

      {/* Mode tabs */}
      <div style={{ display: 'flex', borderBottom: `1px solid ${TH.rule}`, background: TH.panel, overflowX: 'auto' }}>
        {MODES.map(([k, l]) => (
          <div key={k} onClick={() => changeMode(k)}
            style={{ padding: '11px 18px', fontFamily: TH.mono, fontSize: 10, letterSpacing: 1.5, textTransform: 'uppercase', cursor: 'pointer', whiteSpace: 'nowrap', color: mode === k ? TH.accent : TH.inkSoft, borderBottom: mode === k ? `2px solid ${TH.accent}` : '2px solid transparent', marginBottom: -1, flexShrink: 0 }}>
            {l}
          </div>
        ))}
      </div>

      {/* Layout */}
      <div style={wide ? { display: 'grid', gridTemplateColumns: '280px 1fr 190px', minHeight: 500 } : { display: 'flex', flexDirection: 'column' }}>

        {/* LEFT: controls */}
        <div style={{ borderRight: wide ? `1px solid ${TH.rule}` : 'none', borderBottom: wide ? 'none' : `1px solid ${TH.rule}`, padding: '16px 14px', background: TH.panel, overflowY: 'auto' }}>
          <div style={{ fontFamily: TH.mono, fontSize: 10, color: TH.accent, letterSpacing: 2, marginBottom: 14 }}>◉ INPUTS</div>
          {controls[mode]}

          {showRunBtn && (
            <div style={{ marginTop: 14 }}>
              <RunBtn onClick={run} loading={loading} disabled={!token}>
                {!token ? 'Connecting…' : 'Run Backtest →'}
              </RunBtn>
            </div>
          )}

          {showOptButtons && (
            <div style={{ marginTop: 10 }}>
              <SField label="Optimize By"><MetricSel value={optMetric} onChange={setOptMetric}/></SField>
              <SecBtn onClick={runOptimize} loading={optLoading} disabled={!token}>Find Best Leverage</SecBtn>
              <SecBtn onClick={runFindBestSL} loading={slLoading} disabled={!token} style={{ borderColor: '#2d6b3a', color: slLoading ? TH.muted : '#4ade80' }}>Find Best SL</SecBtn>
              {showSkipBtn && <SecBtn onClick={runFindBestSkip} loading={skipLoading} disabled={!token} style={{ borderColor: '#2d4a6b', color: skipLoading ? TH.muted : '#7ab3d4' }}>Find Best Skip</SecBtn>}
              <SecBtn onClick={savePreset} disabled={false} style={{ marginTop: 4 }}>↓ Save Preset</SecBtn>
            </div>
          )}
          {!showOptButtons && mode !== 'overall' && (
            <SecBtn onClick={savePreset} style={{ marginTop: 10 }}>↓ Save Preset</SecBtn>
          )}
        </div>

        {/* CENTER: chart + results */}
        <div style={{ padding: 18, overflowY: 'auto', minWidth: 0 }}>
          <div style={{ marginBottom: 8 }}>
            <div style={{ fontFamily: TH.mono, fontSize: 11, color: TH.accent, letterSpacing: 1.5 }}>{chartTitle || 'EQUITY CURVE'}</div>
            {chartSub && <div style={{ fontFamily: TH.mono, fontSize: 10, color: TH.muted, marginTop: 2 }}>{chartSub}</div>}
          </div>

          {/* Chart */}
          <div style={{ height: 300, background: TH.panel, border: `1px solid ${TH.rule}`, padding: 12, position: 'relative' }}>
            {!chartTitle && !loading && !optLoading && !slLoading && !skipLoading && !overallLoading && (
              <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 10 }}>
                <svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke={TH.ruleHi} strokeWidth="1.5"><path d="M3 3v18h18"/><path d="M7 14l4-4 4 4 5-6"/></svg>
                <div style={{ fontFamily: TH.mono, fontSize: 10, color: TH.muted, letterSpacing: 1 }}>Configure and run a backtest</div>
              </div>
            )}
            {(loading || optLoading || slLoading || skipLoading || overallLoading) && (
              <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', gap: 12 }}>
                <div style={{ width: 26, height: 26, border: `2px solid ${TH.rule}`, borderTopColor: TH.accent, borderRadius: '50%', animation: 'wf-spin 0.8s linear infinite' }}/>
                <div style={{ fontFamily: TH.mono, fontSize: 10, color: TH.muted, letterSpacing: 1 }}>Computing…</div>
              </div>
            )}
            <canvas ref={canvasRef} style={{ display: chartTitle && !loading && !optLoading && !slLoading && !skipLoading && !overallLoading ? 'block' : 'none', width: '100%', height: '100%' }}/>
          </div>

          {/* Optimize result */}
          {optimizeResult && (() => {
            const { data, mode: optMode, metric, activeIdx } = optimizeResult;
            const b = data.best;
            const mLabel = METRIC_LABELS[metric] || 'Score';
            let bestCells;
            if (optMode === 'crossover') bestCells = [{ label:'Fast MA', value: b.fastPeriod }, { label:'Slow MA', value: b.slowPeriod }, { label:'Long Lev', value: b.leverageLong+'x' }, { label:'Short Lev', value: b.leverageShort+'x' }, { label:'Return', value: fmtPct(b.totalReturn), color: retColorStr(b.totalReturn) }, { label:'CAGR', value: fmtPct(b.cagr), color: retColorStr(b.cagr) }, { label:'Max DD', value: b.maxDrawdown?.toFixed(1)+'%', color: TH.red }, { label: mLabel, value: scoreDisplayStr(b, metric) }];
            else if (optMode === 'sweep') bestCells = [{ label:'MA Period', value: `${b.maType} ${b.period}` }, { label:'Long Lev', value: b.leverageLong+'x' }, { label:'Short Lev', value: b.leverageShort+'x' }, { label:'Return', value: fmtPct(b.totalReturn), color: retColorStr(b.totalReturn) }, { label:'CAGR', value: fmtPct(b.cagr), color: retColorStr(b.cagr) }, { label:'Max DD', value: b.maxDrawdown?.toFixed(1)+'%', color: TH.red }, { label: mLabel, value: scoreDisplayStr(b, metric) }];
            else if (optMode === 'overall-sweep') bestCells = [{ label:'Asset', value: aLabel(b.asset) }, { label:'MA Period', value: `${b.maType} ${b.period}` }, { label:'Long Lev', value: b.leverageLong+'x' }, { label:'Short Lev', value: b.leverageShort+'x' }, { label:'Return', value: fmtPct(b.totalReturn), color: retColorStr(b.totalReturn) }, { label:'CAGR', value: fmtPct(b.cagr), color: retColorStr(b.cagr) }, { label:'Max DD', value: b.maxDrawdown?.toFixed(1)+'%', color: TH.red }, { label: mLabel, value: scoreDisplayStr(b, metric) }];
            else if (optMode === 'dual') bestCells = [{ label:'MA 1', value: b.maPeriod1 }, { label:'MA 2', value: b.maPeriod2 }, { label:'Long Lev', value: b.leverageLong+'x' }, { label:'Short Lev', value: b.leverageShort+'x' }, { label:'Return', value: fmtPct(b.totalReturn), color: retColorStr(b.totalReturn) }, { label:'CAGR', value: fmtPct(b.cagr), color: retColorStr(b.cagr) }, { label:'Max DD', value: b.maxDrawdown?.toFixed(1)+'%', color: TH.red }, { label: mLabel, value: scoreDisplayStr(b, metric) }];
            else bestCells = [{ label:'Cond 1', value: `${aLabel(b.asset1)} ${b.maPeriod1}` }, { label:'Cond 2', value: `${aLabel(b.asset2)} ${b.maPeriod2}` }, { label:'Trade', value: aLabel(b.tradeAsset) }, { label:'Long Lev', value: b.leverageLong+'x' }, { label:'Return', value: fmtPct(b.totalReturn), color: retColorStr(b.totalReturn) }, { label:'CAGR', value: fmtPct(b.cagr), color: retColorStr(b.cagr) }, { label:'Max DD', value: b.maxDrawdown?.toFixed(1)+'%', color: TH.red }, { label: mLabel, value: scoreDisplayStr(b, metric) }];

            const tableHead = optMode === 'crossover' ? ['Fast / Slow','Leverage','Return','CAGR','Max DD',mLabel,'Trades'] :
              optMode === 'overall-sweep' ? ['Asset · MA','Leverage','Return','CAGR','Max DD',mLabel,'Trades'] :
              optMode === 'overall-dual' ? ['Cond1+Cond2→Trade','Leverage','Return','CAGR','Max DD',mLabel,'Trades'] :
              optMode === 'dual' ? ['MA1 / MA2','Leverage','Return','CAGR','Max DD',mLabel,'Trades'] :
              ['MA Period','Leverage','Return','CAGR','Max DD',mLabel,'Trades'];

            return (
              <ResultCard
                title="BEST COMBINATION"
                subtitle={`${mLabel} optimized · top ${data.results.length} results · click row to view curve`}
                badge={`${mLabel.toUpperCase()} OPTIMIZED`}
                bestGrid={<OptBestGrid cells={bestCells}/>}
                tableHead={tableHead}
                tableRows={buildOptRows(data.results, optMode, metric)}
                activeIdx={activeIdx}
                onRowClick={(i, r) => handleOptRowClick(i, r._raw)}
              />
            );
          })()}

          {/* SL result */}
          {slResult && (() => {
            const { data, metric, activeIdx } = slResult;
            const b = data.best;
            const mLabel = METRIC_LABELS[metric] || 'Score';
            const bestCells = [
              { label:'SL Type', value: b.slLabel },
              { label:'Return', value: fmtPct(b.totalReturn), color: retColorStr(b.totalReturn) },
              { label:'CAGR', value: fmtPct(b.cagr), color: retColorStr(b.cagr) },
              { label:'Max DD', value: b.maxDrawdown?.toFixed(1)+'%', color: TH.red },
              { label: mLabel, value: scoreDisplayStr(b, metric) },
              { label:'Trades', value: b.tradeCount },
            ];
            return (
              <ResultCard
                title="BEST STOP LOSS"
                subtitle={`${mLabel} optimized · ${data.results.length} SL types tested · click row to view curve`}
                badge={`${mLabel.toUpperCase()} OPTIMIZED`}
                bestGrid={<OptBestGrid cells={bestCells}/>}
                tableHead={['Stop Loss','Return','CAGR','Max DD',mLabel,'Trades']}
                tableRows={buildSLRows(data.results, metric)}
                activeIdx={activeIdx}
                onRowClick={(i, r) => handleSLRowClick(i, r._raw)}
              />
            );
          })()}

          {/* Skip result */}
          {skipResult && (() => {
            const { data, metric, activeIdx } = skipResult;
            const b = data.best;
            const mLabel = METRIC_LABELS[metric] || 'Score';
            const bestCells = [
              { label:'Skip Setting', value: b.label },
              { label:'Return', value: fmtPct(b.totalReturn), color: retColorStr(b.totalReturn) },
              { label:'CAGR', value: fmtPct(b.cagr), color: retColorStr(b.cagr) },
              { label:'Max DD', value: b.maxDrawdown?.toFixed(1)+'%', color: TH.red },
              { label: mLabel, value: scoreDisplayStr(b, metric) },
              { label:'Trades', value: b.tradeCount },
            ];
            return (
              <ResultCard
                title="BEST WIN-SKIP"
                subtitle={`${mLabel} optimized · ${data.results.length} combinations · click row to apply`}
                badge={`${mLabel.toUpperCase()} OPTIMIZED`}
                bestGrid={<OptBestGrid cells={bestCells}/>}
                tableHead={['Skip Setting','Return','CAGR','Max DD',mLabel,'Trades']}
                tableRows={buildSkipRows(data.results, metric)}
                activeIdx={activeIdx}
                onRowClick={(i, r) => handleSkipRowClick(i, r._raw)}
              />
            );
          })()}

          {sweepTable}
          <TradeLog trades={trades}/>
        </div>

        {/* RIGHT: metrics */}
        <div style={{ borderLeft: wide ? `1px solid ${TH.rule}` : 'none', borderTop: wide ? 'none' : `1px solid ${TH.rule}`, padding: '16px 14px', background: TH.panel, overflowY: 'auto' }}>
          <div style={{ fontFamily: TH.mono, fontSize: 10, color: TH.accent, letterSpacing: 2, marginBottom: 14 }}>◉ RESULTS</div>
          {metricsPanel}
        </div>
      </div>

      <Footer/>
      <style>{`@keyframes wf-spin { to { transform: rotate(360deg); } }`}</style>
    </div>
  );
}

Object.assign(window, { Backtester });
