// Cortex — Backtest Results page.
// Strategy variant selector → headline KPIs → equity curve + drawdowns →
// monthly returns heatmap + per-bucket attribution.

// Two-line chart: strategy vs benchmark, built from persisted backtest returns.
function EquityChart({ rows = [], height = 280 }) {
  if (!rows.length) {
    return (
      <svg viewBox={`0 0 1200 ${height}`} width="100%" style={{ display: 'block' }}>
        <text x="600" y={height / 2} textAnchor="middle" fontFamily={CX.mono} fontSize="11" fill={CX.faint} letterSpacing="0.1em">
          NO BACKTEST RETURNS
        </text>
      </svg>
    );
  }
  const a = [], b = [];
  let va = 100, vb = 100;
  rows.forEach(r => {
    va *= 1 + (Number(r.port) || 0);
    vb *= 1 + (Number(r.bench) || 0);
    a.push(va); b.push(vb);
  });
  const N = a.length;
  const all = [...a, ...b];
  const min = Math.min(...all), max = Math.max(...all);
  const W = 1200, H = height;
  const xy = (i, p) => [50 + (i / (N - 1)) * (W - 70), 20 + ((max - p) / (max - min)) * (H - 50)];
  const path = (vals) => vals.map((p, i) => { const [x, y] = xy(i, p); return `${i === 0 ? 'M' : 'L'}${x.toFixed(1)} ${y.toFixed(1)}`; }).join(' ');
  const area = path(a) + ` L${xy(N - 1, a[N - 1])[0]} ${H - 30} L${xy(0, a[0])[0]} ${H - 30} Z`;
  return (
    <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: 'block' }}>
      {[0, 0.25, 0.5, 0.75, 1].map((g, i) => (
        <line key={i} x1="50" x2={W - 20} y1={20 + g * (H - 50)} y2={20 + g * (H - 50)} stroke={CX.line} strokeDasharray={i === 0 || i === 4 ? '' : '2 4'} strokeWidth="1" />
      ))}
      <path d={area} fill={CX.accentVery} />
      <path d={path(b)} fill="none" stroke={CX.dim} strokeWidth="1.2" strokeDasharray="3 3" />
      <path d={path(a)} fill="none" stroke={CX.accent} strokeWidth="1.5" />
      {[max, (max+min)/2, min].map((v, i) => (
        <text key={i} x="44" y={[24, H / 2, H - 34][i]} textAnchor="end" fontFamily={CX.mono} fontSize="10" fill={CX.faint}>{v.toFixed(0)}</text>
      ))}
      {[0, Math.floor(N / 3), Math.floor((2 * N) / 3), N - 1].map((idx, i) => (
        <text key={i} x={50 + (idx / Math.max(1, N - 1)) * (W - 70)} y={H - 8} fontFamily={CX.mono} fontSize="10" fill={CX.faint} letterSpacing="0.6">
          {(rows[idx]?.date || '').slice(0, 4)}
        </text>
      ))}
      {/* legend */}
      <g transform={`translate(${W - 320}, 28)`}>
        <line x1="0" y1="6" x2="20" y2="6" stroke={CX.accent} strokeWidth="1.5" />
        <text x="26" y="10" fontFamily={CX.mono} fontSize="11" fill={CX.text}>cortex · long-only</text>
        <line x1="180" y1="6" x2="200" y2="6" stroke={CX.dim} strokeDasharray="3 3" strokeWidth="1.2" />
        <text x="206" y="10" fontFamily={CX.mono} fontSize="11" fill={CX.dim}>CARNEGIE NORDIC SC</text>
      </g>
    </svg>
  );
}

function DrawdownChart({ rows = [], height = 90 }) {
  if (!rows.length) return null;
  const N = rows.length;
  const pts = [];
  let high = 100, eq = 100;
  rows.forEach(r => {
    eq *= 1 + (Number(r.port) || 0);
    high = Math.max(high, eq);
    pts.push(-(1 - eq / high));
  });
  const min = Math.min(...pts);
  const W = 1200, H = height;
  const xy = (i, p) => [50 + (i / (N - 1)) * (W - 70), 8 + (p / min) * (H - 24)];
  const path = pts.map((p, i) => { const [x, y] = xy(i, p); return `${i === 0 ? 'M' : 'L'}${x.toFixed(1)} ${y.toFixed(1)}`; }).join(' ');
  const area = path + ` L${xy(N - 1, 0)[0]} 8 L${xy(0, 0)[0]} 8 Z`;
  return (
    <svg viewBox={`0 0 ${W} ${H}`} width="100%" style={{ display: 'block' }}>
      <line x1="50" x2={W - 20} y1="8" y2="8" stroke={CX.line} strokeWidth="1" />
      <path d={area} fill={CX.redDim} />
      <path d={path} fill="none" stroke={CX.red} strokeWidth="1" />
      <text x="44" y="12" textAnchor="end" fontFamily={CX.mono} fontSize="10" fill={CX.faint}>0%</text>
      <text x="44" y={H - 8} textAnchor="end" fontFamily={CX.mono} fontSize="10" fill={CX.red}>{(min * 100).toFixed(1)}%</text>
    </svg>
  );
}

function ReturnsHeatmap({ rows = [] }) {
  if (!rows.length) return null;
  const months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
  const byYear = {};
  rows.forEach(r => {
    const d = new Date(r.date);
    if (Number.isNaN(d.getTime())) return;
    const y = d.getUTCFullYear();
    const m = d.getUTCMonth();
    (byYear[y] ??= Array(12).fill(null))[m] = (Number(r.port) || 0) * 100;
  });
  const years = Object.keys(byYear).map(Number).sort((a, b) => a - b);
  const matrix = years.map(y => byYear[y]);
  function cellColor(v) {
    if (v === null) return 'transparent';
    if (v >= 0) return `rgba(123,229,164,${Math.min(0.5, Math.abs(v) / 14 * 0.6 + 0.05)})`;
    return `rgba(240,132,106,${Math.min(0.5, Math.abs(v) / 14 * 0.6 + 0.05)})`;
  }
  function textColor(v) {
    if (v === null) return CX.faint;
    return v >= 0 ? CX.accent : CX.red;
  }
  return (
    <div style={{ background: CX.panel, border: `1px solid ${CX.line}`, padding: '16px 18px' }}>
      <Mono style={{ fontSize: 10, color: CX.faint, letterSpacing: '0.16em' }}>MONTHLY RETURNS · % · STRATEGY</Mono>
      <div style={{ marginTop: 14, display: 'grid', gridTemplateColumns: '60px repeat(12, 1fr) 60px', gap: 4 }}>
        <div />
        {months.map(m => <Mono key={m} style={{ fontSize: 10, color: CX.faint, textAlign: 'center', letterSpacing: '0.06em' }}>{m}</Mono>)}
        <Mono style={{ fontSize: 10, color: CX.accent, textAlign: 'right', letterSpacing: '0.1em' }}>YTD</Mono>
        {years.map((y, yi) => {
          const ytd = matrix[yi].filter(x => x !== null).reduce((a, b) => a + b, 0);
          return (
            <React.Fragment key={y}>
              <Mono style={{ fontSize: 11, color: CX.dim, alignSelf: 'center' }}>{y}</Mono>
              {months.map((_, mi) => {
                const v = matrix[yi][mi];
                return (
                  <div key={mi} style={{
                    padding: '8px 0', textAlign: 'center', background: cellColor(v),
                    border: `1px solid ${v === null ? 'transparent' : 'rgba(255,255,255,0.04)'}`,
                  }}>
                    <Mono style={{ fontSize: 11, color: textColor(v) }}>{v === null ? '—' : (v >= 0 ? '+' : '') + v.toFixed(1)}</Mono>
                  </div>
                );
              })}
              <Mono style={{ fontSize: 11, color: ytd >= 0 ? CX.accent : CX.red, textAlign: 'right', alignSelf: 'center' }}>
                {ytd >= 0 ? '+' : ''}{ytd.toFixed(1)}
              </Mono>
            </React.Fragment>
          );
        })}
      </div>
    </div>
  );
}

function _pct(v) {
  if (v == null || Number.isNaN(Number(v))) return '—';
  const n = Number(v) * 100;
  return `${n >= 0 ? '+' : ''}${n.toFixed(1)}%`;
}

function _backtestStats(rows) {
  if (!rows.length) {
    return { total: null, benchTotal: null, avg: null, benchAvg: null, maxDrawdown: null, hitRate: null };
  }
  let eq = 1, bench = 1, high = 1, maxDrawdown = 0, hits = 0;
  let sum = 0, benchSum = 0;
  rows.forEach(r => {
    const pr = Number(r.port) || 0;
    const br = Number(r.bench) || 0;
    eq *= 1 + pr;
    bench *= 1 + br;
    high = Math.max(high, eq);
    maxDrawdown = Math.min(maxDrawdown, eq / high - 1);
    sum += pr;
    benchSum += br;
    if (pr > 0) hits += 1;
  });
  return {
    total: eq - 1,
    benchTotal: bench - 1,
    avg: sum / rows.length,
    benchAvg: benchSum / rows.length,
    maxDrawdown,
    hitRate: hits / rows.length * 100,
  };
}

function PageBacktest() {
  const variants = BACKTEST?.variants || [];
  const [variant, setVariant] = React.useState(BACKTEST?.current || variants[0]?.id || null);
  const returnsBy = BACKTEST?.returns_by_portfolio || {};
  const portfolioNames = Object.keys(returnsBy).filter(p => p.includes('_top'));
  const [portfolio, setPortfolio] = React.useState(portfolioNames[0] || Object.keys(returnsBy)[0] || '');
  const rows = returnsBy[portfolio] || [];
  const stats = _backtestStats(rows);
  const kpis = [
    ['Total return', _pct(stats.total), `bench ${_pct(stats.benchTotal)}`, stats.total >= stats.benchTotal ? CX.accent : CX.red, 'backtest_kpi_cagr'],
    ['Avg period', _pct(stats.avg), `bench ${_pct(stats.benchAvg)}`, stats.avg >= stats.benchAvg ? CX.accent : CX.red, 'backtest_kpi_cagr'],
    ['Max drawdown', _pct(stats.maxDrawdown), 'portfolio', stats.maxDrawdown > -0.25 ? CX.accent : CX.red, 'backtest_kpi_max_drawdown'],
    ['Hit rate', stats.hitRate == null ? '—' : `${stats.hitRate.toFixed(1)}%`, 'periods', CX.text, 'backtest_kpi_hit_rate'],
    ['Periods', String(rows.length), portfolio || 'none', CX.text, null],
    ['Run count', String(BACKTEST?.count || 0), 'persisted', CX.text, null],
  ];
  const buckets = portfolioNames.map(name => {
    const rs = returnsBy[name] || [];
    const avg = rs.length ? rs.reduce((a, r) => a + (Number(r.port) || 0), 0) / rs.length : null;
    return [name.replace('_top30_3m', ''), _pct(avg), avg == null ? 0 : avg * 10];
  });

  return (
    <div style={{ padding: '32px 32px 64px', overflowY: 'auto', height: '100%' }}>
      <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', marginBottom: 24 }}>
        <div>
          <Mono style={{ fontSize: 10, color: CX.accent, letterSpacing: '0.18em' }}>04 · /Q.BACKTEST</Mono>
          <h1 style={{ fontFamily: CX.display, fontSize: 40, fontWeight: 400, letterSpacing: '-0.02em', margin: '8px 0 4px', color: CX.textHi }}>
            Backtest results<span style={{ color: CX.accent }}>.</span>
          </h1>
          <Mono style={{ fontSize: 11, color: CX.faint, letterSpacing: '0.08em' }}>
            {variants.length} persisted runs · current portfolio {portfolio || 'none'}
          </Mono>
        </div>
        <div style={{ display: 'flex', gap: 4 }}>
          {portfolioNames.map(p => {
            const on = portfolio === p;
            return (
              <button key={p} onClick={() => setPortfolio(p)} style={{
                appearance: 'none', cursor: 'pointer',
                padding: '8px 14px',
                background: on ? CX.accentDim : 'transparent',
                color: on ? CX.accent : CX.dim,
                border: `1px solid ${on ? CX.accent : CX.lineHi}`,
                fontFamily: CX.mono, fontSize: 11, letterSpacing: '0.08em',
              }}>{p.replace('_top30_3m', '')}</button>
            );
          })}
        </div>
      </div>

      {/* variant strip */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 0, border: `1px solid ${CX.line}`, marginBottom: 24, background: CX.panel }}>
        {variants.map((v, i) => {
          const on = variant === v.id;
          return (
            <button key={v.id} onClick={() => setVariant(v.id)}
              style={{ appearance: 'none', cursor: 'pointer', textAlign: 'left',
                padding: '16px 20px', borderRight: i < 3 ? `1px solid ${CX.line}` : 'none',
                background: on ? CX.accentVery : 'transparent',
                borderTop: `2px solid ${on ? CX.accent : 'transparent'}`,
                marginTop: -1,
              }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
                <Mono style={{ fontSize: 10, color: on ? CX.accent : CX.faint, letterSpacing: '0.16em' }}>VARIANT</Mono>
                {v.live && <Mono style={{ fontSize: 9, color: CX.accent, letterSpacing: '0.16em' }}>● CURRENT</Mono>}
              </div>
              <div style={{ fontFamily: CX.display, fontSize: 15, color: on ? CX.textHi : CX.text, marginTop: 6 }}>{v.label}</div>
              <Mono style={{ fontSize: 11, color: CX.dim, marginTop: 4, display: 'block' }}>built {v.date}</Mono>
            </button>
          );
        })}
      </div>

      {/* KPIs */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(6, 1fr)', gap: 1, background: CX.line, border: `1px solid ${CX.line}`, marginBottom: 24 }}>
        {kpis.map((k, i) => (
          <div key={i} style={{ background: CX.panel, padding: '18px 18px 20px' }}>
            <div style={{ display: 'flex', alignItems: 'center' }}>
              <Mono style={{ fontSize: 10, color: CX.faint, letterSpacing: '0.16em' }}>{k[0].toUpperCase()}</Mono>
              {k[4] && <Help id={k[4]} />}
            </div>
            <Mono style={{ display: 'block', fontSize: 28, color: k[3], marginTop: 10, letterSpacing: '-0.01em' }}>{k[1]}</Mono>
            <Mono style={{ display: 'block', fontSize: 11, color: CX.dim, marginTop: 4, letterSpacing: '0.04em' }}>{k[2]}</Mono>
          </div>
        ))}
      </div>

      {/* equity curve */}
      <div style={{ background: CX.panel, border: `1px solid ${CX.line}`, padding: '20px 22px', marginBottom: 24 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 8 }}>
          <div style={{ display: 'flex', alignItems: 'center' }}>
            <Mono style={{ fontSize: 10, color: CX.faint, letterSpacing: '0.16em' }}>EQUITY CURVE · INDEXED TO 100</Mono>
            <Help id="backtest_equity_curve" />
          </div>
          <Mono style={{ fontSize: 11, color: CX.dim }}>{rows[0]?.date || '—'} — {rows[rows.length - 1]?.date || '—'}</Mono>
        </div>
        <EquityChart rows={rows} />
        <div style={{ display: 'flex', alignItems: 'center', marginTop: 16 }}>
          <Mono style={{ fontSize: 10, color: CX.faint, letterSpacing: '0.16em' }}>DRAWDOWN · % FROM PEAK</Mono>
          <Help id="backtest_kpi_max_drawdown" />
        </div>
        <DrawdownChart rows={rows} />
      </div>

      {/* heatmap + bucket attribution */}
      <div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: 24, marginBottom: 24 }}>
        <ReturnsHeatmap rows={rows} />
        <div style={{ background: CX.panel, border: `1px solid ${CX.line}`, padding: '16px 18px' }}>
          <div style={{ display: 'flex', alignItems: 'center' }}>
            <Mono style={{ fontSize: 10, color: CX.faint, letterSpacing: '0.16em' }}>ATTRIBUTION BY BUCKET</Mono>
            <Help id="backtest_attribution" />
          </div>
          <div style={{ marginTop: 18, display: 'flex', flexDirection: 'column' }}>
            {buckets.map((b, i) => (
              <div key={i} style={{ padding: '14px 0', borderTop: i === 0 ? 'none' : `1px solid ${CX.line}` }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 8 }}>
                  <Mono style={{ fontSize: 12, color: CX.text }}>{b[0]}</Mono>
                  <Mono style={{ fontSize: 13, color: b[2] >= 0 ? CX.accent : CX.red }}>{b[1]}</Mono>
                </div>
                <div style={{ height: 5, background: CX.line, position: 'relative' }}>
                  <div style={{ position: 'absolute', left: '50%', top: 0, bottom: 0, width: 1, background: 'rgba(255,255,255,0.18)' }} />
                  <div style={{
                    position: 'absolute', top: 0, bottom: 0,
                    left: b[2] >= 0 ? '50%' : `${50 - Math.abs(b[2]) * 50}%`,
                    width: `${Math.abs(b[2]) * 50}%`,
                    background: b[2] >= 0 ? CX.accent : CX.red,
                  }} />
                </div>
              </div>
            ))}
          </div>
        </div>
      </div>

      {/* notes */}
      <div style={{ background: CX.panel, border: `1px solid ${CX.line}`, padding: '20px 22px' }}>
        <Mono style={{ fontSize: 10, color: CX.faint, letterSpacing: '0.16em' }}>PERSISTED RUNS</Mono>
        <div style={{ marginTop: 14, display: 'flex', flexDirection: 'column' }}>
          {variants.map((n, i) => (
            <div key={i} style={{ display: 'grid', gridTemplateColumns: '70px 1fr', gap: 14, padding: '10px 0', borderTop: i === 0 ? 'none' : `1px solid ${CX.line}` }}>
              <Mono style={{ fontSize: 11, color: CX.accent, letterSpacing: '0.06em' }}>{n.date}</Mono>
              <div style={{ fontFamily: CX.display, fontSize: 14, color: CX.text, lineHeight: 1.55, textWrap: 'pretty' }}>
                {n.label}{n.notes ? ` · ${n.notes}` : ''}
              </div>
            </div>
          ))}
          {variants.length === 0 && (
            <Mono style={{ fontSize: 11, color: CX.faint, letterSpacing: '0.08em' }}>NO RUNS PERSISTED</Mono>
          )}
        </div>
      </div>
    </div>
  );
}

window.PageBacktest = PageBacktest;
