import { useState, useEffect, useCallback, useRef } from 'react'
import {
  Search, RefreshCw, Plus, Trash2, Edit3, Sparkles, ExternalLink,
  ChevronDown, ChevronUp, Loader2, CheckCircle, AlertCircle, X,
  SlidersHorizontal, BarChart2, Star, Users
} from 'lucide-react'
import clsx from 'clsx'

// ── Types ─────────────────────────────────────────────────────────────────────

interface SourceRef {
  title: string
  source: string
  url: string
  date: string
  original_score: number
}

interface InstrumentImpact {
  id: number
  instrument_id: string
  impact_score: number
  direction: 'bullish' | 'bearish' | 'neutral' | 'depends_on_outcome'
  rationale: string
  confidence: number
  ai_generated: number
  manually_adjusted: number
  adjusted_score: number | null
  adjusted_direction: string | null
  override_rationale: string
}

interface MarketEvent {
  id: number
  name: string
  start_date: string
  end_date: string | null
  level: 'long' | 'medium' | 'short'
  category: string
  sub_type: string
  description: string
  market_impact: string
  affected_assets: string[]
  impact_score: number
  absorption_pct: number | null
  source_refs: SourceRef[]
  origin?: string
  created_at?: string
  impacts?: InstrumentImpact[]
  evaluated?: boolean
}

// ── Constants ─────────────────────────────────────────────────────────────────

const CAT_COLORS: Record<string, string> = {
  event_calendar: 'bg-amber-500/20 text-amber-300 border-amber-500/30',
  geopolitical:   'bg-red-500/20 text-red-300 border-red-500/30',
  fundamental:    'bg-emerald-500/20 text-emerald-300 border-emerald-500/30',
  report:         'bg-blue-500/20 text-blue-300 border-blue-500/30',
  sentiment:      'bg-purple-500/20 text-purple-300 border-purple-500/30',
  technical:      'bg-cyan-500/20 text-cyan-300 border-cyan-500/30',
}
const CAT_DOT: Record<string, string> = {
  event_calendar: 'bg-amber-400',
  geopolitical:   'bg-red-400',
  fundamental:    'bg-emerald-400',
  report:         'bg-blue-400',
  sentiment:      'bg-purple-400',
  technical:      'bg-cyan-400',
}
const LEVEL_COLORS: Record<string, string> = {
  long:   'text-purple-400',
  medium: 'text-amber-400',
  short:  'text-emerald-400',
}
const ORIGIN_META: Record<string, { label: string; icon: string; color: string; description: string }> = {
  bootstrap_macro:     { label: 'Bootstrap macro',  icon: '📚', color: 'text-slate-400',  description: 'Données historiques macro/géopolitiques chargées au démarrage' },
  bootstrap_eco:       { label: 'Bootstrap éco',    icon: '📅', color: 'text-amber-400',  description: 'Calendrier économique FRED historique (FOMC, CPI, NFP…)' },
  bootstrap_ma:        { label: 'Bootstrap MA',      icon: '📈', color: 'text-cyan-400',   description: 'Signal technique MA détecté sur données historiques (yfinance)' },
  bootstrap_legacy:    { label: 'Bootstrap legacy',  icon: '🗄️', color: 'text-slate-500',  description: 'Données initiales (bootstrap, origine exacte inconnue)' },
  detector_news:       { label: 'News RSS',           icon: '📰', color: 'text-red-400',   description: 'Généré depuis un flux RSS — validé par IA (GPT-4o-mini)' },
  detector_eco:        { label: 'Surprise FRED',      icon: '📊', color: 'text-amber-400', description: 'Surprise économique FRED détectée par le cycle (z-score > seuil)' },
  detector_technical:  { label: 'Signal technique',   icon: '📐', color: 'text-cyan-400',  description: 'Croisement MA50/MA100/MA200 détecté automatiquement (yfinance)' },
  detector_report:     { label: 'Rapport instit.',    icon: '📋', color: 'text-blue-400',  description: 'Rapport institutionnel haute importance (COT, EIA, Fed…)' },
  manual:              { label: 'Manuel',             icon: '✏️', color: 'text-emerald-400', description: 'Créé manuellement dans l\'interface' },
  unknown:             { label: 'Inconnu',            icon: '❓', color: 'text-slate-600',  description: 'Origine non tracée (données antérieures au versioning)' },
}

const DIR_COLORS: Record<string, string> = {
  bullish:            'text-emerald-400',
  bearish:            'text-red-400',
  neutral:            'text-slate-400',
  depends_on_outcome: 'text-amber-400',
}
const DIR_ICONS: Record<string, string> = {
  bullish: '▲', bearish: '▼', neutral: '●', depends_on_outcome: '◆',
}

const CATEGORIES = ['event_calendar', 'geopolitical', 'fundamental', 'report', 'sentiment', 'technical']
const LEVELS     = ['long', 'medium', 'short']
const ALL_INSTRUMENTS = [
  'SPY','QQQ','IWM','EEM','EFA','GLD','SLV','USO','UNG','TLT',
  'HYG','EURUSD=X','USDJPY=X','GBPUSD=X','VXX','AAPL','NVDA','GS','XOM','BTC-USD',
]

// ── Helpers ───────────────────────────────────────────────────────────────────

function ScoreBar({ score, max = 1 }: { score: number; max?: number }) {
  const pct = Math.round((score / max) * 100)
  const color = score >= 0.7 ? 'bg-red-500' : score >= 0.5 ? 'bg-amber-500' : 'bg-emerald-500'
  return (
    <div className="flex items-center gap-2">
      <div className="flex-1 h-1.5 bg-slate-700 rounded-full overflow-hidden">
        <div className={`h-full rounded-full ${color}`} style={{ width: `${pct}%` }} />
      </div>
      <span className="text-xs text-slate-400 w-8 text-right">{score.toFixed(2)}</span>
    </div>
  )
}

function CatBadge({ cat }: { cat: string }) {
  return (
    <span className={clsx('inline-flex items-center gap-1 px-2 py-0.5 rounded border text-xs font-medium', CAT_COLORS[cat] ?? 'bg-slate-700/50 text-slate-400 border-slate-700')}>
      <span className={clsx('w-1.5 h-1.5 rounded-full', CAT_DOT[cat] ?? 'bg-slate-400')} />
      {cat.replace('_', ' ')}
    </span>
  )
}

// ── Instrument impact row (inline editable) ───────────────────────────────────

function ImpactRow({
  imp, eventId, onUpdated, onDeleted,
}: {
  imp: InstrumentImpact
  eventId: number
  onUpdated: () => void
  onDeleted: () => void
}) {
  const [editing, setEditing] = useState(false)
  const [score, setScore]     = useState(String(imp.adjusted_score ?? imp.impact_score))
  const [dir, setDir]         = useState(imp.adjusted_direction || imp.direction)
  const [note, setNote]       = useState(imp.override_rationale || '')
  const [saving, setSaving]   = useState(false)

  const effectiveScore = imp.adjusted_score ?? imp.impact_score
  const effectiveDir   = imp.adjusted_direction || imp.direction

  const save = async () => {
    setSaving(true)
    await fetch(`/api/market-events/${eventId}/impacts/${imp.id}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ adjusted_score: +score, adjusted_direction: dir, override_rationale: note }),
    })
    setSaving(false)
    setEditing(false)
    onUpdated()
  }

  const del = async () => {
    if (!confirm(`Supprimer l'impact sur ${imp.instrument_id} ?`)) return
    await fetch(`/api/market-events/${eventId}/impacts/${imp.id}`, { method: 'DELETE' })
    onDeleted()
  }

  return (
    <div className="border-b border-slate-800/60 last:border-0">
      <div className="flex items-center gap-3 py-2 px-3 hover:bg-slate-800/20">
        {/* Ticker */}
        <span className="font-mono text-sm text-slate-200 w-24 shrink-0">{imp.instrument_id}</span>
        {/* Direction */}
        <span className={clsx('text-sm font-bold w-6 shrink-0', DIR_COLORS[effectiveDir])}>
          {DIR_ICONS[effectiveDir] ?? '●'}
        </span>
        {/* Score bar */}
        <div className="w-28 shrink-0">
          <ScoreBar score={effectiveScore} />
        </div>
        {/* Confidence */}
        <span className="text-xs text-slate-500 w-10 shrink-0">{Math.round(imp.confidence * 100)}%</span>
        {/* Rationale */}
        <span className="text-xs text-slate-400 flex-1 min-w-0 truncate">{imp.rationale}</span>
        {/* AI badge */}
        {imp.ai_generated ? (
          <span className="text-xs text-blue-400 shrink-0">AI</span>
        ) : (
          <span className="text-xs text-slate-600 shrink-0">manual</span>
        )}
        {imp.manually_adjusted ? <span className="text-xs text-amber-400 shrink-0">adj.</span> : null}
        {/* Actions */}
        <button onClick={() => setEditing(x => !x)} className="text-slate-500 hover:text-slate-300 shrink-0"><Edit3 size={12} /></button>
        <button onClick={del} className="text-slate-600 hover:text-red-400 shrink-0"><Trash2 size={12} /></button>
      </div>
      {editing && (
        <div className="px-3 pb-3 grid grid-cols-3 gap-2 bg-slate-800/30">
          <label className="flex flex-col gap-1">
            <span className="text-xs text-slate-500">Score ajusté</span>
            <input type="range" min="0" max="1" step="0.05"
              value={score} onChange={e => setScore(e.target.value)}
              className="w-full accent-amber-400" />
            <span className="text-xs text-center text-amber-300">{(+score).toFixed(2)}</span>
          </label>
          <label className="flex flex-col gap-1">
            <span className="text-xs text-slate-500">Direction</span>
            <select value={dir} onChange={e => setDir(e.target.value)}
              className="bg-dark-900 border border-slate-700 rounded px-2 py-1 text-slate-200 text-sm">
              {['bullish','bearish','neutral','depends_on_outcome'].map(d => (
                <option key={d} value={d}>{d}</option>
              ))}
            </select>
          </label>
          <label className="flex flex-col gap-1">
            <span className="text-xs text-slate-500">Note</span>
            <input value={note} onChange={e => setNote(e.target.value)}
              className="bg-dark-900 border border-slate-700 rounded px-2 py-1 text-slate-200 text-sm"
              placeholder="Raison de l'ajustement..." />
          </label>
          <div className="col-span-3 flex gap-2 mt-1">
            <button onClick={save} disabled={saving}
              className="flex items-center gap-1 bg-emerald-700 hover:bg-emerald-600 text-white text-xs rounded px-3 py-1">
              {saving ? <Loader2 size={11} className="animate-spin" /> : <CheckCircle size={11} />}
              Sauvegarder
            </button>
            <button onClick={() => setEditing(false)} className="text-xs text-slate-500 hover:text-slate-300 px-2">Annuler</button>
          </div>
        </div>
      )}
    </div>
  )
}

// ── Add instrument panel ──────────────────────────────────────────────────────

function AddInstrumentPanel({ eventId, onAdded }: { eventId: number; onAdded: () => void }) {
  const [open, setOpen]     = useState(false)
  const [ticker, setTicker] = useState('')
  const [score, setScore]   = useState(0.5)
  const [dir, setDir]       = useState('neutral')
  const [rationale, setRat] = useState('')
  const [saving, setSaving] = useState(false)

  const submit = async () => {
    if (!ticker) return
    setSaving(true)
    await fetch(`/api/market-events/${eventId}/impacts`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ instrument_id: ticker, impact_score: score, direction: dir, rationale, confidence: 0.8 }),
    })
    setSaving(false)
    setOpen(false)
    setTicker('')
    setRat('')
    onAdded()
  }

  if (!open) {
    return (
      <button onClick={() => setOpen(true)}
        className="flex items-center gap-1.5 text-xs text-slate-500 hover:text-emerald-400 py-2 px-3 border-t border-slate-800/60">
        <Plus size={12} /> Ajouter instrument
      </button>
    )
  }

  return (
    <div className="border-t border-slate-800/60 px-3 py-3 bg-slate-800/30 grid grid-cols-4 gap-2">
      <select value={ticker} onChange={e => setTicker(e.target.value)}
        className="bg-dark-900 border border-slate-700 rounded px-2 py-1 text-slate-200 text-sm col-span-1">
        <option value="">Ticker...</option>
        {ALL_INSTRUMENTS.map(t => <option key={t} value={t}>{t}</option>)}
      </select>
      <select value={dir} onChange={e => setDir(e.target.value)}
        className="bg-dark-900 border border-slate-700 rounded px-2 py-1 text-slate-200 text-sm">
        {['bullish','bearish','neutral','depends_on_outcome'].map(d => <option key={d} value={d}>{d}</option>)}
      </select>
      <div className="flex flex-col gap-0.5">
        <input type="range" min="0" max="1" step="0.05" value={score}
          onChange={e => setScore(+e.target.value)} className="accent-emerald-400" />
        <span className="text-xs text-center text-emerald-300">{score.toFixed(2)}</span>
      </div>
      <input value={rationale} onChange={e => setRat(e.target.value)} placeholder="Rationale..."
        className="bg-dark-900 border border-slate-700 rounded px-2 py-1 text-slate-200 text-sm" />
      <div className="col-span-4 flex gap-2">
        <button onClick={submit} disabled={saving || !ticker}
          className="flex items-center gap-1 bg-emerald-700 hover:bg-emerald-600 disabled:bg-slate-700 disabled:text-slate-500 text-white text-xs rounded px-3 py-1">
          {saving ? <Loader2 size={11} className="animate-spin" /> : <Plus size={11} />} Ajouter
        </button>
        <button onClick={() => setOpen(false)} className="text-xs text-slate-500 hover:text-slate-300 px-2">Annuler</button>
      </div>
    </div>
  )
}

// ── Event detail panel (right side) ──────────────────────────────────────────

function EventDetail({
  event, onClose, onUpdated, onDeleted,
}: {
  event: MarketEvent
  onClose: () => void
  onUpdated: (ev: MarketEvent) => void
  onDeleted: (id: number) => void
}) {
  const [detail, setDetail]       = useState<MarketEvent>(event)
  const [evaluating, setEval]     = useState(false)
  const [evalMsg, setEvalMsg]     = useState<string | null>(null)
  const [editing, setEditing]     = useState(false)
  const [editData, setEditData]   = useState<Partial<MarketEvent>>({})
  const [saving, setSaving]       = useState(false)
  const [allCats, setAllCats]     = useState<EventCategory[]>([])
  const [catSaving, setCatSaving] = useState(false)

  const loadDetail = useCallback(async () => {
    const r = await fetch(`/api/market-events/${event.id}`)
    if (r.ok) {
      const d = await r.json()
      setDetail(d)
    }
  }, [event.id])

  useEffect(() => { loadDetail() }, [loadDetail])
  useEffect(() => {
    fetch('/api/impact/categories')
      .then(r => r.json())
      .then(d => setAllCats(Array.isArray(d) ? d : []))
      .catch(() => {})
  }, [])

  // Quick-save sub_type without entering full edit mode
  const assignCategory = async (catName: string) => {
    const cat = allCats.find(c => c.name === catName)
    setCatSaving(true)
    const payload = {
      ...detail,
      sub_type: catName,
      category: cat?.type ?? detail.category,
      source_refs: detail.source_refs ?? [],
    }
    const r = await fetch(`/api/market-events/${event.id}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    })
    setCatSaving(false)
    if (r.ok) {
      setDetail(d => ({ ...d, sub_type: catName, category: cat?.type ?? d.category }))
      onUpdated({ ...detail, sub_type: catName, category: cat?.type ?? detail.category })
    }
  }

  const evaluate = async (force = false) => {
    setEval(true)
    setEvalMsg(null)
    try {
      const r = await fetch(`/api/market-events/${event.id}/evaluate?force=${force}`, { method: 'POST' })
      const d = await r.json()
      if (!r.ok) throw new Error(d.detail || r.statusText)
      const catInfo = d.matched_category ? ` · catégorie: ${d.matched_category}` : ''
      setEvalMsg(`✓ ${d.n_instruments} instruments évalués${catInfo}`)
      await loadDetail()
    } catch (e: any) {
      setEvalMsg(`✗ ${e.message}`)
    } finally {
      setEval(false)
    }
  }

  const saveEdit = async () => {
    setSaving(true)
    const payload = { ...detail, ...editData, source_refs: detail.source_refs ?? [] }
    const r = await fetch(`/api/market-events/${event.id}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    })
    setSaving(false)
    if (r.ok) {
      setEditing(false)
      await loadDetail()
      onUpdated({ ...detail, ...editData } as MarketEvent)
    }
  }

  const del = async () => {
    if (!confirm(`Supprimer "${detail.name}" ?`)) return
    await fetch(`/api/market-events/${event.id}`, { method: 'DELETE' })
    onDeleted(event.id)
    onClose()
  }

  const impacts = detail.impacts || []
  const evalScore = detail.impact_score

  return (
    <div className="flex flex-col h-full overflow-hidden">
      {/* Header */}
      <div className="flex items-start gap-3 px-5 py-4 border-b border-slate-700/40 shrink-0">
        <div className="flex-1 min-w-0">
          {editing ? (
            <input
              defaultValue={detail.name}
              onChange={e => setEditData(d => ({ ...d, name: e.target.value }))}
              className="bg-dark-900 border border-amber-500/50 rounded px-3 py-1.5 text-slate-100 font-bold text-sm w-full"
            />
          ) : (
            <h2 className="text-slate-100 font-bold text-sm leading-snug">{detail.name}</h2>
          )}
          <div className="flex items-center gap-2 mt-1.5 flex-wrap">
            <CatBadge cat={detail.category} />
            <span className={clsx('text-xs font-semibold', LEVEL_COLORS[detail.level])}>{detail.level}</span>
            <span className="text-xs text-slate-500">{detail.start_date}</span>
            {detail.end_date && <span className="text-xs text-slate-600">→ {detail.end_date}</span>}
          </div>
        </div>
        <div className="flex items-center gap-1.5 shrink-0">
          <button onClick={() => { setEditing(x => !x); setEditData({}) }}
            className={clsx('p-1.5 rounded hover:bg-slate-700/50', editing ? 'text-amber-400' : 'text-slate-500 hover:text-slate-300')}>
            <Edit3 size={14} />
          </button>
          <button onClick={del} className="p-1.5 rounded text-slate-600 hover:text-red-400 hover:bg-red-900/20">
            <Trash2 size={14} />
          </button>
          <button onClick={onClose} className="p-1.5 rounded text-slate-500 hover:text-slate-300 hover:bg-slate-700/50">
            <X size={14} />
          </button>
        </div>
      </div>

      {/* Scrollable body */}
      <div className="flex-1 overflow-y-auto space-y-4 px-5 py-4">

        {/* Impact score + absorption */}
        <div className="grid grid-cols-2 gap-3">
          <div className="bg-slate-800/40 rounded-lg p-3">
            <div className="text-xs text-slate-500 mb-1">Score impact</div>
            <ScoreBar score={evalScore} />
          </div>
          {detail.absorption_pct != null && (
            <div className="bg-slate-800/40 rounded-lg p-3">
              <div className="text-xs text-slate-500 mb-1">Absorption marché</div>
              <div className="flex items-center gap-2">
                <div className="flex-1 h-1.5 bg-slate-700 rounded-full overflow-hidden">
                  <div className="h-full bg-blue-500 rounded-full" style={{ width: `${detail.absorption_pct}%` }} />
                </div>
                <span className="text-xs text-blue-300">{detail.absorption_pct}%</span>
              </div>
            </div>
          )}
        </div>

        {/* ── Catégorie IA ─────────────────────────────────────────────── */}
        {(() => {
          const currentSub  = editing ? (editData.sub_type ?? detail.sub_type ?? '') : (detail.sub_type ?? '')
          const matchedCat  = allCats.find(c => c.name === currentSub)
          const nDefaults   = matchedCat?.default_impacts?.length ?? 0

          return (
            <div className="bg-slate-800/40 rounded-lg p-3 border border-slate-700/30">
              <div className="flex items-center justify-between mb-2">
                <span className="text-xs text-slate-500 uppercase tracking-wide">Catégorie IA</span>
                {matchedCat && (
                  <span className="text-xs text-slate-600">
                    {nDefaults} impact{nDefaults !== 1 ? 's' : ''} par défaut
                  </span>
                )}
              </div>
              {editing ? (
                <select
                  value={editData.sub_type ?? detail.sub_type ?? ''}
                  onChange={e => {
                    const cat = allCats.find(c => c.name === e.target.value)
                    setEditData(d => ({
                      ...d,
                      sub_type: e.target.value,
                      ...(cat ? { category: cat.type } : {}),
                    }))
                  }}
                  className="w-full bg-dark-900 border border-slate-700 rounded px-2 py-1.5 text-slate-200 text-sm"
                >
                  <option value="">— aucune catégorie —</option>
                  {allCats.map(c => (
                    <option key={c.name} value={c.name}>{c.name} ({c.type})</option>
                  ))}
                </select>
              ) : (
                <div className="flex items-center gap-2">
                  <select
                    value={currentSub}
                    onChange={e => assignCategory(e.target.value)}
                    disabled={catSaving}
                    className="flex-1 bg-dark-900 border border-slate-700 rounded px-2 py-1.5 text-slate-200 text-sm"
                  >
                    <option value="">— aucune catégorie —</option>
                    {allCats.map(c => (
                      <option key={c.name} value={c.name}>{c.name} ({c.type})</option>
                    ))}
                  </select>
                  {catSaving && <Loader2 size={13} className="animate-spin text-slate-500 shrink-0" />}
                </div>
              )}
              {matchedCat?.description && (
                <div className="text-xs text-slate-600 mt-1.5 italic">{matchedCat.description}</div>
              )}
            </div>
          )
        })()}

        {/* Description */}
        {editing ? (
          <textarea
            defaultValue={detail.description}
            onChange={e => setEditData(d => ({ ...d, description: e.target.value }))}
            rows={3}
            className="w-full bg-dark-900 border border-slate-700 rounded px-3 py-2 text-slate-300 text-sm resize-none"
          />
        ) : detail.description ? (
          <p className="text-sm text-slate-300 leading-relaxed">{detail.description}</p>
        ) : null}

        {editing ? (
          <div className="grid grid-cols-2 gap-2">
            <div>
              <div className="text-xs text-slate-500 mb-1">Type</div>
              <select defaultValue={detail.category}
                onChange={e => setEditData(d => ({ ...d, category: e.target.value }))}
                className="w-full bg-dark-900 border border-slate-700 rounded px-2 py-1 text-slate-200 text-sm">
                {CATEGORIES.map(c => <option key={c} value={c}>{c}</option>)}
              </select>
            </div>
            <div>
              <div className="text-xs text-slate-500 mb-1">Niveau</div>
              <select defaultValue={detail.level}
                onChange={e => setEditData(d => ({ ...d, level: e.target.value as any }))}
                className="w-full bg-dark-900 border border-slate-700 rounded px-2 py-1 text-slate-200 text-sm">
                {LEVELS.map(l => <option key={l} value={l}>{l}</option>)}
              </select>
            </div>
            <div>
              <div className="text-xs text-slate-500 mb-1">Score impact</div>
              <input type="number" step="0.05" min="0" max="1" defaultValue={detail.impact_score}
                onChange={e => setEditData(d => ({ ...d, impact_score: +e.target.value }))}
                className="w-full bg-dark-900 border border-slate-700 rounded px-2 py-1 text-slate-200 text-sm" />
            </div>
          </div>
        ) : null}

        {editing && (
          <div className="flex gap-2">
            <button onClick={saveEdit} disabled={saving}
              className="flex items-center gap-1.5 bg-emerald-700 hover:bg-emerald-600 text-white text-sm rounded-lg px-4 py-1.5">
              {saving ? <Loader2 size={13} className="animate-spin" /> : <CheckCircle size={13} />} Sauvegarder
            </button>
            <button onClick={() => setEditing(false)} className="text-sm text-slate-500 hover:text-slate-300 px-3">Annuler</button>
          </div>
        )}

        {/* Origin + Sources */}
        <div>
          <div className="text-xs text-slate-500 uppercase tracking-wide mb-2">Provenance</div>
          {/* Origin badge */}
          {(() => {
            const meta = ORIGIN_META[detail.origin ?? 'unknown'] ?? ORIGIN_META['unknown']
            return (
              <div className="flex items-start gap-2 bg-slate-800/50 rounded-lg px-3 py-2 mb-2 border border-slate-700/30">
                <span className="text-base shrink-0">{meta.icon}</span>
                <div className="flex-1 min-w-0">
                  <div className={`text-xs font-semibold ${meta.color}`}>{meta.label}</div>
                  <div className="text-xs text-slate-500 mt-0.5">{meta.description}</div>
                </div>
              </div>
            )
          })()}
          {/* Source refs */}
          {(detail.source_refs?.length ?? 0) > 0 ? (
            <div className="space-y-1.5">
              {detail.source_refs!.map((ref, i) => (
                <div key={i} className="flex items-start gap-2 bg-slate-800/30 rounded-lg px-3 py-2 border border-slate-800/60">
                  <div className="flex-1 min-w-0">
                    <div className="text-xs text-slate-200 font-medium">{ref.title}</div>
                    <div className="flex items-center gap-2 mt-0.5 flex-wrap">
                      <span className="text-xs text-slate-500">{ref.source}</span>
                      <span className="text-xs text-slate-600">{ref.date}</span>
                      {ref.original_score > 0 && (
                        <span className="text-xs text-slate-600">
                          score brut: <span className="text-slate-400">{ref.original_score.toFixed(2)}</span>
                        </span>
                      )}
                    </div>
                  </div>
                  {ref.url && (
                    <a href={ref.url} target="_blank" rel="noreferrer"
                      className="text-slate-500 hover:text-blue-400 shrink-0 mt-0.5">
                      <ExternalLink size={12} />
                    </a>
                  )}
                </div>
              ))}
            </div>
          ) : (
            <div className="text-xs text-slate-600 italic px-3 py-2 bg-slate-800/20 rounded-lg border border-slate-800/40">
              Aucune source liée — cet événement ne référence pas de document ou d'article spécifique.
            </div>
          )}
        </div>

        {/* Instrument impacts */}
        <div>
          <div className="flex items-center justify-between mb-2">
            <div className="text-xs text-slate-500 uppercase tracking-wide">
              Impacts instruments
              <span className="text-slate-600 ml-1.5 normal-case">({impacts.length})</span>
            </div>
            <div className="flex items-center gap-2">
              {evalMsg && (
                <span className={clsx('text-xs', evalMsg.startsWith('✓') ? 'text-emerald-400' : 'text-red-400')}>
                  {evalMsg}
                </span>
              )}
              <button
                onClick={() => evaluate(impacts.length > 0)}
                disabled={evaluating}
                className="flex items-center gap-1.5 text-xs bg-blue-700/40 hover:bg-blue-700/60 border border-blue-700/50 text-blue-300 rounded px-3 py-1"
              >
                {evaluating ? <Loader2 size={11} className="animate-spin" /> : <Sparkles size={11} />}
                {impacts.length > 0 ? 'Ré-analyser' : 'Analyser IA'}
              </button>
            </div>
          </div>

          {impacts.length === 0 ? (
            <div className="text-xs text-slate-600 italic py-3 px-3 bg-slate-800/30 rounded-lg">
              Aucun impact évalué — cliquez sur "Analyser IA" pour lancer l'évaluation.
            </div>
          ) : (
            <div className="bg-slate-800/30 rounded-lg overflow-hidden">
              {/* Header */}
              <div className="grid text-xs text-slate-600 px-3 py-1.5 border-b border-slate-800 bg-slate-800/50"
                style={{ gridTemplateColumns: '6rem 1.5rem 8rem 2.5rem 1fr 3rem 3rem 1.5rem 1.5rem' }}>
                <span>Instrument</span>
                <span></span>
                <span>Score</span>
                <span>Conf.</span>
                <span>Rationale</span>
                <span></span>
                <span></span>
                <span></span>
                <span></span>
              </div>
              {impacts
                .slice()
                .sort((a, b) => (b.adjusted_score ?? b.impact_score) - (a.adjusted_score ?? a.impact_score))
                .map(imp => (
                  <ImpactRow
                    key={imp.id}
                    imp={imp}
                    eventId={detail.id}
                    onUpdated={loadDetail}
                    onDeleted={loadDetail}
                  />
                ))}
              <AddInstrumentPanel eventId={detail.id} onAdded={loadDetail} />
            </div>
          )}
          {impacts.length === 0 && (
            <div className="mt-2">
              <AddInstrumentPanel eventId={detail.id} onAdded={loadDetail} />
            </div>
          )}
        </div>
      </div>
    </div>
  )
}

// ── Create event modal ────────────────────────────────────────────────────────

function CreateModal({ onCreated, onClose }: { onCreated: () => void; onClose: () => void }) {
  const [form, setForm] = useState({
    name: '', start_date: new Date().toISOString().slice(0, 10),
    category: 'fundamental', level: 'short', sub_type: '',
    description: '', impact_score: 0.5,
  })
  const [saving, setSaving] = useState(false)

  const submit = async () => {
    if (!form.name || !form.start_date) return
    setSaving(true)
    await fetch('/api/market-events', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ ...form, source_refs: [] }),
    })
    setSaving(false)
    onCreated()
    onClose()
  }

  const f = (k: string) => (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) =>
    setForm(d => ({ ...d, [k]: e.target.value }))

  return (
    <div className="fixed inset-0 bg-black/60 z-50 flex items-center justify-center">
      <div className="bg-dark-800 border border-slate-700/50 rounded-xl w-full max-w-lg shadow-2xl">
        <div className="flex items-center justify-between px-5 py-4 border-b border-slate-700/40">
          <h3 className="text-slate-100 font-semibold">Nouvel événement marché</h3>
          <button onClick={onClose} className="text-slate-500 hover:text-slate-300"><X size={16} /></button>
        </div>
        <div className="p-5 space-y-3">
          <input value={form.name} onChange={f('name')} placeholder="Nom de l'événement *"
            className="w-full bg-dark-900 border border-slate-700 rounded px-3 py-2 text-slate-200 text-sm" />
          <div className="grid grid-cols-2 gap-3">
            <div>
              <div className="text-xs text-slate-500 mb-1">Date début</div>
              <input type="date" value={form.start_date} onChange={f('start_date')}
                className="w-full bg-dark-900 border border-slate-700 rounded px-3 py-2 text-slate-200 text-sm" />
            </div>
            <div>
              <div className="text-xs text-slate-500 mb-1">Catégorie</div>
              <select value={form.category} onChange={f('category')}
                className="w-full bg-dark-900 border border-slate-700 rounded px-3 py-2 text-slate-200 text-sm">
                {CATEGORIES.map(c => <option key={c} value={c}>{c}</option>)}
              </select>
            </div>
            <div>
              <div className="text-xs text-slate-500 mb-1">Niveau</div>
              <select value={form.level} onChange={f('level')}
                className="w-full bg-dark-900 border border-slate-700 rounded px-3 py-2 text-slate-200 text-sm">
                {LEVELS.map(l => <option key={l} value={l}>{l}</option>)}
              </select>
            </div>
            <div>
              <div className="text-xs text-slate-500 mb-1">Sous-type</div>
              <input value={form.sub_type} onChange={f('sub_type')} placeholder="ex: CPI, Conflit..."
                className="w-full bg-dark-900 border border-slate-700 rounded px-3 py-2 text-slate-200 text-sm" />
            </div>
          </div>
          <textarea value={form.description} onChange={f('description')} placeholder="Description..." rows={3}
            className="w-full bg-dark-900 border border-slate-700 rounded px-3 py-2 text-slate-200 text-sm resize-none" />
          <div>
            <div className="text-xs text-slate-500 mb-1">Score impact: {form.impact_score.toFixed(2)}</div>
            <input type="range" min="0" max="1" step="0.05" value={form.impact_score}
              onChange={e => setForm(d => ({ ...d, impact_score: +e.target.value }))}
              className="w-full accent-amber-400" />
          </div>
        </div>
        <div className="flex gap-2 px-5 pb-5">
          <button onClick={submit} disabled={saving || !form.name}
            className="flex items-center gap-2 bg-emerald-700 hover:bg-emerald-600 disabled:bg-slate-700 disabled:text-slate-500 text-white rounded-lg px-5 py-2 text-sm font-medium">
            {saving ? <Loader2 size={14} className="animate-spin" /> : <Plus size={14} />} Créer
          </button>
          <button onClick={onClose} className="text-sm text-slate-500 hover:text-slate-300 px-4">Annuler</button>
        </div>
      </div>
    </div>
  )
}

// ── Event list row ────────────────────────────────────────────────────────────

function EventRow({
  ev, selected, onClick,
}: { ev: MarketEvent; selected: boolean; onClick: () => void }) {
  return (
    <div
      onClick={onClick}
      className={clsx(
        'flex items-center gap-3 px-4 py-3 cursor-pointer border-b border-slate-800/50 hover:bg-slate-800/40 transition-colors',
        selected && 'bg-slate-800/60 border-l-2 border-l-blue-500',
      )}
    >
      <span className={clsx('w-1.5 h-1.5 rounded-full shrink-0', CAT_DOT[ev.category] ?? 'bg-slate-500')} />
      <div className="flex-1 min-w-0">
        <div className="text-sm text-slate-200 truncate font-medium">{ev.name}</div>
        <div className="flex items-center gap-2 mt-0.5">
          <span className="text-xs text-slate-500">{ev.start_date}</span>
          {ev.sub_type && <span className="text-xs text-slate-600">{ev.sub_type}</span>}
          {ev.origin && (
            <span className="text-xs shrink-0" title={(ORIGIN_META[ev.origin] ?? ORIGIN_META['unknown']).description}>
              {(ORIGIN_META[ev.origin] ?? ORIGIN_META['unknown']).icon}
            </span>
          )}
          {ev.created_at && (
            <span className="text-[10px] text-slate-700 shrink-0" title={`Généré le ${ev.created_at}`}>
              ⚡{ev.created_at.slice(5, 10)}
            </span>
          )}
        </div>
      </div>
      <div className="shrink-0 text-right">
        <div className="text-xs font-bold" style={{ color: ev.impact_score >= 0.7 ? '#ef4444' : ev.impact_score >= 0.5 ? '#f59e0b' : '#10b981' }}>
          {ev.impact_score.toFixed(2)}
        </div>
        <div className={clsx('text-xs', LEVEL_COLORS[ev.level])}>{ev.level}</div>
      </div>
    </div>
  )
}

// ── Types catégorie ──────────────────────────────────────────────────────────

interface DefaultImpact {
  instrument_id: string
  sensitivity: number     // 0-1
  typical_direction: string
  notes: string
}

interface EventCategory {
  id?: number
  name: string
  type: string
  sub_type: string
  description: string
  default_impacts: DefaultImpact[]
  is_ai_generated?: number
  updated_at?: string
}

// ── Category editor panel ─────────────────────────────────────────────────────

function CategoryEditor({
  cat, onSaved, onDeleted, onClose,
}: {
  cat: EventCategory | null
  onSaved: () => void
  onDeleted?: () => void
  onClose: () => void
}) {
  const isNew = !cat?.id
  const [form, setForm] = useState<EventCategory>(cat ?? {
    name: '', type: 'event_calendar', sub_type: '', description: '', default_impacts: [],
  })
  const [saving, setSaving]   = useState(false)
  const [deleting, setDel]    = useState(false)

  const save = async () => {
    if (!form.name) return
    setSaving(true)
    await fetch(`/api/impact/categories/${encodeURIComponent(form.name)}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        type: form.type, sub_type: form.sub_type,
        description: form.description,
        default_impacts: form.default_impacts,
      }),
    })
    setSaving(false)
    onSaved()
    onClose()
  }

  const del = async () => {
    if (!cat?.name || !confirm(`Supprimer la catégorie "${cat.name}" ?`)) return
    setDel(true)
    await fetch(`/api/impact/categories/${encodeURIComponent(cat.name)}`, { method: 'DELETE' })
    setDel(false)
    onDeleted?.()
    onClose()
  }

  const setImpact = (idx: number, field: keyof DefaultImpact, value: any) => {
    setForm(f => {
      const impacts = [...f.default_impacts]
      impacts[idx] = { ...impacts[idx], [field]: value }
      return { ...f, default_impacts: impacts }
    })
  }

  const addImpact = () => {
    setForm(f => ({
      ...f,
      default_impacts: [...f.default_impacts, { instrument_id: 'SPY', sensitivity: 0.5, typical_direction: 'depends_on_outcome', notes: '' }],
    }))
  }

  const removeImpact = (idx: number) => {
    setForm(f => ({ ...f, default_impacts: f.default_impacts.filter((_, i) => i !== idx) }))
  }

  return (
    <div className="flex flex-col h-full overflow-hidden">
      <div className="flex items-center gap-3 px-5 py-4 border-b border-slate-700/40 shrink-0">
        <h3 className="text-slate-100 font-semibold text-sm flex-1">
          {isNew ? 'Nouvelle catégorie' : `Éditer : ${cat!.name}`}
        </h3>
        {!isNew && (
          <button onClick={del} disabled={deleting}
            className="flex items-center gap-1 text-xs text-red-400 hover:text-red-300 border border-red-800/50 rounded px-2 py-1">
            {deleting ? <Loader2 size={11} className="animate-spin" /> : <Trash2 size={11} />} Supprimer
          </button>
        )}
        <button onClick={onClose} className="text-slate-500 hover:text-slate-300"><X size={14} /></button>
      </div>

      <div className="flex-1 overflow-y-auto px-5 py-4 space-y-4">
        {/* Name + type */}
        <div className="grid grid-cols-2 gap-3">
          <div>
            <div className="text-xs text-slate-500 mb-1">Nom *</div>
            <input value={form.name} onChange={e => setForm(f => ({ ...f, name: e.target.value }))}
              disabled={!isNew}
              placeholder="ex: BOE — Décision de taux"
              className="w-full bg-dark-900 border border-slate-700 rounded px-3 py-1.5 text-slate-200 text-sm disabled:opacity-50" />
          </div>
          <div>
            <div className="text-xs text-slate-500 mb-1">Type</div>
            <select value={form.type} onChange={e => setForm(f => ({ ...f, type: e.target.value }))}
              className="w-full bg-dark-900 border border-slate-700 rounded px-3 py-1.5 text-slate-200 text-sm">
              {CATEGORIES.map(c => <option key={c} value={c}>{c}</option>)}
            </select>
          </div>
          <div>
            <div className="text-xs text-slate-500 mb-1">Sous-type</div>
            <input value={form.sub_type} onChange={e => setForm(f => ({ ...f, sub_type: e.target.value }))}
              placeholder="ex: BOE"
              className="w-full bg-dark-900 border border-slate-700 rounded px-3 py-1.5 text-slate-200 text-sm" />
          </div>
          <div>
            <div className="text-xs text-slate-500 mb-1">Description</div>
            <input value={form.description} onChange={e => setForm(f => ({ ...f, description: e.target.value }))}
              placeholder="Résumé court..."
              className="w-full bg-dark-900 border border-slate-700 rounded px-3 py-1.5 text-slate-200 text-sm" />
          </div>
        </div>

        {/* Default impacts table */}
        <div>
          <div className="flex items-center justify-between mb-2">
            <div className="text-xs text-slate-500 uppercase tracking-wide">
              Impacts par défaut
              <span className="text-slate-600 ml-1.5 normal-case">— l'IA s'appuie sur ces sensibilités comme base</span>
            </div>
            <button onClick={addImpact}
              className="flex items-center gap-1 text-xs text-emerald-400 hover:text-emerald-300 border border-emerald-800/50 rounded px-2 py-1">
              <Plus size={11} /> Ajouter
            </button>
          </div>

          {form.default_impacts.length === 0 ? (
            <div className="text-xs text-slate-600 italic px-3 py-3 bg-slate-800/20 rounded-lg border border-slate-800/40">
              Aucun impact par défaut — cliquez sur "Ajouter" pour en définir.
            </div>
          ) : (
            <div className="bg-slate-800/30 rounded-lg overflow-hidden">
              {/* Header */}
              <div className="grid text-xs text-slate-600 px-3 py-1.5 border-b border-slate-800 bg-slate-800/50"
                style={{ gridTemplateColumns: '7rem 8rem 2rem 10rem 1fr 1.5rem' }}>
                <span>Instrument</span>
                <span>Sensibilité</span>
                <span></span>
                <span>Direction type</span>
                <span>Notes</span>
                <span></span>
              </div>
              {form.default_impacts.map((imp, idx) => (
                <div key={idx} className="grid items-center gap-2 px-3 py-2 border-b border-slate-800/60 last:border-0"
                  style={{ gridTemplateColumns: '7rem 8rem 2rem 10rem 1fr 1.5rem' }}>
                  <select value={imp.instrument_id}
                    onChange={e => setImpact(idx, 'instrument_id', e.target.value)}
                    className="bg-dark-900 border border-slate-700 rounded px-1.5 py-1 text-slate-200 text-xs w-full">
                    {ALL_INSTRUMENTS.map(t => <option key={t} value={t}>{t}</option>)}
                  </select>
                  <div className="flex flex-col gap-0.5">
                    <input type="range" min="0" max="1" step="0.05" value={imp.sensitivity}
                      onChange={e => setImpact(idx, 'sensitivity', +e.target.value)}
                      className="accent-amber-400 w-full" />
                    <span className="text-xs text-center text-amber-300">{imp.sensitivity.toFixed(2)}</span>
                  </div>
                  <span className={clsx('text-sm font-bold text-center', DIR_COLORS[imp.typical_direction] ?? 'text-slate-400')}>
                    {DIR_ICONS[imp.typical_direction] ?? '●'}
                  </span>
                  <select value={imp.typical_direction}
                    onChange={e => setImpact(idx, 'typical_direction', e.target.value)}
                    className="bg-dark-900 border border-slate-700 rounded px-1.5 py-1 text-slate-200 text-xs w-full">
                    {['bullish','bearish','neutral','depends_on_outcome'].map(d => <option key={d} value={d}>{d}</option>)}
                  </select>
                  <input value={imp.notes} onChange={e => setImpact(idx, 'notes', e.target.value)}
                    placeholder="Note..."
                    className="bg-dark-900 border border-slate-700 rounded px-2 py-1 text-slate-300 text-xs w-full" />
                  <button onClick={() => removeImpact(idx)}
                    className="text-slate-600 hover:text-red-400"><Trash2 size={12} /></button>
                </div>
              ))}
            </div>
          )}
        </div>
      </div>

      <div className="px-5 pb-5 pt-3 border-t border-slate-700/30 flex gap-2 shrink-0">
        <button onClick={save} disabled={saving || !form.name}
          className="flex items-center gap-2 bg-emerald-700 hover:bg-emerald-600 disabled:bg-slate-700 disabled:text-slate-500 text-white rounded-lg px-5 py-2 text-sm font-medium">
          {saving ? <Loader2 size={14} className="animate-spin" /> : <CheckCircle size={14} />}
          {isNew ? 'Créer' : 'Sauvegarder'}
        </button>
        <button onClick={onClose} className="text-sm text-slate-500 hover:text-slate-300 px-4">Annuler</button>
      </div>
    </div>
  )
}

// ── Categories panel ──────────────────────────────────────────────────────────

function CategoriesPanel() {
  const [cats, setCats]           = useState<EventCategory[]>([])
  const [loading, setLoading]     = useState(false)
  const [selected, setSelected]   = useState<EventCategory | null>(null)
  const [showNew, setShowNew]     = useState(false)
  const [search, setSearch]       = useState('')

  const load = useCallback(async () => {
    setLoading(true)
    const r = await fetch('/api/impact/categories')
    if (r.ok) {
      const data = await r.json()
      setCats(data.map((c: any) => ({
        ...c,
        default_impacts: typeof c.default_impacts === 'string'
          ? JSON.parse(c.default_impacts || '[]')
          : (c.default_impacts || []),
      })))
    }
    setLoading(false)
  }, [])

  useEffect(() => { load() }, [load])

  const filtered = cats.filter(c =>
    !search || c.name.toLowerCase().includes(search.toLowerCase()) ||
    c.type.includes(search.toLowerCase()) || c.sub_type.toLowerCase().includes(search.toLowerCase())
  )

  // Group by type
  const grouped = CATEGORIES.reduce((acc, t) => {
    acc[t] = filtered.filter(c => c.type === t)
    return acc
  }, {} as Record<string, EventCategory[]>)

  return (
    <div className="flex h-full overflow-hidden">
      {/* Left: list */}
      <div className="w-72 shrink-0 flex flex-col border-r border-slate-700/40 overflow-hidden">
        <div className="px-4 py-3 border-b border-slate-700/30 space-y-2">
          <div className="flex items-center gap-2">
            <span className="text-sm font-semibold text-slate-200 flex-1">Catégories ({cats.length})</span>
            <button onClick={() => { setSelected(null); setShowNew(true) }}
              className="flex items-center gap-1 bg-emerald-700 hover:bg-emerald-600 text-white text-xs rounded px-2 py-1">
              <Plus size={11} /> Nouvelle
            </button>
            <button onClick={load} disabled={loading}
              className="p-1 text-slate-500 hover:text-slate-300">
              <RefreshCw size={13} className={loading ? 'animate-spin' : ''} />
            </button>
          </div>
          <div className="relative">
            <Search size={12} className="absolute left-2.5 top-1/2 -translate-y-1/2 text-slate-500" />
            <input value={search} onChange={e => setSearch(e.target.value)}
              placeholder="Filtrer..."
              className="w-full bg-slate-800/60 border border-slate-700/50 rounded pl-7 pr-3 py-1.5 text-xs text-slate-200 placeholder:text-slate-600" />
          </div>
        </div>

        <div className="flex-1 overflow-y-auto">
          {CATEGORIES.map(type => {
            const group = grouped[type] ?? []
            if (group.length === 0) return null
            return (
              <div key={type}>
                <div className={clsx('px-3 py-1.5 text-xs font-semibold uppercase tracking-wide sticky top-0 bg-dark-900/90',
                  CAT_COLORS[type]?.split(' ')[1] ?? 'text-slate-500')}>
                  {type.replace('_', ' ')} ({group.length})
                </div>
                {group.map(c => (
                  <div key={c.name}
                    onClick={() => { setSelected(c); setShowNew(false) }}
                    className={clsx(
                      'flex items-center gap-2 px-4 py-2.5 cursor-pointer border-b border-slate-800/40 hover:bg-slate-800/40',
                      selected?.name === c.name && 'bg-slate-800/60 border-l-2 border-l-blue-500',
                    )}>
                    <span className={clsx('w-1.5 h-1.5 rounded-full shrink-0', CAT_DOT[c.type] ?? 'bg-slate-500')} />
                    <div className="flex-1 min-w-0">
                      <div className="text-xs text-slate-200 font-medium truncate">{c.name}</div>
                      <div className="text-xs text-slate-600">{c.default_impacts.length} instruments</div>
                    </div>
                  </div>
                ))}
              </div>
            )
          })}
        </div>
      </div>

      {/* Right: editor */}
      <div className="flex-1 overflow-hidden">
        {(showNew || selected) ? (
          <CategoryEditor
            key={showNew ? 'new' : selected!.name}
            cat={showNew ? null : selected}
            onSaved={() => { load(); setShowNew(false) }}
            onDeleted={() => { load(); setSelected(null) }}
            onClose={() => { setShowNew(false); setSelected(null) }}
          />
        ) : (
          <div className="flex flex-col items-center justify-center h-full text-slate-600 gap-3">
            <SlidersHorizontal size={36} className="opacity-30" />
            <div className="text-sm">Sélectionne une catégorie pour l'éditer</div>
            <div className="text-xs opacity-60">Les impacts par défaut sont utilisés par l'IA comme base de référence</div>
          </div>
        )}
      </div>
    </div>
  )
}

// ── Main page ─────────────────────────────────────────────────────────────────

export default function MarketEvents() {
  const [activeTab, setActiveTab]   = useState<'events' | 'categories'>('events')
  const [events, setEvents]         = useState<MarketEvent[]>([])
  const [total, setTotal]           = useState(0)
  const [loading, setLoading]       = useState(false)
  const [selected, setSelected]     = useState<MarketEvent | null>(null)
  const [showCreate, setShowCreate] = useState(false)
  const [bulkIds, setBulkIds]       = useState<Set<number>>(new Set())
  const [bulking, setBulking]       = useState(false)
  const [bulkMsg, setBulkMsg]       = useState<string | null>(null)

  // ── Filter state ─────────────────────────────────────────────────────────
  const [search,     setSearch]     = useState('')
  const [category,   setCategory]   = useState('')
  const [level,      setLevel]      = useState('')
  const [minScore,   setMinScore]   = useState(0)
  const [evaluated,  setEvaluated]  = useState<'' | 'true' | 'false'>('')
  const [dateFrom,   setDateFrom]   = useState('')
  const [dateTo,     setDateTo]     = useState('')
  const [datePreset, setDatePreset] = useState<string>('all')   // 7d|30d|90d|6m|1y|all|custom
  // Generation date filter (created_at)
  const [genFrom,    setGenFrom]    = useState('')
  const [genTo,      setGenTo]      = useState('')
  // Instrument impact filter
  const [instTicker, setInstTicker] = useState('')
  const [instMinScore, setInstMin]  = useState(0.3)
  const [instDir,    setInstDir]    = useState('')
  const [showInstFilter, setShowInst] = useState(false)
  // Sort
  const [sortBy,  setSortBy]        = useState<'date'|'score'|'name'|'instrument_score'>('date')
  const [sortDir, setSortDir]       = useState<'desc'|'asc'>('desc')
  // UI
  const [showFilters, setShowFilters] = useState(false)

  // ── Date preset helper ────────────────────────────────────────────────────
  const applyPreset = useCallback((preset: string) => {
    setDatePreset(preset)
    if (preset === 'custom' || preset === 'all') {
      setDateFrom(''); setDateTo('')
      return
    }
    const now = new Date()
    const to  = now.toISOString().slice(0, 10)
    const from = new Date(now)
    if (preset === '7d')  from.setDate(now.getDate() - 7)
    if (preset === '30d') from.setDate(now.getDate() - 30)
    if (preset === '90d') from.setDate(now.getDate() - 90)
    if (preset === '6m')  from.setMonth(now.getMonth() - 6)
    if (preset === '1y')  from.setFullYear(now.getFullYear() - 1)
    setDateFrom(from.toISOString().slice(0, 10))
    setDateTo(to)
  }, [])

  const debounceRef  = useRef<ReturnType<typeof setTimeout> | null>(null)
  const abortRef     = useRef<AbortController | null>(null)

  const load = useCallback(async () => {
    // Cancel any in-flight fetch
    abortRef.current?.abort()
    abortRef.current = new AbortController()
    const signal = abortRef.current.signal

    setLoading(true)
    const params = new URLSearchParams()
    if (search)    params.set('search', search)
    if (category)  params.set('category', category)
    if (level)     params.set('level', level)
    if (minScore)  params.set('min_score', String(minScore))
    if (dateFrom)  params.set('date_from', dateFrom)
    if (dateTo)    params.set('date_to', dateTo)
    if (genFrom)   params.set('gen_date_from', genFrom)
    if (genTo)     params.set('gen_date_to', genTo)
    if (evaluated) params.set('evaluated', evaluated)
    if (instTicker) {
      params.set('instrument', instTicker)
      if (instMinScore > 0) params.set('min_instrument_score', String(instMinScore))
      if (instDir)          params.set('instrument_direction', instDir)
    }
    params.set('sort_by',  instTicker ? 'instrument_score' : sortBy)
    params.set('sort_dir', sortDir)
    params.set('limit', '500')
    try {
      const r = await fetch(`/api/market-events?${params}`, { signal })
      if (r.ok) {
        const d = await r.json()
        setEvents(d.events)
        setTotal(d.total)
      }
    } catch (e: any) {
      if (e.name === 'AbortError') return  // superseded by a newer load
    }
    setLoading(false)
  }, [search, category, level, minScore, dateFrom, dateTo, genFrom, genTo, evaluated,
      instTicker, instMinScore, instDir, sortBy, sortDir])

  useEffect(() => {
    if (debounceRef.current) clearTimeout(debounceRef.current)
    debounceRef.current = setTimeout(load, 250)
  }, [load])

  const bulkEvaluate = async () => {
    if (bulkIds.size === 0) return
    setBulking(true)
    setBulkMsg(null)
    const r = await fetch('/api/market-events/bulk-evaluate', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify([...bulkIds]),
    })
    const d = await r.json()
    setBulkMsg(`✓ ${d.evaluated} évalués${d.errors > 0 ? `, ${d.errors} erreurs` : ''}`)
    setBulkIds(new Set())
    setBulking(false)
    load()
  }

  const unevaluatedIds = events.filter(e => !e.evaluated).map(e => e.id)

  return (
    <div className="flex flex-col h-screen overflow-hidden">
      {/* ── Tab bar ── */}
      <div className="flex items-center gap-0 border-b border-slate-700/40 bg-dark-800/80 px-4 shrink-0">
        <h1 className="text-sm font-bold text-slate-300 mr-6 py-3">Market Events</h1>
        {(['events', 'categories'] as const).map(tab => (
          <button key={tab} onClick={() => setActiveTab(tab)}
            className={clsx(
              'px-4 py-3 text-sm border-b-2 transition-colors',
              activeTab === tab
                ? 'border-blue-500 text-blue-300 font-medium'
                : 'border-transparent text-slate-500 hover:text-slate-300',
            )}>
            {tab === 'events' ? '📋 Événements' : '🏷️ Catégories & Defaults'}
          </button>
        ))}
      </div>

      {/* ── Categories tab ── */}
      {activeTab === 'categories' && (
        <div className="flex-1 overflow-hidden">
          <CategoriesPanel />
        </div>
      )}

      {/* ── Events tab ── */}
      {activeTab === 'events' && <div className="flex flex-1 overflow-hidden">
      {/* ── Left panel: filters + list ── */}
      <div className="w-96 shrink-0 flex flex-col border-r border-slate-700/40 overflow-hidden">
        {/* ── Search + actions ── */}
        <div className="px-3 pt-3 pb-2 border-b border-slate-700/30 space-y-2">
          <div className="flex items-center gap-2">
            <div className="relative flex-1">
              <Search size={13} className="absolute left-2.5 top-1/2 -translate-y-1/2 text-slate-500" />
              <input value={search} onChange={e => setSearch(e.target.value)}
                placeholder="Recherche nom, description..."
                className="w-full bg-slate-800/60 border border-slate-700/50 rounded-lg pl-8 pr-3 py-1.5 text-sm text-slate-200 placeholder:text-slate-600" />
            </div>
            <button onClick={() => setShowCreate(true)}
              className="flex items-center gap-1 bg-emerald-700 hover:bg-emerald-600 text-white text-xs rounded-lg px-2 py-1.5 shrink-0">
              <Plus size={12} />
            </button>
            <button onClick={load} disabled={loading}
              className="p-1.5 text-slate-500 hover:text-slate-300 rounded shrink-0">
              <RefreshCw size={13} className={loading ? 'animate-spin' : ''} />
            </button>
          </div>

          {/* ── Date presets ── */}
          <div className="flex items-center gap-1 flex-wrap">
            <span className="text-xs text-slate-600 mr-1">Période:</span>
            {[['7d','7j'],['30d','30j'],['90d','3m'],['6m','6m'],['1y','1an'],['all','Tout'],['custom','Perso']] .map(([v, lbl]) => (
              <button key={v} onClick={() => applyPreset(v)}
                className={clsx('px-2 py-0.5 rounded text-xs border transition-colors',
                  datePreset === v
                    ? 'bg-blue-600/30 border-blue-500/50 text-blue-300'
                    : 'border-slate-800 text-slate-600 hover:border-slate-600 hover:text-slate-400')}>
                {lbl}
              </button>
            ))}
          </div>
          {datePreset === 'custom' && (
            <div className="grid grid-cols-2 gap-2">
              <div>
                <div className="text-xs text-slate-600 mb-0.5">Depuis</div>
                <input type="date" value={dateFrom} onChange={e => setDateFrom(e.target.value)}
                  className="w-full bg-dark-900 border border-slate-700 rounded px-2 py-1 text-slate-300 text-xs" />
              </div>
              <div>
                <div className="text-xs text-slate-600 mb-0.5">Jusqu'à</div>
                <input type="date" value={dateTo} onChange={e => setDateTo(e.target.value)}
                  className="w-full bg-dark-900 border border-slate-700 rounded px-2 py-1 text-slate-300 text-xs" />
              </div>
            </div>
          )}

          {/* ── Category chips ── */}
          <div className="flex flex-wrap gap-1">
            {CATEGORIES.map(c => (
              <button key={c} onClick={() => setCategory(category === c ? '' : c)}
                className={clsx('px-2 py-0.5 rounded-full text-xs border transition-colors',
                  category === c
                    ? (CAT_COLORS[c] ?? 'bg-slate-700 text-slate-200 border-slate-600')
                    : 'border-slate-800 text-slate-600 hover:border-slate-600 hover:text-slate-400')}>
                {c.replace('_', ' ')}
              </button>
            ))}
          </div>
        </div>

        {/* ── Sort + more filters toggle ── */}
        <div className="px-3 py-1.5 border-b border-slate-800/40 flex items-center gap-2">
          <span className="text-xs text-slate-600">Tri:</span>
          <select value={sortBy} onChange={e => setSortBy(e.target.value as any)}
            className="bg-transparent border-0 text-xs text-slate-400 cursor-pointer focus:outline-none">
            <option value="date">Date</option>
            <option value="score">Score global</option>
            <option value="name">Nom</option>
          </select>
          <button onClick={() => setSortDir(d => d === 'desc' ? 'asc' : 'desc')}
            className="text-xs text-slate-500 hover:text-slate-300 border border-slate-700 rounded px-1.5 py-0.5">
            {sortDir === 'desc' ? '↓' : '↑'}
          </button>
          <div className="flex-1" />
          <button onClick={() => setShowFilters(x => !x)}
            className={clsx('flex items-center gap-1 text-xs border rounded px-2 py-0.5 transition-colors',
              (showFilters || instTicker)
                ? 'bg-slate-700 border-slate-600 text-slate-300'
                : 'border-slate-800 text-slate-600 hover:border-slate-600')}>
            <SlidersHorizontal size={10} />
            Filtres {(level || minScore > 0 || evaluated || instTicker) ? '●' : ''}
          </button>
        </div>

        {/* ── Extended filters ── */}
        {showFilters && (
          <div className="px-3 py-2 border-b border-slate-800/40 space-y-2 bg-dark-900/50">
            <div className="grid grid-cols-2 gap-2">
              <div>
                <div className="text-xs text-slate-600 mb-0.5">Niveau</div>
                <select value={level} onChange={e => setLevel(e.target.value)}
                  className="w-full bg-dark-900 border border-slate-700 rounded px-2 py-1 text-slate-300 text-xs">
                  <option value="">Tous</option>
                  {LEVELS.map(l => <option key={l} value={l}>{l}</option>)}
                </select>
              </div>
              <div>
                <div className="text-xs text-slate-600 mb-0.5">Évaluation</div>
                <select value={evaluated} onChange={e => setEvaluated(e.target.value as any)}
                  className="w-full bg-dark-900 border border-slate-700 rounded px-2 py-1 text-slate-300 text-xs">
                  <option value="">Tous</option>
                  <option value="true">Évalués IA</option>
                  <option value="false">Non évalués</option>
                </select>
              </div>
            </div>
            <div>
              <div className="text-xs text-slate-600 mb-0.5">Score global min: <span className="text-slate-400">{minScore.toFixed(2)}</span></div>
              <input type="range" min="0" max="1" step="0.05" value={minScore}
                onChange={e => setMinScore(+e.target.value)} className="w-full accent-slate-400" />
            </div>

            {/* ── Instrument impact filter ── */}
            <div className="border-t border-slate-800/60 pt-2">
              <button onClick={() => setShowInst(x => !x)}
                className={clsx('flex items-center gap-1.5 text-xs font-semibold mb-2 transition-colors',
                  instTicker ? 'text-amber-400' : 'text-slate-500 hover:text-slate-300')}>
                <BarChart2 size={11} />
                Impact sur instrument {instTicker ? `(${instTicker})` : ''}
                {showInstFilter ? ' ▲' : ' ▼'}
              </button>
              {showInstFilter && (
                <div className="space-y-2">
                  <div className="grid grid-cols-2 gap-2">
                    <div>
                      <div className="text-xs text-slate-600 mb-0.5">Instrument</div>
                      <select value={instTicker} onChange={e => { setInstTicker(e.target.value); if (e.target.value) setSortBy('instrument_score') }}
                        className="w-full bg-dark-900 border border-slate-700 rounded px-2 py-1 text-slate-300 text-xs">
                        <option value="">Tous</option>
                        {ALL_INSTRUMENTS.map(t => <option key={t} value={t}>{t}</option>)}
                      </select>
                    </div>
                    <div>
                      <div className="text-xs text-slate-600 mb-0.5">Direction</div>
                      <select value={instDir} onChange={e => setInstDir(e.target.value)}
                        className="w-full bg-dark-900 border border-slate-700 rounded px-2 py-1 text-slate-300 text-xs">
                        <option value="">Toutes</option>
                        <option value="bullish">🔼 Bullish</option>
                        <option value="bearish">🔽 Bearish</option>
                        <option value="neutral">● Neutre</option>
                        <option value="depends_on_outcome">◆ Conditionnel</option>
                      </select>
                    </div>
                  </div>
                  <div>
                    <div className="text-xs text-slate-600 mb-0.5">
                      Score impact min: <span className="text-amber-300">{instMinScore.toFixed(2)}</span>
                    </div>
                    <input type="range" min="0" max="1" step="0.05" value={instMinScore}
                      onChange={e => setInstMin(+e.target.value)} className="w-full accent-amber-400" />
                  </div>
                  {instTicker && (
                    <div className="text-xs text-amber-300 bg-amber-900/20 rounded px-2 py-1 border border-amber-800/30">
                      ★ Tri automatique par impact sur {instTicker}
                    </div>
                  )}
                  {instTicker && (
                    <button onClick={() => { setInstTicker(''); setInstDir(''); setInstMin(0.3); setSortBy('date') }}
                      className="text-xs text-slate-500 hover:text-red-400">
                      × Effacer filtre instrument
                    </button>
                  )}
                </div>
              )}
            </div>

            {/* ── Generation date filter ── */}
            <div className="border-t border-slate-800/60 pt-2">
              <div className="flex items-center gap-1.5 text-xs font-semibold mb-2 text-slate-500">
                ⚡ Date de génération
                {(genFrom || genTo) && (
                  <button onClick={() => { setGenFrom(''); setGenTo('') }}
                    className="ml-auto text-slate-600 hover:text-red-400 font-normal">× effacer</button>
                )}
              </div>
              <div className="grid grid-cols-2 gap-2">
                <div>
                  <div className="text-xs text-slate-600 mb-0.5">Généré depuis</div>
                  <input type="date" value={genFrom} onChange={e => setGenFrom(e.target.value)}
                    className="w-full bg-dark-900 border border-slate-700 rounded px-2 py-1 text-slate-300 text-xs" />
                </div>
                <div>
                  <div className="text-xs text-slate-600 mb-0.5">Généré jusqu'à</div>
                  <input type="date" value={genTo} onChange={e => setGenTo(e.target.value)}
                    className="w-full bg-dark-900 border border-slate-700 rounded px-2 py-1 text-slate-300 text-xs" />
                </div>
              </div>
              {(genFrom || genTo) && (
                <div className="text-xs text-blue-300 bg-blue-900/20 rounded px-2 py-1 border border-blue-800/30 mt-1.5">
                  ⚡ Filtre actif sur date de génération (created_at)
                </div>
              )}
            </div>
          </div>
        )}

        {/* ── Bulk evaluate ── */}
        {unevaluatedIds.length > 0 && (
          <div className="px-3 py-1.5 border-b border-slate-800/50 flex items-center gap-2 bg-blue-900/10">
            <span className="text-xs text-blue-300 flex-1">{unevaluatedIds.length} non évalués</span>
            {bulkMsg && <span className="text-xs text-emerald-400">{bulkMsg}</span>}
            <button onClick={() => { setBulkIds(new Set(unevaluatedIds)); setTimeout(bulkEvaluate, 50) }}
              disabled={bulking}
              className="flex items-center gap-1 text-xs bg-blue-700/50 hover:bg-blue-700/70 text-blue-200 rounded px-2 py-1">
              {bulking ? <Loader2 size={10} className="animate-spin" /> : <Sparkles size={10} />}
              Évaluer tous
            </button>
          </div>
        )}

        {/* ── Count bar ── */}
        <div className="px-3 py-1 text-xs text-slate-600 border-b border-slate-800/30 flex items-center gap-2">
          <span>{loading ? '...' : total} événement{total !== 1 ? 's' : ''}</span>
          {instTicker && <span className="text-amber-400">· impact {instTicker}</span>}
          {(category || level || search || minScore > 0 || evaluated || dateFrom || dateTo || genFrom || genTo || instTicker) && (
            <button onClick={() => {
              setSearch(''); setCategory(''); setLevel(''); setMinScore(0)
              setEvaluated(''); setDateFrom(''); setDateTo(''); setDatePreset('all')
              setGenFrom(''); setGenTo('')
              setInstTicker(''); setInstDir(''); setInstMin(0.3); setSortBy('date')
            }} className="ml-auto text-xs text-slate-600 hover:text-red-400">× tout effacer</button>
          )}
        </div>

        {/* ── List ── */}
        <div className="flex-1 overflow-y-auto">
          {loading && events.length === 0 ? (
            <div className="flex items-center justify-center h-32 text-slate-500 text-sm">
              <Loader2 size={16} className="animate-spin mr-2" /> Chargement...
            </div>
          ) : events.length === 0 ? (
            <div className="text-center py-12 text-slate-600 text-sm">Aucun événement trouvé</div>
          ) : (
            events.map(ev => (
              <EventRow
                key={ev.id}
                ev={ev}
                selected={selected?.id === ev.id}
                onClick={() => setSelected(ev)}
              />
            ))
          )}
        </div>
      </div>

      {/* ── Right panel: detail ── */}
      <div className="flex-1 overflow-hidden bg-dark-900/40">
        {selected ? (
          <EventDetail
            key={selected.id}
            event={selected}
            onClose={() => setSelected(null)}
            onUpdated={updated => {
              setEvents(evs => evs.map(e => e.id === updated.id ? { ...e, ...updated } : e))
            }}
            onDeleted={id => {
              setEvents(evs => evs.filter(e => e.id !== id))
              setSelected(null)
            }}
          />
        ) : (
          <div className="flex flex-col items-center justify-center h-full text-slate-600 gap-3">
            <BarChart2 size={40} className="opacity-30" />
            <div className="text-sm">Sélectionne un événement pour voir le détail</div>
            <div className="text-xs opacity-60">Sources · Impacts par instrument · Analyse IA</div>
          </div>
        )}
      </div>

      {showCreate && <CreateModal onCreated={load} onClose={() => setShowCreate(false)} />}
      </div>}
    </div>
  )
}
