// Terminal variant — dual-mode bootstrapper.
// Reads config / runs / currentRun via window.BacktestDataSource, then renders
// the dense mono-terminal UI from the handoff design.
const THEMES = [
  { id: 'terminal-dark',   label: 'TERMINAL DARK' },
  { id: 'bright',          label: 'BRIGHT' },
  { id: 'amber-crt',       label: 'AMBER CRT' },
  { id: 'solarized-dark',  label: 'SOLARIZED DARK' },
  { id: 'solarized-light', label: 'SOLARIZED LIGHT' },
  { id: 'nord',            label: 'NORD' },
  { id: 'synthwave',       label: 'SYNTHWAVE' },
];

// Slider + manual number input. Defined at module scope (not inside TerminalVariant)
// so React doesn't rebuild the component type on every parent render — keeps the
// internal `draft` state alive while the user is typing.
const ParamSlider = ({ pdef, value, isStatic, updateParam, dim }) => {
  const clamped = Math.max(pdef.min, Math.min(pdef.max, Number(value)));
  const pct = ((clamped - pdef.min) / (pdef.max - pdef.min || 1)) * 100;
  const isInt = Number.isInteger(pdef.step);
  const display = isInt ? String(clamped) : Number(clamped).toFixed(3);
  const [draft, setDraft] = React.useState(display);
  const [focused, setFocused] = React.useState(false);
  React.useEffect(() => {
    if (!focused) setDraft(display);
  }, [display, focused]);

  const commit = () => {
    const n = parseFloat(draft);
    if (!Number.isFinite(n)) {
      setDraft(display);
      return;
    }
    const c = Math.max(pdef.min, Math.min(pdef.max, n));
    updateParam(pdef.name, c);
    setDraft(isInt ? String(c) : Number(c).toFixed(3));
  };

  return (
    <div className="field">
      <label>
        {pdef.label}{' '}
        <span style={{ color: dim, fontSize: 8, marginLeft: 4 }}>
          [{pdef.min}–{pdef.max}]
        </span>
      </label>
      <div className="tv-slider">
        <div className="track">
          <div className="fill" style={{ width: `${pct}%` }}></div>
          <div className="thumb" style={{ left: `${pct}%` }}></div>
          <input
            type="range"
            min={pdef.min}
            max={pdef.max}
            step={pdef.step}
            value={clamped}
            disabled={isStatic}
            aria-label={pdef.label}
            onChange={(e) => updateParam(pdef.name, parseFloat(e.target.value))}
          />
        </div>
        <input
          type="number"
          className="num"
          min={pdef.min}
          max={pdef.max}
          step={pdef.step}
          value={draft}
          disabled={isStatic}
          aria-label={`${pdef.label} value`}
          onChange={(e) => setDraft(e.target.value)}
          onFocus={(e) => { setFocused(true); e.target.select(); }}
          onBlur={() => { setFocused(false); commit(); }}
          onKeyDown={(e) => {
            if (e.key === 'Enter') { e.currentTarget.blur(); }
            else if (e.key === 'Escape') { setDraft(display); e.currentTarget.blur(); }
          }}
        />
      </div>
    </div>
  );
};

const TerminalVariant = () => {
  const [data, setData] = React.useState(null); // {config, runs, currentRun}
  const [running, setRunning] = React.useState(false);
  const [error, setError] = React.useState(null);
  const [asset, setAsset] = React.useState('BTC');
  const [timeframe, setTimeframe] = React.useState('1h');
  const [params, setParams] = React.useState({});
  const [capital, setCapital] = React.useState({
    initial: '10000',
    leverage: '3',
    sizing_mode: 'percent_of_equity',
    size_pct: '25',
  });
  const [fees, setFees] = React.useState({ fee_rate: '0.0005', slippage_ticks: 2 });
  const [dateRange, setDateRange] = React.useState(() => {
    // Default: trailing 30 days ending today (UTC date slice so the <input type="date">
    // stays stable regardless of user timezone).
    const end = new Date();
    const start = new Date(end);
    start.setUTCDate(start.getUTCDate() - 30);
    const iso = (d) => d.toISOString().slice(0, 10);
    return [iso(start), iso(end)];
  });
  const [strategy, setStrategy] = React.useState('DualScaleCRSI');
  const [previewBars, setPreviewBars] = React.useState(null);
  const [previewIndicators, setPreviewIndicators] = React.useState([]);
  const [previewError, setPreviewError] = React.useState(null);
  const [tab, setTab] = React.useState('Overview');
  const [fetchStatus, setFetchStatus] = React.useState('idle'); // idle | loading | slow-loading — matches FETCH_STATUS in trading-chart.jsx
  const [fetchContext, setFetchContext] = React.useState({ mode: 'preview', symbol: 'BTC', timeframe: '1h', rangeLabel: '' }); // mode matches FETCH_MODE in trading-chart.jsx
  const slowTimerRef = React.useRef(null);

  const [theme, setTheme] = React.useState(
    () => localStorage.getItem('backtest.theme') || 'terminal-dark'
  );

  // Resizable column widths (config left / summary right). Persisted across sessions.
  const [configWidth, setConfigWidth] = React.useState(() => {
    const v = parseInt(localStorage.getItem('backtest.configWidth') || '', 10);
    return Number.isFinite(v) && v >= 180 && v <= 600 ? v : 260;
  });
  const [summaryWidth, setSummaryWidth] = React.useState(() => {
    const v = parseInt(localStorage.getItem('backtest.summaryWidth') || '', 10);
    return Number.isFinite(v) && v >= 200 && v <= 700 ? v : 300;
  });
  React.useEffect(() => { localStorage.setItem('backtest.configWidth', String(configWidth)); }, [configWidth]);
  React.useEffect(() => { localStorage.setItem('backtest.summaryWidth', String(summaryWidth)); }, [summaryWidth]);

  // Trade-table column widths. Keys must match TRADE_COLUMNS below; defaults sized
  // to the longest plausible value of each column (timestamps need ~200px, qty < 80px).
  const TRADE_COL_DEFAULTS = {
    idx: 40, side: 80, entry_ts: 200, exit_ts: 200, entry: 100, exit: 100,
    qty: 70, pnl: 90, pnl_pct: 80, bars: 60, signal: 80,
  };
  const [colWidths, setColWidths] = React.useState(() => {
    try {
      const saved = JSON.parse(localStorage.getItem('backtest.tradeColWidths') || '{}');
      return { ...TRADE_COL_DEFAULTS, ...saved };
    } catch {
      return { ...TRADE_COL_DEFAULTS };
    }
  });
  React.useEffect(() => {
    localStorage.setItem('backtest.tradeColWidths', JSON.stringify(colWidths));
  }, [colWidths]);

  const colDragRef = React.useRef(null);
  const onColResizeDown = (colId) => (e) => {
    e.preventDefault();
    e.stopPropagation();
    e.currentTarget.setPointerCapture(e.pointerId);
    e.currentTarget.dataset.dragging = 'true';
    colDragRef.current = { colId, startX: e.clientX, startWidth: colWidths[colId] };
  };
  const onColResizeMove = (e) => {
    const drag = colDragRef.current;
    if (!drag) return;
    const dx = e.clientX - drag.startX;
    const next = Math.max(40, Math.min(500, drag.startWidth + dx));
    setColWidths((prev) => (prev[drag.colId] === next ? prev : { ...prev, [drag.colId]: next }));
  };
  const onColResizeUp = (e) => {
    e.currentTarget.dataset.dragging = 'false';
    try { e.currentTarget.releasePointerCapture(e.pointerId); } catch { /* ignore */ }
    colDragRef.current = null;
  };

  // Drag-to-resize for the two splitters. Pointer events + setPointerCapture so
  // drag survives pointer leaving the splitter element (and the browser window).
  const splitterDragRef = React.useRef(null);
  const onSplitterPointerDown = (which) => (e) => {
    e.preventDefault();
    e.currentTarget.setPointerCapture(e.pointerId);
    e.currentTarget.dataset.dragging = 'true';
    splitterDragRef.current = {
      which,
      startX: e.clientX,
      startWidth: which === 'config' ? configWidth : summaryWidth,
    };
  };
  const onSplitterPointerMove = (e) => {
    const drag = splitterDragRef.current;
    if (!drag) return;
    const dx = e.clientX - drag.startX;
    if (drag.which === 'config') {
      const next = Math.max(180, Math.min(600, drag.startWidth + dx));
      setConfigWidth(next);
    } else {
      const next = Math.max(200, Math.min(700, drag.startWidth - dx));
      setSummaryWidth(next);
    }
  };
  const onSplitterPointerUp = (e) => {
    e.currentTarget.dataset.dragging = 'false';
    try { e.currentTarget.releasePointerCapture(e.pointerId); } catch { /* ignore — capture may already be lost */ }
    splitterDragRef.current = null;
  };
  const [cssKick, setCssKick] = React.useState(0);
  React.useEffect(() => {
    document.documentElement.setAttribute('data-theme', theme);
    localStorage.setItem('backtest.theme', theme);
    setCssKick((k) => k + 1);
  }, [theme]);

  // Read from CSS vars — values defined in index.html; cssKick forces re-read after theme change.
  const cssVar = (name, fallback) => {
    if (typeof window === 'undefined') return fallback;
    const v = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
    return v || fallback;
  };
  // cssKick in the expression ensures this block re-evaluates after the DOM attr is applied.
  const _kick = cssKick; // eslint-disable-line no-unused-vars
  const green = cssVar('--green', '#00ff9c');
  const amber = cssVar('--amber', '#ffb627');
  const red   = cssVar('--red',   '#ff5a5a');
  const bg    = cssVar('--bg',    '#05070a');
  const bg2   = cssVar('--bg2',   '#0a0e13');
  const line  = cssVar('--line',  '#1e2a35');
  const dim   = cssVar('--dim',   '#4a5d6e');
  const fg    = cssVar('--fg',    '#c8d9e3');

  React.useEffect(() => {
    window.BacktestDataSource.loadBootstrap()
      .then((d) => {
        setData(d);
        // Initialize params from the selected strategy's defaults, if config is available.
        const sel = d.config?.strategies?.find((s) => s.name === strategy);
        if (sel) {
          setParams(
            Object.fromEntries(sel.params.map((p) => [p.name, p.default]))
          );
        }
      })
      .catch((e) => setError(String(e)));
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  window.ChartUtils.useDebouncedEffect(
    () => {
      if (window.BacktestDataSource.isStatic()) return;
      const [start, end] = dateRange;
      if (!start || !end) return;
      const selectedCfg = data?.config?.strategies?.find((s) => s.name === strategy);
      const indicators = selectedCfg?.indicators || [];
      const paramKeys = {
        supertrend_period: params.st_period,
        supertrend_mult: params.st_mult,
        crsi_dom1: params.dom1,
        crsi_dom2: params.dom2,
        crsi_vibration: params.vibration,
        crsi_leveling: params.leveling,
        crsi_lookback: params.lookback,
      };
      setPreviewError(null);
      setFetchStatus(window.ChartConstants.FETCH_STATUS.LOADING);
      setFetchContext({ mode: window.ChartConstants.FETCH_MODE.PREVIEW, symbol: asset, timeframe, rangeLabel: `${start} → ${end}` });
      clearTimeout(slowTimerRef.current);
      slowTimerRef.current = setTimeout(() => setFetchStatus(window.ChartConstants.FETCH_STATUS.SLOW), 500);

      window.BacktestDataSource.loadBars({
        symbol: asset,
        timeframe,
        start,
        end,
        indicators,
        params: paramKeys,
      })
        .then((resp) => {
          setPreviewBars(resp.price || []);
          setPreviewIndicators(resp.chart_indicators || []);
        })
        .catch((e) => {
          setPreviewError(String(e));
        })
        .finally(() => {
          clearTimeout(slowTimerRef.current);
          setFetchStatus(window.ChartConstants.FETCH_STATUS.IDLE);
        });
    },
    [asset, timeframe, dateRange[0], dateRange[1], strategy, JSON.stringify(params)],
    300
  );

  if (error) {
    return (
      <div style={{ color: red, padding: 40, fontFamily: 'JetBrains Mono, monospace' }}>
        ERROR: {error}
      </div>
    );
  }
  if (!data) {
    return (
      <div
        style={{
          color: dim,
          padding: 40,
          fontFamily: 'JetBrains Mono, monospace',
          background: bg,
          minHeight: '100vh',
        }}
      >
        Loading…
      </div>
    );
  }

  const run = data.currentRun;
  const isStatic = window.BacktestDataSource.isStatic();
  const selectedStrategy = data.config?.strategies?.find((s) => s.name === strategy) || null;
  const strategyList = data.config?.strategies?.map((s) => s.name) || [strategy];
  const symbols = data.config?.symbols || ['BTC', 'SOL', 'HYPE'];
  const timeframes = data.config?.timeframes || ['1m', '5m', '15m', '1h', '4h', '1d'];

  const { fmt, fmtSigned } = window.ChartUtils;

  const priceRows = (previewBars && previewBars.length ? previewBars : run?.price) || [];
  const tradesRows = (run && run.trades) || [];
  const metrics = (run && run.metrics) || {};

  // Indicators / markers grouped by pane. Prefer live preview if available.
  const effectiveIndicators =
    previewIndicators && previewIndicators.length
      ? previewIndicators
      : (run?.chart_indicators || []);
  const indicatorsByPane = { price: [], subpane_1: [], subpane_2: [] };
  for (const ci of effectiveIndicators) {
    if (indicatorsByPane[ci.pane]) indicatorsByPane[ci.pane].push(ci);
  }
  const buildRunRequest = () => ({
    symbol: asset,
    timeframe,
    date_range: dateRange,
    strategy,
    params,
    capital,
    fee_rate: fees.fee_rate,
    slippage_ticks: parseInt(fees.slippage_ticks, 10),
  });

  const runBacktest = async () => {
    if (isStatic) return;
    setRunning(true);
    setError(null);
    setFetchStatus(window.ChartConstants.FETCH_STATUS.LOADING);
    setFetchContext({ mode: window.ChartConstants.FETCH_MODE.RUN, strategy, symbol: asset, timeframe, rangeLabel: `${dateRange[0]} → ${dateRange[1]}` });
    clearTimeout(slowTimerRef.current);
    slowTimerRef.current = setTimeout(() => setFetchStatus(window.ChartConstants.FETCH_STATUS.SLOW), 500);
    try {
      const req = buildRunRequest();
      const result = await window.BacktestDataSource.runBacktest(req);
      setData((prev) => ({
        ...prev,
        currentRun: result,
        runs: [
          {
            id: result.run?.id || '',
            strategy: result.config?.strategy || strategy,
            symbol: result.config?.symbol || asset,
            timeframe: result.config?.timeframe || timeframe,
            date_range: result.config?.date_range || dateRange,
            net_profit_pct: result.metrics?.net_profit_pct || '0',
          },
          ...prev.runs,
        ],
      }));
      try {
        const fresh = await window.BacktestDataSource.loadRuns();
        setData((prev) => ({ ...prev, runs: fresh }));
      } catch (e) {
        console.warn('loadRuns refresh failed:', e);
      }
      setPreviewBars(null);
      setPreviewIndicators([]);
    } catch (e) {
      setError(String(e.message || e));
    } finally {
      setRunning(false);
      clearTimeout(slowTimerRef.current);
      setFetchStatus(window.ChartConstants.FETCH_STATUS.IDLE);
    }
  };

  const loadRun = async (id) => {
    try {
      const result = await window.BacktestDataSource.getRun(id);
      setData((prev) => ({ ...prev, currentRun: result }));
    } catch (e) {
      setError(String(e.message || e));
    }
  };

  const updateParam = (name, value) => setParams((p) => ({ ...p, [name]: value }));

  const styleCss = `
    .tv-root { font-family: 'JetBrains Mono', 'IBM Plex Mono', ui-monospace, Menlo, monospace; background: ${bg}; color: ${fg}; font-size: 11px; line-height: 1.35; letter-spacing: 0.01em; }
    .tv-root * { box-sizing: border-box; }
    .tv-header { background: ${bg2}; font-size: 10px; text-transform: uppercase; letter-spacing: 0.08em; }
    .tv-header .title { color: ${green}; font-weight: 700; }
    .tv-header .kv { color: ${dim}; }
    .tv-header .kv b { color: ${fg}; font-weight: 500; margin-left: 4px; }
    .tv-header .dot { width: 6px; height: 6px; border-radius: 50%; background: ${green}; box-shadow: 0 0 8px ${green}; display: inline-block; margin-right: 5px; animation: pulse 1.6s infinite; }
    @keyframes pulse { 0%, 100% { opacity: 1 } 50% { opacity: 0.4 } }
    .ph { display:flex; justify-content:space-between; align-items:center; color: ${green}; font-size: 10px; text-transform: uppercase; letter-spacing: 0.12em; margin-bottom: 10px; border-bottom: 1px dashed ${line}; padding-bottom: 6px; }
    .ph .tag { color: ${dim}; }
    .row { display: flex; justify-content: space-between; padding: 3px 0; border-bottom: 1px dotted ${line}; }
    .row .k { color: ${dim}; }
    .row .v { color: ${fg}; }
    .row .v.g { color: ${green}; }
    .row .v.r { color: ${red}; }
    .row .v.a { color: ${amber}; }
    .asset-tabs { display: flex; gap: 0; margin-bottom: 12px; border: 1px solid ${line}; }
    .asset-tabs button { flex: 1; background: transparent; border: none; border-right: 1px solid ${line}; color: ${dim}; padding: 6px 0; font-family: inherit; font-size: 10px; cursor: pointer; letter-spacing: 0.08em; }
    .asset-tabs button:last-child { border-right: none; }
    .asset-tabs button.on { background: ${green}; color: ${bg}; font-weight: 700; }
    .field { margin-bottom: 9px; }
    .field label { display: block; color: ${dim}; font-size: 9px; letter-spacing: 0.1em; margin-bottom: 3px; text-transform: uppercase; }
    .field input, .field select { width: 100%; background: ${bg}; border: 1px solid ${line}; color: ${fg}; font-family: inherit; font-size: 11px; padding: 5px 7px; }
    .field input:focus, .field select:focus { outline: none; border-color: ${green}; }
    .field .two { display: grid; grid-template-columns: 1fr 1fr; gap: 6px; }
    .pill-row { display: flex; gap: 0; border: 1px solid ${line}; }
    .pill-row button { flex: 1; background: transparent; border: none; border-right: 1px solid ${line}; color: ${dim}; padding: 4px 0; font-family: inherit; font-size: 10px; cursor: pointer; }
    .pill-row button:last-child { border-right: none; }
    .pill-row button.on { background: ${line}; color: ${green}; }
    .run-btn { width: 100%; background: ${green}; color: ${bg}; border: none; font-family: inherit; font-size: 11px; font-weight: 700; letter-spacing: 0.1em; padding: 9px 0; cursor: pointer; margin-top: 6px; text-transform: uppercase; }
    .run-btn:disabled { background: ${line}; color: ${dim}; cursor: not-allowed; }
    .run-btn:hover:not(:disabled) { box-shadow: 0 0 16px ${green}40; }
    .metric-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 12px; }
    .metric { border: 1px solid ${line}; padding: 7px 9px; }
    .metric .lbl { color: ${dim}; font-size: 9px; letter-spacing: 0.1em; text-transform: uppercase; }
    .metric .val { color: ${green}; font-size: 18px; font-weight: 500; margin-top: 2px; }
    .metric .val.r { color: ${red}; }
    .metric .sub { color: ${dim}; font-size: 10px; margin-top: 1px; }
    .tabs { display: flex; border-bottom: 1px solid ${line}; margin-bottom: 0; }
    .tabs button { background: transparent; border: none; color: ${dim}; padding: 6px 14px; font-family: inherit; font-size: 10px; letter-spacing: 0.1em; cursor: pointer; text-transform: uppercase; border-right: 1px solid ${line}; }
    .tabs button.on { color: ${green}; background: ${bg2}; border-bottom: 1px solid ${green}; }
    .trade-table { border-collapse: collapse; font-size: 10px; table-layout: fixed; }
    .trade-table th { text-align: left; color: ${dim}; font-weight: 400; padding: 4px 8px; border-bottom: 1px solid ${line}; letter-spacing: 0.08em; text-transform: uppercase; position: sticky; top: 0; background: ${bg}; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .trade-table td { padding: 3px 8px; border-bottom: 1px dotted ${line}; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
    .trade-table tr:hover td { background: ${bg2}; }
    .col-resize { position: absolute; top: 0; right: -3px; width: 6px; height: 100%; cursor: col-resize; user-select: none; touch-action: none; z-index: 1; }
    .col-resize:hover, .col-resize[data-dragging="true"] { background: ${green}; opacity: 0.6; }
    .trade-table th { position: sticky; }  /* sticky already set above; this preserves it */
    .trade-table th .th-inner { position: relative; }
    .chart-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
    .chart-legend { display: flex; gap: 14px; font-size: 10px; color: ${dim}; }
    .legend-dot { display: inline-block; width: 10px; height: 2px; margin-right: 5px; vertical-align: middle; }
    .tv-slider { display: flex; align-items: center; gap: 10px; position: relative; }
    .tv-slider .track { flex: 1; height: 3px; background: ${line}; position: relative; }
    .tv-slider .track input[type=range] { position: absolute; left: 0; right: 0; top: -10px; bottom: -10px; width: 100%; height: auto; opacity: 0; cursor: pointer; margin: 0; padding: 0; }
    .tv-slider .fill { position: absolute; left: 0; top: 0; bottom: 0; background: ${green}; box-shadow: 0 0 6px ${green}; pointer-events: none; }
    .tv-slider .thumb { position: absolute; top: 50%; width: 10px; height: 10px; background: ${green}; border: 1px solid ${bg}; transform: translate(-50%, -50%); box-shadow: 0 0 8px ${green}80; pointer-events: none; }
    .tv-slider input.num { background: transparent; border: 1px solid transparent; color: ${green}; font-family: inherit; font-size: 11px; width: 64px; text-align: right; font-variant-numeric: tabular-nums; outline: none; padding: 2px 4px; -moz-appearance: textfield; }
    .tv-slider input.num::-webkit-outer-spin-button, .tv-slider input.num::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
    .tv-slider input.num:hover:not(:disabled) { border-color: ${line}; }
    .tv-slider input.num:focus { border-color: ${green}; }
    .tv-slider input.num:disabled { cursor: not-allowed; opacity: 0.6; }
    .hist-row { padding: 3px 0; border-bottom: 1px dotted ${line}; cursor: pointer; font-size: 10px; }
    .hist-row.on { background: ${bg2}; font-weight: 600; }
    .trades-section { border-top: 1px solid ${line}; margin-top: 12px; }
    .trades-section-body { overflow-x: auto; }
  `;

  const splitterProps = {
    onPointerMove: onSplitterPointerMove,
    onPointerUp: onSplitterPointerUp,
    onPointerCancel: onSplitterPointerUp,
  };

  return (
    <div
      className="tv-root"
      style={{
        '--config-w': `${configWidth}px`,
        '--summary-w': `${summaryWidth}px`,
      }}
    >
      <style>{styleCss}</style>
      <div
        className="tv-splitter"
        data-which="config"
        role="separator"
        aria-orientation="vertical"
        aria-label="Resize config panel"
        onPointerDown={onSplitterPointerDown('config')}
        {...splitterProps}
      />
      <div
        className="tv-splitter"
        data-which="summary"
        role="separator"
        aria-orientation="vertical"
        aria-label="Resize summary panel"
        onPointerDown={onSplitterPointerDown('summary')}
        {...splitterProps}
      />

      {/* HEADER */}
      <div className="tv-header">
        <span
          className="title"
          title={
            data?.version
              ? `commit ${data.version.commit}` +
                (data.version.deployed_at ? ` · deployed ${data.version.deployed_at}` : '')
              : undefined
          }
        >
          ◆ QUANT.TERM v{data?.version?.semver || '0.4.1'}
          {data?.version?.build ? ` #${data.version.build}` : ''}
        </span>
        <span className="kv">
          <span className="dot"></span>SESSION{' '}
          <b>
            {run?.run?.id
              ? '#' + run.run.id.toString().slice(-4).toUpperCase()
              : previewBars?.length
                ? 'PREVIEW'
                : 'IDLE'}
          </b>
        </span>
        <span className="kv">
          ENGINE <b style={{ color: green }}>{isStatic ? 'STATIC' : 'ONLINE'}</b>
        </span>
        <span className="kv">
          BARS <b>{run?.run?.bars ?? (previewBars?.length || '—')}</b>
        </span>
        <span className="kv">
          TRADES <b>{run?.run?.trades ?? tradesRows.length}</b>
        </span>
        <span style={{ flex: 1 }}></span>
        <span className="kv">
          MODE <b>{isStatic ? 'STATIC' : 'SERVER'}</b>
        </span>
        <div style={{ marginLeft: 'auto', display: 'flex', gap: 8, alignItems: 'center' }}>
          <span style={{ color: dim, fontSize: 11 }}>THEME</span>
          <select
            value={theme}
            onChange={(e) => setTheme(e.target.value)}
            style={{
              fontFamily: 'inherit',
              fontSize: 11,
              padding: '4px 8px',
              background: bg2,
              color: fg,
              border: `1px solid ${line}`,
              cursor: 'pointer',
            }}
          >
            {THEMES.map((t) => (
              <option key={t.id} value={t.id}>{t.label}</option>
            ))}
          </select>
        </div>
      </div>

      {/* CONFIG (left column) */}
      <div className="tv-config">
        <div className="ph">
          [ config ] <span className="tag">01</span>
        </div>

        <label
          style={{
            color: dim,
            fontSize: 9,
            letterSpacing: '0.1em',
            display: 'block',
            marginBottom: 5,
            textTransform: 'uppercase',
          }}
        >
          Asset
        </label>
        <div className="asset-tabs">
          {symbols.map((a) => (
            <button
              key={a}
              className={asset === a ? 'on' : ''}
              disabled={isStatic}
              onClick={() => setAsset(a)}
            >
              {a}
            </button>
          ))}
        </div>

        <div className="field">
          <label>Timeframe</label>
          <div className="pill-row">
            {timeframes.map((t) => (
              <button
                key={t}
                className={t === timeframe ? 'on' : ''}
                disabled={isStatic}
                onClick={() => setTimeframe(t)}
              >
                {t}
              </button>
            ))}
          </div>
        </div>

        <div className="field">
          <label>Date range</label>
          <div className="two">
            <input
              value={dateRange[0]}
              disabled={isStatic}
              onChange={(e) => setDateRange([e.target.value, dateRange[1]])}
            />
            <input
              value={dateRange[1]}
              disabled={isStatic}
              onChange={(e) => setDateRange([dateRange[0], e.target.value])}
            />
          </div>
        </div>

        <div className="field">
          <label>Strategy</label>
          <select
            value={strategy}
            disabled={isStatic}
            onChange={(e) => {
              const name = e.target.value;
              setStrategy(name);
              const sel = data.config?.strategies?.find((s) => s.name === name);
              if (sel) {
                setParams(
                  Object.fromEntries(sel.params.map((p) => [p.name, p.default]))
                );
              }
            }}
          >
            {strategyList.map((s) => (
              <option key={s}>{s}</option>
            ))}
          </select>
        </div>

        {selectedStrategy && selectedStrategy.params.length > 0 && (
          <>
            <div className="ph" style={{ marginTop: 14 }}>
              [ params ]
            </div>
            {selectedStrategy.params.map((p) => {
              if (p.name === 'direction_mode') {
                const mode = params[p.name] ?? p.default;
                const opts = [
                  { val: 0, label: 'BOTH' },
                  { val: 1, label: 'LONG' },
                  { val: 2, label: 'SHORT' },
                ];
                return (
                  <div key={p.name} className="field">
                    <label>Direction</label>
                    <div className="pill-row">
                      {opts.map((o) => (
                        <button
                          key={o.val}
                          type="button"
                          className={Number(mode) === o.val ? 'on' : ''}
                          disabled={isStatic}
                          onClick={() => updateParam(p.name, o.val)}
                        >
                          {o.label}
                        </button>
                      ))}
                    </div>
                  </div>
                );
              }
              return (
                <ParamSlider
                  key={p.name}
                  pdef={p}
                  value={params[p.name] ?? p.default}
                  isStatic={isStatic}
                  updateParam={updateParam}
                  dim={dim}
                />
              );
            })}
          </>
        )}

        <div className="ph" style={{ marginTop: 14 }}>
          [ capital ]
        </div>
        <div className="field">
          <label>Initial capital (USDT)</label>
          <input
            value={capital.initial}
            disabled={isStatic}
            onChange={(e) => setCapital({ ...capital, initial: e.target.value })}
          />
        </div>
        <div className="field">
          <label>Leverage</label>
          <input
            value={capital.leverage}
            disabled={isStatic}
            onChange={(e) => setCapital({ ...capital, leverage: e.target.value })}
          />
        </div>
        <div className="field">
          <label>Position sizing</label>
          <select
            value={capital.sizing_mode}
            disabled={isStatic}
            onChange={(e) => setCapital({ ...capital, sizing_mode: e.target.value })}
          >
            <option value="fixed">Fixed</option>
            <option value="percent_of_equity">% of equity</option>
            <option value="kelly">Kelly</option>
          </select>
        </div>
        <div className="field">
          <label>Size %</label>
          <input
            value={capital.size_pct}
            disabled={isStatic}
            onChange={(e) => setCapital({ ...capital, size_pct: e.target.value })}
          />
        </div>
        <div className="field">
          <label>Fee / Slippage (ticks)</label>
          <div className="two">
            <input
              value={fees.fee_rate}
              disabled={isStatic}
              onChange={(e) => setFees({ ...fees, fee_rate: e.target.value })}
            />
            <input
              value={fees.slippage_ticks}
              disabled={isStatic}
              onChange={(e) =>
                setFees({ ...fees, slippage_ticks: parseInt(e.target.value, 10) || 0 })
              }
            />
          </div>
        </div>

        <button
          className="run-btn"
          disabled={isStatic || running}
          title={isStatic ? 'Static report — use CLI: backtest-engine run' : ''}
          onClick={runBacktest}
        >
          {running ? '▶ RUNNING…' : '▶ RUN BACKTEST'}
        </button>

        <div className="ph" style={{ marginTop: 14 }}>
          [ history ]
        </div>
        <div style={{ maxHeight: 200, overflow: 'auto', fontSize: 10 }}>
          {(!data.runs || data.runs.length === 0) && (
            <div style={{ color: dim, padding: '4px 0' }}>No past runs.</div>
          )}
          {(data.runs || []).map((r) => {
            const net = parseFloat(r.net_profit_pct || '0');
            const active = run?.run?.id === r.id;
            return (
              <div
                key={r.id}
                className={`hist-row ${active ? 'on' : ''}`}
                onClick={() => loadRun(r.id)}
                title={r.id}
              >
                <span style={{ color: net >= 0 ? green : red }}>
                  {net >= 0 ? '+' : ''}
                  {r.net_profit_pct}%
                </span>{' '}
                <span style={{ color: fg }}>{(r.strategy || '').slice(0, 16)}</span>{' '}
                <span style={{ color: dim }}>
                  {r.symbol}/{r.timeframe}
                </span>
              </div>
            );
          })}
        </div>
      </div>

      {/* CHART (centre column) */}
      <div className="tv-chart">
        {previewError && (
          <div style={{ color: red, padding: '4px 0 8px', fontSize: 12, fontFamily: 'JetBrains Mono, monospace' }}>
            preview error: {previewError}
          </div>
        )}
        <TradingChart
          priceRows={priceRows}
          indicatorsByPane={indicatorsByPane}
          trades={tradesRows}
          fetchStatus={fetchStatus}
          fetchContext={fetchContext}
          dateRange={dateRange}
          onRangeChange={setDateRange}
          theme={{ fg, bg, bg2, line, dim, green, amber, red }}
        />
      </div>

      {/* TRADES (full-width row below chart — grid-area: trades) */}
      <div className="trades-section" style={{ gridArea: 'trades' }}>
        <div className="tabs">
          {['Overview', 'Performance', 'Trades', 'Properties'].map((t) => (
            <button key={t} className={tab === t ? 'on' : ''} onClick={() => setTab(t)}>
              {t}
            </button>
          ))}
          <div style={{ flex: 1 }}></div>
          <div style={{ padding: '6px 14px', color: dim, fontSize: 10 }}>
            showing {tradesRows.length} closed
          </div>
        </div>
        <div className="trades-section-body">
          {(() => {
            const cols = [
              { id: 'idx',      label: '#' },
              { id: 'side',     label: 'Side' },
              { id: 'entry_ts', label: 'Entry time' },
              { id: 'exit_ts',  label: 'Exit time' },
              { id: 'entry',    label: 'Entry $' },
              { id: 'exit',     label: 'Exit $' },
              { id: 'qty',      label: 'Qty' },
              { id: 'pnl',      label: 'P&L $' },
              { id: 'pnl_pct',  label: 'P&L %' },
              { id: 'bars',     label: 'Bars' },
              { id: 'signal',   label: 'Signal' },
            ];
            const totalWidth = cols.reduce((s, c) => s + colWidths[c.id], 0);
            const ResizeHandle = ({ colId }) => (
              <span
                className="col-resize"
                role="separator"
                aria-orientation="vertical"
                aria-label={`Resize column ${colId}`}
                onPointerDown={onColResizeDown(colId)}
                onPointerMove={onColResizeMove}
                onPointerUp={onColResizeUp}
                onPointerCancel={onColResizeUp}
              />
            );
            return (
              <table className="trade-table" style={{ width: `${totalWidth}px` }}>
                <colgroup>
                  {cols.map((c) => <col key={c.id} style={{ width: `${colWidths[c.id]}px` }} />)}
                </colgroup>
                <thead>
                  <tr>
                    {cols.map((c) => (
                      <th key={c.id}>
                        <div className="th-inner">
                          {c.label}
                          <ResizeHandle colId={c.id} />
                        </div>
                      </th>
                    ))}
                  </tr>
                </thead>
                <tbody>
                  {tradesRows.map((tr) => {
                    const pnl = parseFloat(tr.pnl ?? 0);
                    const pnlPct = parseFloat(tr.pnl_pct ?? 0);
                    const isLong = window.ChartUtils.isLongSide(tr.side);
                    return (
                      <tr key={tr.id}>
                        <td style={{ color: dim }}>{tr.id}</td>
                        <td style={{ color: isLong ? green : amber }}>
                          {isLong ? '▲ LONG' : '▼ SHORT'}
                        </td>
                        <td>{tr.entry_ts}</td>
                        <td>{tr.exit_ts}</td>
                        <td>{fmt(tr.entry, 2)}</td>
                        <td>{fmt(tr.exit, 2)}</td>
                        <td>{fmt(tr.qty, 4)}</td>
                        <td style={{ color: pnl >= 0 ? green : red }}>{fmtSigned(pnl)}</td>
                        <td style={{ color: pnl >= 0 ? green : red }}>{fmtSigned(pnlPct, 2, '%')}</td>
                        <td style={{ color: dim }}>{tr.bars}</td>
                        <td style={{ color: dim }}>{tr.signal}</td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            );
          })()}
        </div>
      </div>

      {/* SUMMARY (right column) */}
      <div className="tv-summary">
        <div className="ph">
          [ summary ] <span className="tag">{run ? 'LIVE' : 'IDLE'}</span>
        </div>

        <div className="metric-grid">
          <div className="metric">
            <div className="lbl">Net Profit</div>
            <div className={`val ${parseFloat(metrics.net_profit_pct || 0) < 0 ? 'r' : ''}`}>
              {metrics.net_profit_pct ? fmtSigned(metrics.net_profit_pct, 2, '%') : '—'}
            </div>
            <div className="sub">{metrics.net_profit_closed ?? metrics.net_profit ?? '—'}</div>
          </div>
          <div className="metric">
            <div className="lbl">Max DD</div>
            <div className="val r">
              {metrics.max_drawdown_pct ? fmtSigned(-Math.abs(parseFloat(metrics.max_drawdown_pct)), 2, '%') : '—'}
            </div>
            <div className="sub">{metrics.max_drawdown ?? '—'}</div>
          </div>
          <div className="metric">
            <div className="lbl">Sharpe</div>
            <div className="val">{metrics.sharpe != null ? fmt(metrics.sharpe, 2) : '—'}</div>
            <div className="sub">annualized</div>
          </div>
          <div className="metric">
            <div className="lbl">Sortino</div>
            <div className="val">{metrics.sortino != null ? fmt(metrics.sortino, 2) : '—'}</div>
            <div className="sub">annualized</div>
          </div>
        </div>

        <div className="ph" style={{ marginTop: 4 }}>
          [ stats ]
        </div>
        <div className="row">
          <span className="k">Profit factor</span>
          <span className="v">{metrics.profit_factor ?? '—'}</span>
        </div>
        <div className="row">
          <span className="k">Total trades</span>
          <span className="v">{metrics.total_closed_trades ?? tradesRows.length}</span>
        </div>
        <div className="row">
          <span className="k">Win rate</span>
          <span className="v g">{metrics.percent_profitable ?? '—'}</span>
        </div>
        {(() => {
          const isLong = window.ChartUtils.isLongSide;
          const counts = { all: { w: 0, l: 0 }, long: { w: 0, l: 0 }, short: { w: 0, l: 0 } };
          for (const t of tradesRows) {
            const pnl = parseFloat(t.pnl ?? 0);
            if (pnl === 0) continue;  // breakeven excluded — matches backend convention
            const bucket = isLong(t.side) ? counts.long : counts.short;
            const k = pnl > 0 ? 'w' : 'l';
            bucket[k] += 1;
            counts.all[k] += 1;
          }
          const ratio = (w, l) => (l > 0 ? (w / l).toFixed(2) : (w > 0 ? '∞' : '—'));
          const Row = ({ label, w, l }) => (
            <div className="row">
              <span className="k">{label}</span>
              <span className="v">
                <span className="g">{w}</span>
                <span style={{ color: dim }}> / </span>
                <span className="r">{l}</span>
                <span style={{ color: dim }}>{' • '}{ratio(w, l)}</span>
              </span>
            </div>
          );
          return (
            <>
              <Row label="W / L" w={counts.all.w} l={counts.all.l} />
              <Row label="W / L · long" w={counts.long.w} l={counts.long.l} />
              <Row label="W / L · short" w={counts.short.w} l={counts.short.l} />
            </>
          );
        })()}
        <div className="row">
          <span className="k">Largest win</span>
          <span className="v g">{metrics.largest_win ?? '—'}</span>
        </div>
        <div className="row">
          <span className="k">Largest loss</span>
          <span className="v r">{metrics.largest_loss ?? '—'}</span>
        </div>
        <div className="row">
          <span className="k">Commission paid</span>
          <span className="v">{metrics.commissions_paid ?? '—'}</span>
        </div>
      </div>
    </div>
  );
};

window.TerminalVariant = TerminalVariant;
