// ============================================================
// Primitives — shared utilities and tiny chart components
// ============================================================

const { useState, useEffect, useRef, useMemo, useCallback, Fragment } = React;

// ---- Formatters ---------------------------------------------
const fmt = {
  pct: (v, d = 1) => v == null || isNaN(v) ? '—' : (v * 100).toFixed(d) + '%',
  pctRaw: (v, d = 1) => v == null || isNaN(v) ? '—' : v.toFixed(d) + '%',
  pp: (v, d = 1) => v == null || isNaN(v) ? '—' : (v >= 0 ? '+' : '') + (v * 100).toFixed(d) + 'pp',
  units: (v, d = 1) => v == null || isNaN(v) ? '—' : (v >= 0 ? '+' : '') + v.toFixed(d) + 'u',
  num: (v, d = 0) => v == null || isNaN(v) ? '—' : v.toLocaleString('en-US', { minimumFractionDigits: d, maximumFractionDigits: d }),
  signed: (v, d = 2) => v == null || isNaN(v) ? '—' : (v >= 0 ? '+' : '') + v.toFixed(d),
  date: (s) => {
    if (!s) return '—';
    const d = new Date(s);
    return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
  },
  team: (s) => s || '—',
};

// ---- Color helpers ------------------------------------------
const colors = {
  green: '#22c55e',
  red:   '#ef4444',
  amber: '#f59e0b',
  blue:  '#3b82f6',
  fg1:   '#e1e4ea',
  fg2:   '#8892a6',
  fg3:   '#5a6478',
  border:'#252d42',
  borderLight: '#2e3850',
  surface: '#111520',
  surface2:'#161b28',
  surface3:'#1c2235',
};

// pos/neg/neutral class helper
const cls = (v, zero = 0) =>
  v == null || isNaN(v) ? 'neutral'
  : v > zero ? 'pos'
  : v < zero ? 'neg'
  : 'neutral';

// ---- Info tooltip chip --------------------------------------
function Info({ tip }) {
  return <span className="info-chip" data-tip={tip}>?</span>;
}

// ---- Chart tooltip (fixed positioning) ----------------------
function ChartTooltip({ x, y, children }) {
  if (x == null) return null;
  // Offset above-and-right of cursor; clamp to viewport
  const ox = 14, oy = -10;
  return (
    <div className="chart-tip" style={{ left: x + ox, top: y + oy, transform: 'translateY(-100%)' }}>
      {children}
    </div>
  );
}

// ---- Sparkline (Tufte) --------------------------------------
// Tiny inline line, optional zero baseline, optional dot at end,
// optional area shade for cumulative metrics.
function Sparkline({
  data,              // [{ x, y }]
  width = 140,
  height = 32,
  stroke = colors.fg1,
  fill = null,
  zeroLine = false,
  baselineY = null,  // if set, draws dashed ref at this y-value
  endDot = true,
  endLabel = null,
}) {
  if (!data || data.length === 0) return <svg width={width} height={height} />;
  const xs = data.map(d => d.x);
  const ys = data.map(d => d.y);
  const xMin = Math.min(...xs), xMax = Math.max(...xs);
  const yMin = Math.min(...ys, baselineY ?? Infinity);
  const yMax = Math.max(...ys, baselineY ?? -Infinity);
  const padY = (yMax - yMin) * 0.12 || 1;
  const y0 = yMin - padY, y1 = yMax + padY;
  const pad = 2;
  const xScale = (v) => pad + ((v - xMin) / (xMax - xMin || 1)) * (width - 2*pad);
  const yScale = (v) => height - pad - ((v - y0) / (y1 - y0 || 1)) * (height - 2*pad);

  const path = data.map((d, i) =>
    (i === 0 ? 'M' : 'L') + xScale(d.x).toFixed(2) + ',' + yScale(d.y).toFixed(2)
  ).join(' ');
  const area = path + ` L${xScale(xMax).toFixed(2)},${height-pad} L${xScale(xMin).toFixed(2)},${height-pad} Z`;
  const last = data[data.length - 1];

  return (
    <svg width={width} height={height} className="chart-svg" style={{ overflow: 'visible' }}>
      {fill && <path d={area} fill={fill} opacity="0.18" />}
      {baselineY != null && (
        <line x1={pad} x2={width-pad} y1={yScale(baselineY)} y2={yScale(baselineY)} className="reference-line" />
      )}
      <path d={path} fill="none" stroke={stroke} strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
      {endDot && <circle cx={xScale(last.x)} cy={yScale(last.y)} r="2.5" fill={stroke} />}
      {endLabel && (
        <text x={xScale(last.x) + 5} y={yScale(last.y) + 3} className="label-text" fill={stroke}>
          {endLabel}
        </text>
      )}
    </svg>
  );
}

// ---- Tiny bullet bars (P/L sparkline alternative) ----------
// Vertical micro-bars with zero baseline. Tufte's "small multiples" friend.
function MicroBars({
  data,              // [{ x, y }] — y can be +/-
  width = 140,
  height = 32,
  posColor = colors.green,
  negColor = colors.red,
  gap = 1,
}) {
  if (!data || data.length === 0) return <svg width={width} height={height} />;
  const ys = data.map(d => d.y);
  const yMax = Math.max(...ys, 0);
  const yMin = Math.min(...ys, 0);
  const yRange = (yMax - yMin) || 1;
  const yZero = (yMax / yRange) * height;
  const barW = (width - gap*(data.length-1)) / data.length;
  return (
    <svg width={width} height={height} className="chart-svg">
      <line x1={0} x2={width} y1={yZero} y2={yZero} stroke={colors.fg3} strokeWidth="0.5" opacity="0.6" />
      {data.map((d, i) => {
        const x = i * (barW + gap);
        const h = Math.abs(d.y) / yRange * height;
        const y = d.y >= 0 ? yZero - h : yZero;
        return <rect key={i} x={x} y={y} width={barW} height={Math.max(h, 0.5)} fill={d.y >= 0 ? posColor : negColor} opacity="0.85" />;
      })}
    </svg>
  );
}

// ---- Horizontal dot strip (calibration / by-edge) -----------
// A horizontal axis with N dots positioned by value, sized by sample.
function DotStrip({
  rows,              // [{ label, n, value, ref }] value in [vmin,vmax]
  vmin = 0,
  vmax = 1,
  width = 280,
  height = 22,
  refValue = null,
  refLabel = null,
  color = colors.accent || '#3b82f6',
  labelInside = false,
  formatValue = (v) => fmt.pct(v),
}) {
  // Single horizontal axis at height/2
  const pad = 14;
  const xScale = (v) => pad + ((v - vmin) / (vmax - vmin || 1)) * (width - 2*pad);
  const maxN = Math.max(...rows.map(r => r.n), 1);
  const dotR = (n) => 3 + Math.sqrt(n / maxN) * 4.5;
  return (
    <svg width={width} height={height} className="chart-svg" style={{ overflow: 'visible' }}>
      <line x1={pad} x2={width-pad} y1={height/2} y2={height/2} stroke={colors.border} strokeWidth="1" />
      {refValue != null && (
        <line x1={xScale(refValue)} x2={xScale(refValue)} y1={2} y2={height-2} className="reference-line" />
      )}
      {rows.map((r, i) => (
        <circle key={i}
          cx={xScale(r.value)} cy={height/2}
          r={dotR(r.n)}
          fill={r.color || color}
          opacity={r.muted ? 0.4 : 0.85}>
          <title>{r.label}: {formatValue(r.value)} (n={r.n})</title>
        </circle>
      ))}
    </svg>
  );
}

// ---- Inline winrate band (used inside KPIs) -----------------
// Range bar with markers — model prob bands aligned with break-even.
function RangeBar({ value, ref, vmin, vmax, width = 140, height = 8, color }) {
  const pad = 2;
  const xScale = (v) => pad + ((v - vmin) / (vmax - vmin || 1)) * (width - 2*pad);
  return (
    <svg width={width} height={height} className="chart-svg" style={{ overflow: 'visible' }}>
      <rect x={pad} y={height/2 - 1} width={width-2*pad} height="2" fill={colors.border} rx="1" />
      {ref != null && (
        <line x1={xScale(ref)} x2={xScale(ref)} y1={0} y2={height} stroke={colors.fg3} strokeWidth="1" strokeDasharray="2 2" />
      )}
      <circle cx={xScale(value)} cy={height/2} r="3.5" fill={color || colors.fg1} />
    </svg>
  );
}

// ---- Window selector ----------------------------------------
function Segmented({ value, onChange, options, accent = false }) {
  return (
    <div className="segmented">
      {options.map(opt => (
        <button
          key={opt.value}
          className={(value === opt.value ? 'active ' : '') + (accent ? 'accent' : '')}
          onClick={() => onChange(opt.value)}>
          {opt.label}
        </button>
      ))}
    </div>
  );
}

// Expose
Object.assign(window, {
  React, useState, useEffect, useRef, useMemo, useCallback, Fragment,
  fmt, colors, cls,
  Info, ChartTooltip, Sparkline, MicroBars, DotStrip, RangeBar, Segmented,
});
