// ============================================================
// App v2 — Cleaner, less dense.
// Hero bankroll + 3 secondary KPIs + market split + slice explorer
// Calibration & bias panels live further down, smaller
// ============================================================

const WINDOWS = [
  { value: '7d',     label: '7d' },
  { value: '30d',    label: '30d' },
  { value: '60d',    label: '60d' },
  { value: 'season', label: 'Season' },
];

const MARKETS = [
  { value: 'all',     label: 'All' },
  { value: 'ml',      label: 'ML' },
  { value: 'totals',  label: 'Totals' },
  { value: 'kprops',  label: 'K-Props' },
  { value: 'hr',      label: 'HR' },
];

const SLICE_TABS = [
  { value: 'spread',   label: 'Favorite size',  source: 'by_spread',   metric: 'ml_accuracy', vmin: 0.30, vmax: 0.85, ref: 0.524, refLabel: 'b/e' },
  { value: 'edge',     label: 'Edge band',      source: 'by_edge',     metric: 'ml_accuracy', vmin: 0.30, vmax: 0.85, ref: 0.524, refLabel: 'b/e' },
  { value: 'total',    label: 'Total range',    source: 'by_total',    metric: 'ml_accuracy', vmin: 0.30, vmax: 0.85, ref: 0.524, refLabel: 'b/e' },
  { value: 'lineup',   label: 'Lineup status',  source: 'by_lineup',   metric: 'ml_accuracy', vmin: 0.30, vmax: 0.85, ref: 0.524, refLabel: 'b/e' },
  { value: 'team',     label: 'Team',           source: 'by_team',     metric: 'ml_accuracy', vmin: 0.30, vmax: 0.85, ref: 0.524, refLabel: 'b/e' },
];


// ---- Hero bankroll panel ----
function Hero({ w }) {
  const variants = w?.kelly_backtest?.variants;
  const growth = variants?.full_kelly?.growth_curve || [];
  const startBR = w?.kelly_backtest?.starting_bankroll ?? 100;
  const roi = w?.roi || {};
  const profit = roi.total_profit_units;
  const roiPct = roi.roi_pct != null ? roi.roi_pct / 100 : null;
  const final = growth[growth.length - 1]?.bankroll ?? startBR;
  const retPct = (final - startBR) / startBR;
  const maxDD = variants?.full_kelly?.max_drawdown_pct;

  return (
    <div className="hero">
      <div className="hero-chart-wrap">
        <div className="hero-chart-head">
          <span className="hero-chart-title">Bankroll · Quarter-Kelly</span>
          <span className="hero-chart-sub">
            ${startBR} start · {variants?.full_kelly?.n_bets_sized ?? 0} bets sized
          </span>
        </div>
        <BankrollChart growth={growth} startBR={startBR} variants={variants} height={240} showFooterStats={false} />
      </div>
      <div className="hero-stats">
        <div>
          <div className="hero-stat-label">Profit</div>
          <div className={'hero-stat-val ' + cls(profit)}>{fmt.units(profit, 1)}</div>
          <div className="hero-stat-foot">{roi.total_bets || 0} bets · {roi.total_wagered_units ? roi.total_wagered_units.toFixed(0) : '—'}u staked</div>
        </div>
        <div>
          <div className="hero-stat-label">ROI</div>
          <div className={'hero-stat-val ' + cls(roiPct)}>
            {roiPct == null ? '—' : (roiPct >= 0 ? '+' : '') + (roiPct * 100).toFixed(1) + '%'}
          </div>
          <div className="hero-stat-foot">Return {retPct >= 0 ? '+' : ''}{(retPct*100).toFixed(2)}%</div>
        </div>
        <div>
          <div className="hero-stat-label">Max drawdown</div>
          <div className="hero-stat-val neg">
            {maxDD != null ? maxDD.toFixed(1) + '%' : '—'}
          </div>
          <div className="hero-stat-foot">Peak ${Math.max(...growth.map(g => g.bankroll), startBR).toFixed(1)}</div>
        </div>
      </div>
    </div>
  );
}


// ---- Three secondary KPIs (cleaner — no sparklines) ----
function KpiRow({ w }) {
  const ml = w?.ml_accuracy || {};
  const mlAcc = ml.accuracy;
  const mlBe = ml.break_even_accuracy ?? 0.524;
  const mlEdge = mlAcc != null ? mlAcc - mlBe : null;
  const clv = w?.clv || {};
  const avgClv = clv.avg_clv;
  const scalars = w?.calibration_scalars || {};
  const brier = scalars.brier;
  const baseline = scalars.brier_baseline;
  const calEdge = brier != null && baseline ? (baseline - brier) / baseline : null;
  const ece = scalars.ece;

  return (
    <div className="kpi-strip">
      <div className="kpi">
        <div className="kpi-label">
          Moneyline hit rate
          <Info tip="% of moneyline picks that won. Break-even at standard -110 odds is 52.4%. Anything below is unprofitable even if 'accurate.'" />
        </div>
        <div className="kpi-meta">{ml.correct}/{ml.total_games} picks</div>
        <div className={'kpi-value ' + (mlEdge == null ? 'neutral' : mlEdge >= 0 ? 'pos' : 'neg')}>
          {fmt.pct(mlAcc, 1)}
        </div>
        <div className="kpi-foot">
          <span className="delta-vs">vs {(mlBe*100).toFixed(1)}% b/e</span>
          <span className={'delta-val ' + cls(mlEdge)}>{fmt.pp(mlEdge)}</span>
        </div>
      </div>

      <div className="kpi">
        <div className="kpi-label">
          Avg CLV
          <Info tip="Closing-line value. Average percentage points your bet price was better (or worse) than the line at first pitch. The single best long-term predictor of a betting edge." />
        </div>
        <div className="kpi-meta">{clv.clv_count || 0} resolved bets</div>
        <div className={'kpi-value ' + (avgClv == null ? 'neutral' : avgClv >= 0 ? 'pos' : 'neg')}>
          {avgClv == null ? '—' : (avgClv >= 0 ? '+' : '') + (avgClv * 100).toFixed(2) + 'pp'}
        </div>
        <div className="kpi-foot">
          <span className="delta-vs">+CLV rate {fmt.pct(clv.positive_clv_rate, 0)}</span>
          <span className={'delta-val ' + (avgClv >= 0 ? 'pos' : 'neg')}>
            {avgClv >= 0 ? 'Beats close' : 'Loses to close'}
          </span>
        </div>
      </div>

      <div className="kpi">
        <div className="kpi-label">
          Calibration
          <Info tip="How honest the model's probabilities are. Brier score under the coin-flip baseline (0.25) means the model is informative. ECE = average gap between predicted and actual win rate." />
        </div>
        <div className="kpi-meta">ECE {fmt.pctRaw((ece ?? 0)*100, 1)}</div>
        <div className={'kpi-value ' + (calEdge == null ? 'neutral' : calEdge >= 0 ? 'pos' : 'neg')}>
          {brier == null ? '—' : brier.toFixed(3)}
        </div>
        <div className="kpi-foot">
          <span className="delta-vs">vs {(baseline ?? 0.25).toFixed(3)} baseline</span>
          <span className={'delta-val ' + cls(calEdge)}>
            {calEdge == null ? '—' : (calEdge >= 0 ? '+' : '') + (calEdge*100).toFixed(1) + '%'}
          </span>
        </div>
      </div>
    </div>
  );
}


// ---- Daily activity (P/L bars + last 10 days strip) ----
function DailyActivity({ w }) {
  const growth = w?.kelly_backtest?.variants?.full_kelly?.growth_curve || [];
  return (
    <div className="card">
      <div className="card-header">
        <div>
          <div className="card-title">Daily P/L · last 30 days</div>
          <div className="card-sub">Hover any bar for date · ML hit rate · bankroll</div>
        </div>
      </div>
      <DailyPL growth={growth} daily={w?.daily} height={130} />
      <div style={{ marginTop: 18, paddingTop: 14, borderTop: '1px solid var(--sb-border)' }}>
        <div style={{ fontSize: 10, color: colors.fg3, fontFamily: 'var(--sb-font-mono)', textTransform: 'uppercase', letterSpacing: '0.04em', marginBottom: 10 }}>
          Last 10 days
        </div>
        <RecentDaysStrip growth={growth} daily={w?.daily} />
      </div>
    </div>
  );
}


function App() {
  const data = window.ANALYTICS;
  const [windowKey, setWindowKey] = useState('30d');
  const [market, setMarket] = useState('all');
  const [sliceTab, setSliceTab] = useState('spread');
  const [selectedSlice, setSelectedSlice] = useState(null);
  const [showDiagnostics, setShowDiagnostics] = useState(false);

  if (!data) {
    return <div style={{ padding: 40, color: colors.fg2 }}>Loading analytics…</div>;
  }

  const w = data.windows?.[windowKey];
  const sliceCfg = SLICE_TABS.find(s => s.value === sliceTab);
  const sliceRows = w?.[sliceCfg?.source] || [];

  // ---- Header
  const header = (
    <div className="topbar">
      <div>
        <span className="brand">
          <span className="brand-mark"></span>
          <span className="brand-title">SugarBets</span>
          <span className="brand-sub">/ Model Performance</span>
        </span>
        <div style={{ marginTop: 8, fontFamily: 'var(--sb-font-mono)', fontSize: 11, color: colors.fg3, letterSpacing: '0.02em' }}>
          {w?.label || windowKey} · {w?.start} → {w?.end} · {w?.n_games} games
        </div>
      </div>
      <div className="filters">
        <a href="index.html" className="nav-link" style={{ marginRight: 4 }}>← Dashboard</a>
        <div className="filter-group">
          <span className="filter-label">Window</span>
          <Segmented value={windowKey} onChange={setWindowKey} options={WINDOWS} accent />
        </div>
      </div>
    </div>
  );

  return (
    <div className="app">
      {header}

      {/* HERO: bankroll curve + key money numbers */}
      <Hero w={w} />

      {/* Secondary KPIs */}
      <KpiRow w={w} />

      {/* Daily activity */}
      <DailyActivity w={w} />

      {/* Performance by market — single section */}
      <div className="h-section">
        <h2>Performance by market</h2>
        <span className="h-meta">Quarter-Kelly profit per market</span>
      </div>
      <div className="card">
        <ByMarket
          roi={w?.roi}
          kpropRoi={w?.k_prop_roi_trusted}
          batterHr={w?.batter_hr}
          totalsStatus={w?.totals_model_status}
        />
        {w?.totals_model_status && (
          <div style={{ marginTop: 16, paddingTop: 16, borderTop: '1px solid var(--sb-border)' }}>
            <div className="callout">
              <span className="callout-icon">!</span>
              <div>
                <strong>Totals signal weak.</strong>{' '}
                <span style={{ color: colors.fg2 }}>
                  Direction-correct rate <strong style={{ color: colors.amber }}>{w.totals_model_status.overall.direction_correct_pct}%</strong> trails
                  the <strong>{w.totals_model_status.breakeven_pct}%</strong> needed for break-even.
                  Totals bets gated to ≥{w.totals_model_status.gate_min_edge_pct}% edge until signal develops.
                </span>
              </div>
            </div>
          </div>
        )}
      </div>

      {/* Slice explorer — the analytical workhorse */}
      <div className="h-section">
        <h2>Where the model wins · loses</h2>
        <span className="h-meta">Click a slice for detail · dot size = sample</span>
      </div>
      <div className="card">
        <div className="subtabs">
          {SLICE_TABS.map(t => {
            const rows = w?.[t.source] || [];
            return (
              <button
                key={t.value}
                className={'subtab ' + (sliceTab === t.value ? 'active' : '')}
                onClick={() => { setSliceTab(t.value); setSelectedSlice(null); }}>
                {t.label} <span className="count">{rows.length}</span>
              </button>
            );
          })}
        </div>
        <SliceDotPlot
          rows={sliceRows}
          metric={sliceCfg.metric}
          vmin={sliceCfg.vmin}
          vmax={sliceCfg.vmax}
          refValue={sliceCfg.ref}
          refLabel={sliceCfg.refLabel}
          onSelect={(r) => setSelectedSlice(r === selectedSlice ? null : r)}
          selected={selectedSlice?.slice}
          minSample={sliceTab === 'team' ? 8 : 1}
        />
        {selectedSlice && (
          <SliceDrawer
            slice={selectedSlice}
            tab={sliceCfg}
            onClose={() => setSelectedSlice(null)}
          />
        )}
      </div>

      {/* Diagnostics — collapsible. Calibration + bias flags */}
      <div className="h-section">
        <h2>
          <button
            onClick={() => setShowDiagnostics(v => !v)}
            style={{
              background: 'none', border: 'none', color: 'inherit',
              font: 'inherit', cursor: 'pointer', padding: 0,
              display: 'flex', alignItems: 'center', gap: 8,
            }}>
            <span style={{ fontSize: 14, color: colors.fg3, transform: showDiagnostics ? 'rotate(90deg)' : 'rotate(0)', transition: '150ms ease', display: 'inline-block' }}>▶</span>
            Diagnostics
          </button>
        </h2>
        <span className="h-meta">
          {showDiagnostics ? 'Calibration · bias · data quality' : `${w?.bias_flags?.length || 0} flags · click to expand`}
        </span>
      </div>
      {showDiagnostics && (
        <div className="fade-in">
          <div className="row-2">
            <div className="card">
              <div className="card-header">
                <div>
                  <div className="card-title">Moneyline reliability</div>
                  <div className="card-sub">Each dot = a model-probability bucket · Size = sample</div>
                </div>
                <div className="legend-inline">
                  <span><span className="swatch" style={{ background: colors.green }}></span>≤5pp gap</span>
                  <span><span className="swatch" style={{ background: colors.amber }}></span>5–10pp</span>
                  <span><span className="swatch" style={{ background: colors.red }}></span>&gt;10pp</span>
                </div>
              </div>
              <CalibrationPlot buckets={w?.calibration} />
              <div className="card-note">
                Dots above the diagonal = model under-confident. Dots below = over-confident.
                The 50–55% bucket carries the most weight — it's where pick'em games live
                and where bias hurts ROI most.
              </div>
            </div>
            <div className="card">
              <div className="card-header">
                <div>
                  <div className="card-title">Bias flags</div>
                  <div className="card-sub">Slices where accuracy deviates by 2+ standard errors · top 12</div>
                </div>
              </div>
              <BiasFlags flags={w?.bias_flags} />
            </div>
          </div>
          <div className="row-2">
            <div className="card">
              <div className="card-header">
                <div>
                  <div className="card-title">Park run bias</div>
                  <div className="card-sub">Actual − predicted total runs · top 10 by magnitude</div>
                </div>
              </div>
              <ParkBias parks={w?.park_bias} />
            </div>
            <div className="card">
              <div className="card-header">
                <div>
                  <div className="card-title">K-prop ROI by edge band</div>
                  <div className="card-sub">
                    Trusted picks only · {w?.k_prop_roi_trusted?.n_total_bets || 0} bets ·
                    {w?.k_prop_roi_trusted?.skipped_untrusted || 0} skipped untrusted
                  </div>
                </div>
              </div>
              {w?.k_prop_roi_trusted?.buckets?.length ? (
                <KpropBands data={w.k_prop_roi_trusted} />
              ) : (
                <div style={{ color: colors.fg3, fontSize: 12, padding: 12 }}>No trusted K-prop bets in window.</div>
              )}
            </div>
          </div>
          <FillRates rates={data.fill_rates} />
        </div>
      )}

      <div style={{ marginTop: 40, paddingTop: 18, borderTop: '1px solid var(--sb-border)',
        fontSize: 11, color: colors.fg3, fontFamily: 'var(--sb-font-mono)',
        display: 'flex', justifyContent: 'space-between', flexWrap: 'wrap', gap: 12 }}>
        <span>Built {data.built_at?.slice(0,10)} · as of {data.as_of_date}</span>
        <span>Model projections for informational purposes only. Gamble responsibly.</span>
      </div>
    </div>
  );
}


// ---- K-prop ROI by edge bucket -------------------------------
function KpropBands({ data }) {
  if (!data || !data.buckets) return null;
  const rows = data.buckets;
  const side = data.side_split || {};

  return (
    <div>
      <div style={{ display: 'grid', gridTemplateColumns: '90px 36px 1fr 70px 60px', gap: 10,
        fontSize: 10, color: colors.fg3, fontFamily: 'var(--sb-font-mono)',
        textTransform: 'uppercase', letterSpacing: '0.04em',
        padding: '0 6px 6px', borderBottom: '1px solid var(--sb-border)', marginBottom: 4 }}>
        <span>Edge</span>
        <span style={{ textAlign: 'right' }}>n</span>
        <span>Hit rate vs 52% b/e</span>
        <span style={{ textAlign: 'right' }}>ROI</span>
        <span style={{ textAlign: 'right' }}>P/L</span>
      </div>
      {rows.map((b, i) => {
        const hit = b.hit_rate;
        const ok = hit >= 0.524;
        const roi = b.roi;
        return (
          <div key={i} className="slice-row" style={{ gridTemplateColumns: '90px 36px 1fr 70px 60px' }}>
            <div className="slice-name">{b.band}</div>
            <div className="slice-n">{b.n}</div>
            <div style={{ position: 'relative', height: 16 }}>
              <svg width="100%" height="16" preserveAspectRatio="none" viewBox="0 0 100 16" style={{ position: 'absolute', inset: 0 }}>
                <line x1={0} x2={100} y1={8} y2={8} stroke={colors.border} strokeWidth="1" />
                <line x1={52.4} x2={52.4} y1={2} y2={14} stroke={colors.fg3} strokeDasharray="2 2" opacity="0.6" />
                <circle cx={hit * 100} cy={8} r={3 + Math.sqrt(b.n / 5) * 2} fill={ok ? colors.green : colors.red} opacity={0.85} />
              </svg>
            </div>
            <div className="slice-val" style={{ color: roi >= 0 ? colors.green : colors.red }}>
              {roi >= 0 ? '+' : ''}{(roi*100).toFixed(1)}%
            </div>
            <div className="slice-val" style={{ color: b.profit_units >= 0 ? colors.green : colors.red }}>
              {b.profit_units >= 0 ? '+' : ''}{b.profit_units.toFixed(2)}u
            </div>
          </div>
        );
      })}
      <div style={{ marginTop: 12, paddingTop: 10, borderTop: '1px solid var(--sb-border)',
        display: 'flex', gap: 18, fontSize: 11, color: colors.fg2, fontFamily: 'var(--sb-font-mono)' }}>
        <span>Over <strong style={{ color: side.over?.roi >= 0 ? colors.green : colors.red, fontWeight: 600 }}>
          {side.over ? `${side.over.hits}/${side.over.n} · ${(side.over.roi*100).toFixed(1)}%` : '—'}
        </strong></span>
        <span>Under <strong style={{ color: side.under?.roi >= 0 ? colors.green : colors.red, fontWeight: 600 }}>
          {side.under ? `${side.under.hits}/${side.under.n} · ${(side.under.roi*100).toFixed(1)}%` : '—'}
        </strong></span>
      </div>
    </div>
  );
}


// ---- Slice drawer --------------------------------------------
function SliceDrawer({ slice, tab, onClose }) {
  return (
    <div className="drawer fade-in">
      <div className="drawer-title">
        <span>
          <span style={{ color: colors.fg3, fontSize: 11, fontFamily: 'var(--sb-font-mono)', textTransform: 'uppercase', letterSpacing: '0.04em', marginRight: 8 }}>
            {tab.label} ·
          </span>
          <span style={{ fontSize: 14, fontWeight: 600 }}>{slice.slice}</span>
        </span>
        <button className="drawer-close" onClick={onClose}>×</button>
      </div>
      <div className="drawer-stats">
        <div>
          <div className="drawer-stat-label">Sample</div>
          <div className="drawer-stat-val">{slice.n} games</div>
        </div>
        <div>
          <div className="drawer-stat-label">ML accuracy</div>
          <div className="drawer-stat-val" style={{ color: slice.ml_accuracy >= 0.524 ? colors.green : colors.red }}>
            {(slice.ml_accuracy*100).toFixed(1)}%
          </div>
        </div>
        <div>
          <div className="drawer-stat-label">O/U accuracy</div>
          <div className="drawer-stat-val" style={{ color: slice.ou_accuracy >= 0.524 ? colors.green : colors.red }}>
            {slice.ou_accuracy != null ? (slice.ou_accuracy*100).toFixed(1) + '%' : '—'}
          </div>
        </div>
        <div>
          <div className="drawer-stat-label">Totals bias</div>
          <div className="drawer-stat-val" style={{ color: Math.abs(slice.total_bias_runs || 0) < 1 ? colors.fg1 : colors.amber }}>
            {slice.total_bias_runs != null ? (slice.total_bias_runs >= 0 ? '+' : '') + slice.total_bias_runs.toFixed(2) + 'r' : '—'}
          </div>
        </div>
      </div>
      <div style={{ marginTop: 12, fontSize: 12, color: colors.fg2, lineHeight: 1.5 }}>
        {sliceInterpretation(slice)}
      </div>
    </div>
  );
}

function sliceInterpretation(s) {
  const acc = s.ml_accuracy;
  const bias = s.total_bias_runs;
  const parts = [];
  if (acc >= 0.60) parts.push(`Moneyline picks are unusually strong here (${(acc*100).toFixed(0)}%).`);
  else if (acc <= 0.45) parts.push(`Moneyline picks underperform here (${(acc*100).toFixed(0)}%) — worth investigating.`);
  else parts.push(`Moneyline performance is near baseline.`);
  if (Math.abs(bias || 0) > 1) parts.push(`Totals model is biased ${bias > 0 ? 'low' : 'high'} by ${Math.abs(bias).toFixed(1)} runs.`);
  if (s.n < 20) parts.push(`Sample is small (n=${s.n}) — interpret with caution.`);
  return parts.join(' ');
}


// ---- Fill rates ----------------------------------------------
function FillRates({ rates }) {
  if (!rates) return null;
  const entries = Object.entries(rates).map(([k, v]) => {
    const [num, den] = v.split('/').map(Number);
    return { name: k, num, den, ratio: den > 0 ? num/den : 0 };
  });
  return (
    <div className="card">
      <div className="card-header">
        <div>
          <div className="card-title">Data quality</div>
          <div className="card-sub">Coverage of inputs the model relies on</div>
        </div>
      </div>
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(6, 1fr)', gap: 16 }}>
        {entries.map((e, i) => {
          const pct = e.ratio;
          const color = pct >= 0.95 ? colors.green : pct >= 0.8 ? colors.amber : colors.red;
          return (
            <div key={i}>
              <div style={{ fontSize: 10, color: colors.fg3, fontFamily: 'var(--sb-font-mono)', textTransform: 'uppercase', letterSpacing: '0.04em' }}>
                {e.name.replace(/_/g, ' ')}
              </div>
              <div style={{ fontFamily: 'var(--sb-font-mono)', fontVariantNumeric: 'tabular-nums', fontSize: 15, fontWeight: 600, marginTop: 4, color }}>
                {(pct*100).toFixed(1)}%
              </div>
              <div style={{ fontFamily: 'var(--sb-font-mono)', fontSize: 10, color: colors.fg3, marginTop: 2 }}>
                {e.num}/{e.den}
              </div>
              <svg width="100%" height="3" style={{ display: 'block', marginTop: 4 }} preserveAspectRatio="none" viewBox="0 0 100 3">
                <rect x="0" y="0" width="100" height="3" fill={colors.border} />
                <rect x="0" y="0" width={pct*100} height="3" fill={color} />
              </svg>
            </div>
          );
        })}
      </div>
    </div>
  );
}


// ---- Mount ---------------------------------------------------
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

Object.assign(window, { App, Hero, KpiRow, DailyActivity, KpropBands, SliceDrawer, FillRates });
