import { Fragment, useMemo, useState } from 'react'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { Waves, Play, Save, Trash2, X } from 'lucide-react'
import clsx from 'clsx'
import { api } from '../hooks/useApi'
import {
  WaveletTriggerKind, WaveletPositionMode, WaveletExitVariant, WaveletOptimizationResult, WaveletDateRange,
  WAVELET_TRIGGER_KINDS, runFullOptimization, formatBandIndex,
} from '../lib/waveletTrade'

const ASSET_CLASS_LABELS: Record<string, string> = {
  energy: 'Énergie', metals: 'Métaux', agriculture: 'Agriculture',
  indices: 'Indices', etfs: 'ETFs', equities: 'Actions', forex: 'Forex',
}
const WAVELET_FAMILIES = ['gmw', 'morlet', 'bump'] as const
const ALL_MODES: WaveletPositionMode[] = ['long_only', 'short_only', 'both']
const MODE_LABELS: Record<WaveletPositionMode, string> = { long_only: 'Long', short_only: 'Short', both: 'Les deux' }
const ALL_BANDS = [0, 1, 2, 3, 4, 5]

function ChipInput({ values, onChange, placeholder, min = 0 }: { values: number[]; onChange: (v: number[]) => void; placeholder: string; min?: number }) {
  const [input, setInput] = useState('')
  const add = () => {
    const n = Number(input)
    if (!isNaN(n) && n >= min && !values.includes(n)) onChange([...values, n].sort((a, b) => a - b))
    setInput('')
  }
  return (
    <div className="flex items-center gap-1.5 flex-wrap">
      {values.map(v => (
        <span key={v} className="flex items-center gap-1 bg-dark-700 border border-slate-700/40 rounded px-1.5 py-0.5 text-[10px] text-slate-300">
          {v}
          <button onClick={() => onChange(values.filter(x => x !== v))} className="text-slate-600 hover:text-red-400">
            <X className="w-2.5 h-2.5" />
          </button>
        </span>
      ))}
      <input
        value={input} onChange={e => setInput(e.target.value)}
        onKeyDown={e => e.key === 'Enter' && add()}
        placeholder={placeholder}
        className="w-16 bg-dark-900 border border-slate-700/40 rounded px-1.5 py-0.5 text-[10px] text-white"
      />
    </div>
  )
}

export default function WaveletsSimulation() {
  const qc = useQueryClient()
  const [subTab, setSubTab] = useState<'optimisation' | 'summary'>('optimisation')

  const { data: watchlistUniverse } = useQuery({
    queryKey: ['wavelet-sim-instruments'],
    queryFn: () => api.get('/market/watchlist').then(r => r.data as Record<string, { symbol: string; name: string; currency: string }[]>),
    staleTime: 60 * 60_000,
  })

  // ── Form state ──────────────────────────────────────────────────────────────
  const [selectedInstruments, setSelectedInstruments] = useState<Set<string>>(new Set())
  const [period, setPeriod] = useState('2y')
  const [useCustomRange, setUseCustomRange] = useState(false)
  const [customStart, setCustomStart] = useState('2013-01-01')
  const [customEnd, setCustomEnd] = useState('2019-12-31')
  const [levels, setLevels] = useState(4)
  const [lookbacks, setLookbacks] = useState<number[]>([90, 130])
  const [wavelets, setWavelets] = useState<Set<string>>(new Set(['gmw']))
  const [method, setMethod] = useState<'cwt' | 'ssq'>('cwt')
  const [bands, setBands] = useState<Set<number>>(new Set([0, 1, 2, 3]))
  const [kinds, setKinds] = useState<Set<WaveletTriggerKind>>(new Set(['extremum', 'level_threshold']))
  const [modes, setModes] = useState<Set<WaveletPositionMode>>(new Set(['both']))
  const [takeProfits, setTakeProfits] = useState<number[]>([0])
  const [stopLosses, setStopLosses] = useState<number[]>([0])
  const [filterBand, setFilterBand] = useState<number | null>(null)
  const [simName, setSimName] = useState(() => `Run ${new Date().toISOString().slice(0, 16).replace('T', ' ')}`)

  // ── Run state ────────────────────────────────────────────────────────────────
  const [running, setRunning] = useState(false)
  const [progress, setProgress] = useState<{ done: number; total: number; label: string } | null>(null)
  const [failures, setFailures] = useState<string[]>([])
  const [results, setResults] = useState<WaveletOptimizationResult[]>([])
  const [activeSimId, setActiveSimId] = useState<string | null>(null)

  // ── Results table filters ───────────────────────────────────────────────────
  const [minTrades, setMinTrades] = useState(1)
  const [sortKey, setSortKey] = useState<'totalReturnPct' | 'winRate' | 'tradeCount'>('totalReturnPct')
  const [expandedRow, setExpandedRow] = useState<number | null>(null)

  const { data: savedSims } = useQuery({
    queryKey: ['wavelet-simulations'],
    queryFn: () => api.get('/wavelet/simulations').then(r => r.data as any[]),
  })

  const toggleSetValue = <T,>(set: Set<T>, v: T, setter: (s: Set<T>) => void) => {
    const next = new Set(set)
    if (next.has(v)) next.delete(v); else next.add(v)
    setter(next)
  }

  const runOptimization = async () => {
    if (selectedInstruments.size === 0 || lookbacks.length === 0 || wavelets.size === 0 || kinds.size === 0) return
    if (useCustomRange && !customStart) return
    setRunning(true)
    setFailures([])
    setResults([])
    setExpandedRow(null)

    const dateRange: WaveletDateRange = useCustomRange ? { startDate: customStart, endDate: customEnd || undefined } : null

    // Create the persisted run up-front so results can be appended incrementally —
    // a run spanning many combos shouldn't lose progress to a refresh/interruption.
    const form = {
      instruments: [...selectedInstruments], period, levels, wavelets: [...wavelets],
      lookbacks, bands: [...bands], kinds: [...kinds], modes: [...modes],
      takeProfits, stopLosses, method, filterBand,
      customRange: dateRange,
    }
    const created = await api.post('/wavelet/simulations', { name: simName, form, results: [] }).then(r => r.data)
    setActiveSimId(created.id)

    const exitVariants: WaveletExitVariant[] = []
    for (const tp of takeProfits) for (const sl of stopLosses) {
      exitVariants.push({ exitStyle: tp === 0 && sl === 0 ? 'signal' : 'signal_or_pct', takeProfitPct: tp || null, stopLossPct: sl || null })
    }

    const allResults: WaveletOptimizationResult[] = []
    await runFullOptimization(
      [...selectedInstruments], lookbacks, [...wavelets], period, levels,
      [...bands], [...kinds], [...modes], exitVariants,
      (done, total, label, fails) => { setProgress({ done, total, label }); setFailures([...fails]) },
      async (batch) => {
        allResults.push(...batch)
        setResults([...allResults])
        try { await api.patch(`/wavelet/simulations/${created.id}`, { append_results: batch }) } catch { /* non-blocking */ }
      },
      method,
      filterBand,
      dateRange,
    )
    setRunning(false)
    qc.invalidateQueries({ queryKey: ['wavelet-simulations'] })
  }

  const loadSimulation = async (id: string) => {
    const sim = await api.get(`/wavelet/simulations/${id}`).then(r => r.data)
    setResults(sim.results ?? [])
    setActiveSimId(id)
    setSimName(sim.name)
  }

  const deleteSimulation = async (id: string) => {
    await api.delete(`/wavelet/simulations/${id}`)
    if (activeSimId === id) { setActiveSimId(null); setResults([]) }
    qc.invalidateQueries({ queryKey: ['wavelet-simulations'] })
  }

  const filteredResults = useMemo(() => {
    return [...results]
      .filter(r => r.tradeCount >= minTrades)
      .sort((a, b) => (b[sortKey] as number) - (a[sortKey] as number))
  }, [results, minTrades, sortKey])

  const perSymbolBreakdown = (r: WaveletOptimizationResult) =>
    results.filter(x => x.symbol === r.symbol && x.lookback === r.lookback && x.wavelet === r.wavelet
      && x.triggerKind === r.triggerKind && x.bandIndex === r.bandIndex && x.mode === r.mode
      && x.exitStyle === r.exitStyle && x.takeProfitPct === r.takeProfitPct && x.stopLossPct === r.stopLossPct)

  const configSummary = useMemo(() => {
    const symbols = new Set(results.map(r => r.symbol))
    const totalReturn = results.reduce((s, r) => s + r.totalReturnPct, 0)
    const avgWinRate = results.length ? results.reduce((s, r) => s + r.winRate, 0) / results.length : 0
    return { configs: results.length, symbols: symbols.size, totalReturn, avgWinRate }
  }, [results])

  return (
    <div className="p-6 space-y-5">
      <div className="flex items-center justify-between">
        <div>
          <h1 className="text-xl font-bold text-white flex items-center gap-2">
            <Waves className="w-5 h-5 text-blue-400" /> Wavelets Simulation
          </h1>
          <p className="text-xs text-slate-500 mt-0.5">
            Décomposition en bandes de fréquence (CWT/SSQ) + backtest multi-instruments — portage de InstrumentSimulator
          </p>
        </div>
      </div>

      <div className="flex gap-1 bg-dark-700 p-1 rounded w-fit">
        {(['optimisation', 'summary'] as const).map(k => (
          <button key={k} onClick={() => setSubTab(k)}
            className={clsx('px-3 py-1.5 rounded text-sm transition-colors capitalize',
              subTab === k ? 'bg-blue-600 text-white' : 'text-slate-400 hover:text-slate-200')}>
            {k === 'optimisation' ? 'Optimisation' : 'Summary'}
          </button>
        ))}
      </div>

      {subTab === 'optimisation' && (
        <div className="space-y-4">
          {/* ── Config form ── */}
          <div className="card space-y-4">
            <div>
              <div className="section-title mb-2">Instruments</div>
              <div className="grid grid-cols-4 gap-3 max-h-56 overflow-y-auto">
                {Object.entries(watchlistUniverse ?? {}).map(([cls, items]) => (
                  <div key={cls}>
                    <div className="text-[10px] text-slate-500 uppercase mb-1">{ASSET_CLASS_LABELS[cls] ?? cls}</div>
                    <div className="space-y-0.5">
                      {items.map(it => (
                        <label key={it.symbol} className="flex items-center gap-1.5 text-[10px] text-slate-300 cursor-pointer">
                          <input type="checkbox" checked={selectedInstruments.has(it.symbol)}
                            onChange={() => toggleSetValue(selectedInstruments, it.symbol, setSelectedInstruments)} />
                          <span className="truncate">{it.symbol}</span>
                        </label>
                      ))}
                    </div>
                  </div>
                ))}
              </div>
              <div className="text-[10px] text-slate-600 mt-1">{selectedInstruments.size} instrument(s) sélectionné(s)</div>
            </div>

            <div className="grid grid-cols-4 gap-4 text-xs">
              <div>
                <div className="flex items-center justify-between mb-1">
                  <span className="text-slate-500">Période</span>
                  <label className="flex items-center gap-1 cursor-pointer text-slate-500">
                    <input type="checkbox" checked={useCustomRange} onChange={e => setUseCustomRange(e.target.checked)} /> Personnalisée
                  </label>
                </div>
                {useCustomRange ? (
                  <div className="flex items-center gap-1">
                    <input type="date" value={customStart} onChange={e => setCustomStart(e.target.value)}
                      className="bg-dark-900 border border-slate-700/40 rounded px-1.5 py-1 text-white w-full" />
                    <span className="text-slate-600">→</span>
                    <input type="date" value={customEnd} onChange={e => setCustomEnd(e.target.value)}
                      className="bg-dark-900 border border-slate-700/40 rounded px-1.5 py-1 text-white w-full" />
                  </div>
                ) : (
                  <select value={period} onChange={e => setPeriod(e.target.value)}
                    className="bg-dark-900 border border-slate-700/40 rounded px-2 py-1 text-white w-full">
                    {['6mo', '1y', '2y', '5y'].map(p => <option key={p} value={p}>{p}</option>)}
                  </select>
                )}
              </div>
              <div>
                <div className="text-slate-500 mb-1">Niveaux</div>
                <input type="number" min={2} max={6} value={levels}
                  onChange={e => setLevels(Math.max(2, Math.min(6, Number(e.target.value) || 4)))}
                  className="bg-dark-900 border border-slate-700/40 rounded px-2 py-1 text-white w-full" />
              </div>
              <div>
                <div className="text-slate-500 mb-1">Méthode</div>
                <div className="flex gap-2 pt-1">
                  {(['cwt', 'ssq'] as const).map(m => (
                    <label key={m} className="flex items-center gap-1 cursor-pointer">
                      <input type="radio" checked={method === m} onChange={() => setMethod(m)} /> {m.toUpperCase()}
                    </label>
                  ))}
                </div>
              </div>
              <div>
                <div className="text-slate-500 mb-1">Nom de la simulation</div>
                <input value={simName} onChange={e => setSimName(e.target.value)}
                  className="bg-dark-900 border border-slate-700/40 rounded px-2 py-1 text-white w-full" />
              </div>
            </div>

            <div className="grid grid-cols-2 gap-4 text-xs">
              <div>
                <div className="text-slate-500 mb-1">Fenêtres glissantes à tester (jours, ≥32)</div>
                <ChipInput values={lookbacks} onChange={setLookbacks} placeholder="130" min={32} />
              </div>
              <div>
                <div className="text-slate-500 mb-1">Ondelettes</div>
                <div className="flex gap-3">
                  {WAVELET_FAMILIES.map(w => (
                    <label key={w} className="flex items-center gap-1 cursor-pointer">
                      <input type="checkbox" checked={wavelets.has(w)} onChange={() => toggleSetValue(wavelets, w, setWavelets)} /> {w}
                    </label>
                  ))}
                </div>
              </div>
            </div>

            <div className="grid grid-cols-3 gap-4 text-xs">
              <div>
                <div className="text-slate-500 mb-1">Bandes à tester</div>
                <div className="flex gap-2 flex-wrap">
                  {ALL_BANDS.slice(0, levels).map(b => (
                    <label key={b} className="flex items-center gap-1 cursor-pointer">
                      <input type="checkbox" checked={bands.has(b)} onChange={() => toggleSetValue(bands, b, setBands)} /> {b}
                    </label>
                  ))}
                </div>
              </div>
              <div>
                <div className="text-slate-500 mb-1">Modes à tester</div>
                <div className="flex gap-2 flex-wrap">
                  {ALL_MODES.map(m => (
                    <label key={m} className="flex items-center gap-1 cursor-pointer">
                      <input type="checkbox" checked={modes.has(m)} onChange={() => toggleSetValue(modes, m, setModes)} /> {MODE_LABELS[m]}
                    </label>
                  ))}
                </div>
              </div>
              <div>
                <div className="text-slate-500 mb-1">Take-profit / Stop-loss % (0 = aucun)</div>
                <div className="flex flex-col gap-1">
                  <ChipInput values={takeProfits} onChange={setTakeProfits} placeholder="TP%" />
                  <ChipInput values={stopLosses} onChange={setStopLosses} placeholder="SL%" />
                </div>
              </div>
            </div>

            <div>
              <div className="text-slate-500 text-xs mb-1" title="Un retournement (extremum, seuil...) d'une bande à tester ne se déclenche que si cette bande secondaire confirme la même direction (pente positive pour un achat/cover, négative pour une vente/short) — ex. creux de bande 1 achète seulement si bande 3 monte.">
                Filtre de tendance (bande de confirmation)
              </div>
              <div className="flex gap-2 flex-wrap text-xs">
                <label className="flex items-center gap-1 cursor-pointer">
                  <input type="radio" checked={filterBand === null} onChange={() => setFilterBand(null)} /> Aucun
                </label>
                {ALL_BANDS.slice(0, levels).map(b => (
                  <label key={b} className="flex items-center gap-1 cursor-pointer">
                    <input type="radio" checked={filterBand === b} onChange={() => setFilterBand(b)} /> Bande {b}
                  </label>
                ))}
              </div>
            </div>

            <div>
              <div className="text-slate-500 text-xs mb-1">Types de déclencheur</div>
              <div className="flex gap-3 flex-wrap text-xs">
                {WAVELET_TRIGGER_KINDS.filter(k => method === 'ssq' || (k.value !== 'ridge_shift' && k.value !== 'energy_threshold')).map(k => (
                  <label key={k.value} className="flex items-center gap-1.5 cursor-pointer">
                    <input type="checkbox" checked={kinds.has(k.value)} onChange={() => toggleSetValue(kinds, k.value, setKinds)} />
                    {k.label}
                  </label>
                ))}
              </div>
            </div>

            <button onClick={runOptimization} disabled={running || selectedInstruments.size === 0 || (useCustomRange && !customStart)}
              className="flex items-center gap-1.5 px-4 py-2 rounded bg-blue-600 hover:bg-blue-500 text-white text-sm font-medium disabled:opacity-40 transition-colors">
              <Play className="w-4 h-4" /> {running ? 'Optimisation en cours…' : "Lancer l'optimisation"}
            </button>

            {progress && (
              <div className="space-y-1">
                <div className="flex items-center justify-between text-[10px] text-slate-500">
                  <span>{progress.label}</span>
                  <span>{progress.done}/{progress.total}</span>
                </div>
                <div className="bg-dark-700 rounded-full h-1.5">
                  <div className="bg-blue-500 h-1.5 rounded-full transition-all" style={{ width: `${progress.total ? (progress.done / progress.total) * 100 : 0}%` }} />
                </div>
                {failures.length > 0 && (
                  <div className="text-[10px] text-red-400">{failures.length} échec(s) : {failures.slice(0, 3).join(' · ')}</div>
                )}
              </div>
            )}
          </div>

          {/* ── Saved simulations ── */}
          <div className="card">
            <div className="section-title mb-2">Simulations sauvegardées</div>
            {(savedSims ?? []).length === 0 ? (
              <div className="text-xs text-slate-600">Aucune simulation sauvegardée.</div>
            ) : (
              <div className="space-y-1">
                {savedSims!.map(s => (
                  <div key={s.id} className={clsx('flex items-center justify-between px-2 py-1.5 rounded text-xs',
                    activeSimId === s.id ? 'bg-blue-900/20 border border-blue-700/30' : 'bg-dark-800/60')}>
                    <div>
                      <span className="text-slate-200 font-medium">{s.name}</span>
                      <span className="text-slate-600 ml-2">{s.result_count} résultats · {s.updated_at?.slice(0, 16)}</span>
                    </div>
                    <div className="flex items-center gap-2">
                      <button onClick={() => loadSimulation(s.id)} className="text-blue-400 hover:text-blue-300">Charger</button>
                      <button onClick={() => deleteSimulation(s.id)} className="text-slate-600 hover:text-red-400"><Trash2 className="w-3.5 h-3.5" /></button>
                    </div>
                  </div>
                ))}
              </div>
            )}
          </div>

          {/* ── Results ── */}
          {results.length > 0 && (
            <div className="card space-y-3">
              <div className="flex items-center gap-4 text-xs text-slate-400 flex-wrap">
                <span>{configSummary.configs} configs testées</span>
                <span>{configSummary.symbols} instrument(s)</span>
                <span>Retour cumulé: <span className={configSummary.totalReturn >= 0 ? 'text-emerald-400' : 'text-red-400'}>{configSummary.totalReturn.toFixed(1)}%</span></span>
                <span>Win rate moy.: {(configSummary.avgWinRate * 100).toFixed(0)}%</span>
                <div className="ml-auto flex items-center gap-2">
                  <label className="flex items-center gap-1">Min. trades
                    <input type="number" min={0} value={minTrades} onChange={e => setMinTrades(Number(e.target.value) || 0)}
                      className="w-12 bg-dark-900 border border-slate-700/40 rounded px-1 py-0.5 text-white" />
                  </label>
                  <select value={sortKey} onChange={e => setSortKey(e.target.value as any)}
                    className="bg-dark-900 border border-slate-700/40 rounded px-1.5 py-0.5 text-slate-300">
                    <option value="totalReturnPct">Trier: Retour</option>
                    <option value="winRate">Trier: Win rate</option>
                    <option value="tradeCount">Trier: Nb trades</option>
                  </select>
                </div>
              </div>

              <div className="overflow-x-auto">
                <table className="w-full text-xs">
                  <thead>
                    <tr className="text-slate-500 border-b border-slate-700/30 text-left">
                      <th className="py-1.5 pr-3">Symbole</th>
                      <th className="pr-3">Lookback</th>
                      <th className="pr-3">Ondelette</th>
                      <th className="pr-3">Bande</th>
                      <th className="pr-3">Trigger</th>
                      <th className="pr-3">Mode</th>
                      <th className="pr-3 text-right">Retour</th>
                      <th className="pr-3 text-right">Win rate</th>
                      <th className="pr-3 text-right">Trades</th>
                      <th className="pr-3 text-right" title="Gain moyen / max / min parmi les trades gagnants — un gain max qui écrase les autres révèle un résultat porté par un seul trade, pas une vraie récurrence">Gains (moy/max/min)</th>
                      <th className="pr-3 text-right" title="Perte moyenne / la pire / la plus petite parmi les trades perdants">Pertes (moy/max/min)</th>
                    </tr>
                  </thead>
                  <tbody>
                    {filteredResults.slice(0, 100).map((r, i) => (
                      <Fragment key={i}>
                        <tr onClick={() => setExpandedRow(expandedRow === i ? null : i)}
                          className="border-b border-slate-800/40 hover:bg-dark-800/40 cursor-pointer">
                          <td className="py-1 pr-3 font-mono text-slate-200">{r.symbol}</td>
                          <td className="pr-3 text-slate-400">{r.lookback}j</td>
                          <td className="pr-3 text-slate-400">{r.wavelet}</td>
                          <td className="pr-3 text-slate-400">{formatBandIndex(r.bandIndex)}</td>
                          <td className="pr-3 text-slate-400">{r.triggerKind}</td>
                          <td className="pr-3 text-slate-400">{MODE_LABELS[r.mode]}</td>
                          <td className={clsx('pr-3 text-right font-mono font-bold', r.totalReturnPct >= 0 ? 'text-emerald-400' : 'text-red-400')}>
                            {r.totalReturnPct >= 0 ? '+' : ''}{r.totalReturnPct.toFixed(1)}%
                          </td>
                          <td className="pr-3 text-right font-mono text-slate-300">{(r.winRate * 100).toFixed(0)}%</td>
                          <td className="pr-3 text-right font-mono text-slate-300">{r.tradeCount}</td>
                          <td className="pr-3 text-right font-mono text-emerald-400/90 whitespace-nowrap">
                            {r.avgGainPct != null ? `${r.avgGainPct.toFixed(1)} / ${r.maxGainPct!.toFixed(1)} / ${r.minGainPct!.toFixed(1)}` : '—'}
                          </td>
                          <td className="pr-3 text-right font-mono text-red-400/90 whitespace-nowrap">
                            {r.avgLossPct != null ? `${r.avgLossPct.toFixed(1)} / ${r.maxLossPct!.toFixed(1)} / ${r.minLossPct!.toFixed(1)}` : '—'}
                          </td>
                        </tr>
                        {expandedRow === i && (
                          <tr key={`${i}-detail`} className="bg-dark-900/40">
                            <td colSpan={11} className="p-2">
                              <div className="text-[10px] text-slate-500 mb-1">Détail par instrument (même config)</div>
                              <div className="grid grid-cols-4 gap-1.5">
                                {perSymbolBreakdown(r).map((d, j) => (
                                  <div key={j} className="flex items-center justify-between bg-dark-800/60 rounded px-2 py-1 text-[10px]">
                                    <span className="font-mono text-slate-300">{d.symbol}</span>
                                    <span className={clsx('font-mono', d.totalReturnPct >= 0 ? 'text-emerald-400' : 'text-red-400')}>
                                      {d.totalReturnPct >= 0 ? '+' : ''}{d.totalReturnPct.toFixed(1)}%
                                    </span>
                                    <span className="text-slate-600">{d.tradeCount}tr</span>
                                  </div>
                                ))}
                              </div>
                            </td>
                          </tr>
                        )}
                      </Fragment>
                    ))}
                  </tbody>
                </table>
              </div>
            </div>
          )}
        </div>
      )}

      {subTab === 'summary' && <WaveletSummaryTab savedSims={savedSims ?? []} />}
    </div>
  )
}

function WaveletSummaryTab({ savedSims }: { savedSims: any[] }) {
  const [selectedIds, setSelectedIds] = useState<Set<string>>(new Set())
  const [aggregate, setAggregate] = useState<{ configs: number; avgReturn: number; avgWinRate: number; avgTrades: number } | null>(null)
  const [loading, setLoading] = useState(false)

  const toggle = (id: string) => {
    const next = new Set(selectedIds)
    if (next.has(id)) next.delete(id); else next.add(id)
    setSelectedIds(next)
  }

  const computeAggregate = async () => {
    if (selectedIds.size === 0) return
    setLoading(true)
    try {
      const all: WaveletOptimizationResult[] = []
      for (const id of selectedIds) {
        const sim = await api.get(`/wavelet/simulations/${id}`).then(r => r.data)
        all.push(...(sim.results ?? []))
      }
      if (all.length === 0) { setAggregate(null); return }
      setAggregate({
        configs: all.length,
        avgReturn: all.reduce((s, r) => s + r.totalReturnPct, 0) / all.length,
        avgWinRate: all.reduce((s, r) => s + r.winRate, 0) / all.length,
        avgTrades: all.reduce((s, r) => s + r.tradeCount, 0) / all.length,
      })
    } finally {
      setLoading(false)
    }
  }

  return (
    <div className="card space-y-3">
      <div className="section-title">Sélectionner des runs à agréger</div>
      {savedSims.length === 0 ? (
        <div className="text-xs text-slate-600">Aucune simulation sauvegardée.</div>
      ) : (
        <div className="space-y-1">
          {savedSims.map(s => (
            <label key={s.id} className="flex items-center gap-2 text-xs text-slate-300 cursor-pointer">
              <input type="checkbox" checked={selectedIds.has(s.id)} onChange={() => toggle(s.id)} />
              {s.name} <span className="text-slate-600">({s.result_count} résultats)</span>
            </label>
          ))}
        </div>
      )}
      <button onClick={computeAggregate} disabled={loading || selectedIds.size === 0}
        className="px-3 py-1.5 rounded bg-blue-600 hover:bg-blue-500 text-white text-xs font-medium disabled:opacity-40">
        {loading ? 'Calcul…' : 'Agréger'}
      </button>
      {aggregate && (
        <div className="grid grid-cols-4 gap-3 pt-2 border-t border-slate-700/30 text-xs">
          <div><div className="text-slate-600">Configs</div><div className="text-slate-200 font-mono">{aggregate.configs}</div></div>
          <div><div className="text-slate-600">Retour moy.</div><div className={clsx('font-mono', aggregate.avgReturn >= 0 ? 'text-emerald-400' : 'text-red-400')}>{aggregate.avgReturn.toFixed(2)}%</div></div>
          <div><div className="text-slate-600">Win rate moy.</div><div className="text-slate-200 font-mono">{(aggregate.avgWinRate * 100).toFixed(1)}%</div></div>
          <div><div className="text-slate-600">Trades moy.</div><div className="text-slate-200 font-mono">{aggregate.avgTrades.toFixed(1)}</div></div>
        </div>
      )}
    </div>
  )
}
