import { useState, useMemo, useEffect, Fragment } from 'react'
import {
  useAllPatterns, useLastScores, useScorePatterns, useAiStatus,
  usePortfolioPositions, useTradeMtm, useRiskProfiles, useMacroRegime, useAddPosition,
  useConfig, useAiTradeProposals, useConfirmAiTradeProposal, useRejectAiTradeProposal,
} from '../hooks/useApi'
import {
  Target, Brain, Plus, RefreshCw, ChevronDown, ChevronUp, CheckCircle2,
  LayoutGrid, List, Terminal, Bot, Check, X as XIcon,
} from 'lucide-react'
import clsx from 'clsx'
import { format } from 'date-fns'
import { fr } from 'date-fns/locale'

// ── Scoring utilities (re-exported for Dashboard use) ─────────────────────────
export const scoreColor = (s: number) => {
  if (s >= 70) return 'text-emerald-400'
  if (s >= 50) return 'text-yellow-400'
  return 'text-slate-400'
}
export const scoreBg = (s: number) => {
  if (s >= 70) return 'bg-emerald-500'
  if (s >= 50) return 'bg-yellow-500'
  return 'bg-slate-600'
}

// ── Constants ─────────────────────────────────────────────────────────────────
const BUCKET_ICONS: Record<string, string> = {
  actualites: '📰', calendrier: '📅', prix: '📈', rr: '⚖️',
  geo: '🌍', eco: '🌐', flux: '📡',
  banques: '🏦', macro_cal: '📋',
  taux: '📉', energie: '⛽', forex_sig: '💱', actions: '📊', vix: '⚡',
  asymetrie: '⚖️', timing_rr: '⏱️',
}

export const CATEGORIES = [
  { key: 'all', label: 'All' },
  { key: 'energy', label: '⛽ Energy' },
  { key: 'metals', label: '🥇 Metals' },
  { key: 'agriculture', label: '🌾 Agri' },
  { key: 'indices', label: '📊 Indices' },
  { key: 'equities', label: '📈 Equities' },
  { key: 'forex', label: '💱 Forex' },
]

export const THEMATIC_CATEGORIES = [
  { key: 'all', label: 'All' },
  { key: 'géopolitique',     label: '⚔️ Geopo' },
  { key: 'macro_monétaire',  label: '🏦 Macro' },
  { key: 'technique',        label: '📐 Technical' },
  { key: 'commodités_supply',label: '🛢️ Supply' },
  { key: 'risk_off',         label: '🌩️ Risk-off' },
  { key: 'flux_saisonnier',  label: '📅 Flows' },
  { key: 'géo_économique',   label: '🌐 Geo-eco' },
  { key: 'crédit_stress',    label: '💳 Credit' },
]

const SIGNAL_DIR: Record<string, { label: string; color: string }> = {
  bullish:    { label: '▲', color: '#10b981' },
  bearish:    { label: '▼', color: '#ef4444' },
  volatility: { label: '⟷', color: '#a78bfa' },
  neutral:    { label: '↔', color: '#94a3b8' },
}

export interface TradeItem {
  trade: any
  patternName: string
  patternId: string
  assetClass: string
  category: string
  signalDirection: string
  score: number | null
  scoreInfo: any | null
  scoreDelta: number | null
  rankRationale: string | null
  expectedMovePct: number
  convictionScore: number | null
  convictionBonus: number
  convergenceCount: number
  convergencePartners: { name: string; category: string; score: number }[]
}

const BIAS_DISPLAY: Record<string, { label: string; color: string }> = {
  'bullish+': { label: '★★ Aligned',    color: '#10b981' },
  'bullish':  { label: '★ Compatible',  color: '#34d399' },
  'neutral':  { label: '→ Neutral',     color: '#64748b' },
  'bearish':  { label: '✗ Unfavorable', color: '#f97316' },
  'bearish+': { label: '✗✗ Contra',    color: '#ef4444' },
  'defensive':{ label: '⚠ Defensive',  color: '#f59e0b' },
}

// ── IBKR helpers ──────────────────────────────────────────────────────────────
function computeStrikeDollars(price: number, guidance: string, strategy: string): number {
  const g = guidance.toUpperCase()
  const match = g.match(/(\d+)\s*%\s*OTM/)
  if (!match) return price
  const pct = parseInt(match[1]) / 100
  const isBearish = /put|bear/i.test(strategy)
  const raw = isBearish ? price * (1 - pct) : price * (1 + pct)
  const inc = price > 1000 ? 10 : price > 200 ? 5 : price > 50 ? 1 : 0.5
  return Math.round(raw / inc) * inc
}

function estimateExpiryDate(horizonDays: number): Date {
  const d = new Date()
  d.setDate(d.getDate() + horizonDays)
  const dow = d.getDay()
  if (dow !== 5) d.setDate(d.getDate() + ((5 - dow + 7) % 7))
  return d
}

function buildLegs(strategy: string, strike: number | null, price: number | null) {
  const s = strategy.toLowerCase()
  const atm = price ? (() => {
    const inc = price > 1000 ? 10 : price > 200 ? 5 : price > 50 ? 1 : 0.5
    return Math.round(price / inc) * inc
  })() : null
  if (s.includes('bull call spread')) return [
    { action: 'BUY',  type: 'CALL', strike: atm },
    { action: 'SELL', type: 'CALL', strike },
  ]
  if (s.includes('bear put spread')) return [
    { action: 'BUY',  type: 'PUT', strike: atm },
    { action: 'SELL', type: 'PUT', strike },
  ]
  if (s.includes('straddle')) return [
    { action: 'BUY', type: 'CALL', strike: atm ?? strike },
    { action: 'BUY', type: 'PUT',  strike: atm ?? strike },
  ]
  if (s.includes('strangle')) return [
    { action: 'BUY', type: 'CALL', strike: price ? Math.round(price * 1.05) : strike },
    { action: 'BUY', type: 'PUT',  strike: price ? Math.round(price * 0.95) : strike },
  ]
  if (s.includes('call')) return [{ action: 'BUY', type: 'CALL', strike }]
  if (s.includes('put'))  return [{ action: 'BUY', type: 'PUT',  strike }]
  return [{ action: 'BUY', type: strategy.toUpperCase(), strike }]
}

// ── Sub-components ────────────────────────────────────────────────────────────
function BucketBar({ score, max }: { score: number; max: number }) {
  const pct = max > 0 ? (score / max) * 100 : 0
  const color = pct >= 75 ? 'bg-emerald-500' : pct >= 50 ? 'bg-yellow-500' : 'bg-red-500/70'
  return (
    <div className="w-12 bg-dark-600 rounded-full h-1 shrink-0">
      <div className={clsx('h-1 rounded-full transition-all', color)} style={{ width: `${Math.min(pct, 100)}%` }} />
    </div>
  )
}

function ScorePillars({ buckets }: { buckets: any[] }) {
  if (!buckets?.length) return null
  return (
    <div className="grid grid-cols-4 gap-1 mb-2">
      {buckets.map((b: any) => {
        const pct = b.max > 0 ? Math.round((b.score / b.max) * 100) : 0
        const color = pct >= 70 ? 'bg-emerald-500' : pct >= 45 ? 'bg-yellow-500' : 'bg-red-500/70'
        return (
          <div key={b.id} className="flex flex-col items-center gap-0.5">
            <div className="w-full bg-slate-700/60 rounded-full h-1">
              <div className={clsx('h-1 rounded-full', color)} style={{ width: `${pct}%` }} />
            </div>
            <span className="text-[9px] text-slate-600 truncate w-full text-center leading-none">
              {BUCKET_ICONS[b.id]} {b.score}/{b.max}
            </span>
          </div>
        )
      })}
    </div>
  )
}

function ScoreJustification({ buckets, keyCatalyst }: { buckets: any[]; keyCatalyst?: string }) {
  return (
    <div className="space-y-2 text-xs">
      {keyCatalyst && (
        <div className="flex gap-1.5 bg-yellow-400/5 border border-yellow-400/20 rounded px-2 py-1.5">
          <span className="text-yellow-400 shrink-0">🔑</span>
          <p className="text-yellow-200/80 leading-snug">{keyCatalyst}</p>
        </div>
      )}
      {buckets.map((b: any) => {
        const pct = b.max > 0 ? Math.round((b.score / b.max) * 100) : 0
        const sc = pct >= 70 ? 'text-emerald-400' : pct >= 45 ? 'text-yellow-400' : 'text-red-400'
        return (
          <div key={b.id} className="bg-dark-700/60 rounded p-2 space-y-1.5">
            <div className="flex items-center gap-1.5">
              <span className="text-base leading-none">{BUCKET_ICONS[b.id] ?? '•'}</span>
              <span className="text-slate-300 font-medium flex-1">{b.label}</span>
              <BucketBar score={b.score} max={b.max} />
              <span className={clsx('font-mono text-right shrink-0 font-bold', sc)}>{b.score}/{b.max}</span>
            </div>
            {b.comment && (
              <p className="text-slate-400 leading-snug pl-1 border-l-2 border-slate-600">{b.comment}</p>
            )}
            {b.subs?.length > 0 && (
              <div className="pl-2 space-y-1.5 border-l border-slate-700/50 ml-1">
                {b.subs.map((sub: any) => {
                  const sp = sub.max > 0 ? Math.round((sub.score / sub.max) * 100) : 0
                  const ssc = sp >= 70 ? 'text-emerald-400' : sp >= 45 ? 'text-yellow-400' : 'text-slate-500'
                  return (
                    <div key={sub.id} className="space-y-0.5">
                      <div className="flex items-center gap-1">
                        <span className="text-[11px]">{BUCKET_ICONS[sub.id] ?? '›'}</span>
                        <span className="text-slate-500 flex-1 truncate">{sub.label}</span>
                        <span className={clsx('font-mono text-[10px] shrink-0', ssc)}>{sub.score}/{sub.max}</span>
                      </div>
                      {sub.comment && (
                        <p className="text-slate-600 leading-snug text-[10px] pl-3">{sub.comment}</p>
                      )}
                    </div>
                  )
                })}
              </div>
            )}
          </div>
        )
      })}
    </div>
  )
}

function IBKRTicket({ strategy, underlying, strikeGuidance, expiryDays, entryPrice, maxLoss, targetGain, timing, invalidation }: {
  strategy: string; underlying: string; strikeGuidance?: string | null
  expiryDays?: number | null; entryPrice?: number | null
  maxLoss?: number | null; targetGain?: number | null
  timing?: string | null; invalidation?: string | null
}) {
  const strike = entryPrice && strikeGuidance
    ? computeStrikeDollars(entryPrice, strikeGuidance, strategy)
    : (entryPrice ?? null)
  const expiryDate = expiryDays ? estimateExpiryDate(expiryDays) : null
  const legs = buildLegs(strategy, strike, entryPrice ?? null)
  const fmtStrike = (s: number | null | undefined) =>
    s == null ? '—' : s >= 100 ? `$${s.toFixed(0)}` : `$${s.toFixed(2)}`
  return (
    <div className="bg-blue-950/20 border border-blue-700/30 rounded p-3 space-y-2.5">
      <div className="text-[10px] font-bold text-blue-300 uppercase tracking-wide flex items-center gap-1.5">
        <Terminal className="w-3 h-3" /> IBKR Ticket
        {!entryPrice && <span className="ml-auto text-slate-600 font-normal normal-case">Price unavailable — check in IBKR</span>}
      </div>
      <div className="grid grid-cols-3 gap-3 text-[10px]">
        <div>
          <div className="text-slate-600 mb-0.5">Underlying</div>
          <div className="text-white font-mono font-bold text-sm">{underlying}</div>
          <div className="text-slate-600 text-[9px]">Security Type: OPT</div>
        </div>
        <div>
          <div className="text-slate-600 mb-0.5">Strategy</div>
          <div className="text-slate-200 font-semibold">{strategy}</div>
          {entryPrice && <div className="text-slate-600 text-[9px]">Underlying: ${entryPrice.toFixed(2)}</div>}
        </div>
        <div>
          <div className="text-slate-600 mb-0.5">Expiry</div>
          <div className="text-slate-200 font-mono">
            {expiryDate ? format(expiryDate, 'd MMM yyyy', { locale: fr }) : expiryDays ? `~${expiryDays}d` : '—'}
          </div>
          <div className="text-slate-600 text-[9px]">{expiryDays ? `${expiryDays}d DTE` : ''}</div>
        </div>
      </div>
      <div className="space-y-1">
        {legs.map((leg, i) => (
          <div key={i} className="flex items-center gap-2 bg-slate-900/70 rounded px-2.5 py-1.5 font-mono text-[10px]">
            <span className={clsx('w-8 font-bold', leg.action === 'BUY' ? 'text-emerald-400' : 'text-red-400')}>{leg.action}</span>
            <span className="text-slate-400">1 contract</span>
            <span className={clsx('font-bold w-10 text-center', leg.type === 'CALL' ? 'text-blue-300' : leg.type === 'PUT' ? 'text-orange-300' : 'text-slate-300')}>{leg.type}</span>
            <span className="text-slate-600">Strike</span>
            <span className="text-yellow-300 font-bold">{fmtStrike(leg.strike)}</span>
            {leg.strike === null && strikeGuidance && <span className="text-slate-500">({strikeGuidance})</span>}
          </div>
        ))}
      </div>
      <div className="grid grid-cols-3 gap-3 text-[10px]">
        <div>
          <div className="text-slate-600 mb-0.5">Order type</div>
          <div className="text-slate-300 font-semibold">LIMIT</div>
          <div className="text-slate-600 text-[9px]">Price = premium in IBKR</div>
        </div>
        <div>
          <div className="text-slate-600 mb-0.5">Max budget</div>
          <div className="text-slate-300 font-mono">{maxLoss ? `${Math.abs(maxLoss).toFixed(0)}€` : '~1,000€'}</div>
          <div className="text-slate-600 text-[9px]">Estimated max loss</div>
        </div>
        <div>
          <div className="text-slate-600 mb-0.5">Target</div>
          <div className="text-emerald-400 font-mono">{targetGain ? `+${targetGain.toFixed(0)}€` : '—'}</div>
        </div>
      </div>
      {timing && (
        <div className="flex gap-1.5 text-[10px] bg-yellow-900/10 border border-yellow-700/20 rounded px-2 py-1.5">
          <span className="text-yellow-400 shrink-0">⏱</span>
          <span className="text-yellow-200/70 leading-snug">{timing}</span>
        </div>
      )}
      {invalidation && (
        <div className="flex gap-1.5 text-[10px] bg-red-900/10 border border-red-700/20 rounded px-2 py-1.5">
          <span className="text-red-400 shrink-0">✗</span>
          <span className="text-red-200/70 leading-snug">{invalidation}</span>
        </div>
      )}
    </div>
  )
}

export function TradeCard({ item, onAdd, macroInfo, addedInfo, profiles }: {
  item: TradeItem
  onAdd: (item: TradeItem) => void
  macroInfo?: { dominant: string; label: string; color: string; emoji: string; assetBias: Record<string, string> } | null
  addedInfo?: { entry_date: string; expiry_days?: number; entry_price?: number; strike_guidance?: string } | null
  profiles?: any[]
}) {
  const [expanded, setExpanded] = useState(false)
  const { trade, patternName, assetClass, category, signalDirection, score, scoreInfo, scoreDelta, rankRationale, expectedMovePct, convictionScore, convictionBonus, convergenceCount, convergencePartners } = item
  const effectiveScore = score !== null ? Math.max(0, Math.min(100, score + (scoreDelta ?? 0))) : null
  const effectiveConviction = convictionScore !== null ? Math.max(0, Math.min(100, convictionScore + (scoreDelta ?? 0))) : effectiveScore
  const gainPct = expectedMovePct ?? 0
  const evNet = effectiveConviction !== null && gainPct > 0
    ? (effectiveConviction / 100) * (gainPct / 100) - (1 - effectiveConviction / 100)
    : null
  const matchedProfile = useMemo(() => {
    if (effectiveConviction === null || !profiles?.length) return undefined
    return profiles.find(prof =>
      prof.enabled && effectiveConviction >= prof.min_score && gainPct >= prof.min_gain_pct
    ) ?? null
  }, [effectiveConviction, gainPct, profiles])
  const breakdown = scoreInfo?.score_breakdown ?? {}
  const rationale = scoreInfo?.summary ?? trade.rationale ?? scoreInfo?.key_catalyst ?? ''
  const maxLoss = trade.max_loss_eur ?? scoreInfo?.recommended_trade?.max_loss_eur
  const target = maxLoss != null && gainPct > 0
    ? Math.round(Math.abs(maxLoss) * gainPct / 100)
    : (trade.target_gain_eur ?? scoreInfo?.recommended_trade?.target_gain_eur)
  const timing = trade.timing_note ?? scoreInfo?.recommended_trade?.timing_note
  const dirInfo = SIGNAL_DIR[signalDirection] ?? null

  return (
    <div className={clsx('card transition-all', {
      'border-emerald-700/50': effectiveConviction !== null && effectiveConviction >= 70,
      'border-yellow-700/30': effectiveConviction !== null && effectiveConviction >= 50 && effectiveConviction < 70,
      'border-slate-700/20': effectiveConviction === null,
    })}>
      <div className="flex items-center gap-1 mb-1">
        <span className="text-xs text-slate-600 line-clamp-1 font-mono flex-1">{patternName}</span>
        {category && <span className="text-[10px] text-violet-400 bg-violet-900/20 border border-violet-700/30 rounded px-1 shrink-0">{category}</span>}
      </div>
      <div className="flex items-start justify-between mb-1.5">
        <div className="flex-1 min-w-0">
          <div className="flex items-center gap-1 flex-wrap">
            <span className="badge badge-blue text-xs">{assetClass}</span>
            {dirInfo && <span className="text-xs font-bold" style={{ color: dirInfo.color }}>{dirInfo.label}</span>}
            {trade.underlying && <span className="text-sm text-white font-semibold font-mono">{trade.underlying}</span>}
            {trade.isRecommended && effectiveScore !== null && (
              <span className="text-xs text-yellow-400 bg-yellow-400/10 border border-yellow-400/30 rounded px-1">★ IA</span>
            )}
          </div>
          {trade.strategy && <span className="badge badge-green text-xs mt-0.5">{trade.strategy}</span>}
        </div>
        {effectiveScore !== null ? (
          <div className="ml-2 shrink-0 text-center min-w-[52px]">
            <div className={clsx('text-2xl font-bold leading-none', scoreColor(effectiveConviction ?? effectiveScore))}>{effectiveConviction ?? effectiveScore}</div>
            <div className="text-xs text-slate-600 flex items-center justify-center gap-0.5">
              <span>/100</span>
              {scoreDelta !== null && scoreDelta !== 0 && (
                <span className={clsx('text-[10px] font-mono font-bold', scoreDelta > 0 ? 'text-emerald-400' : 'text-red-400')}>
                  {scoreDelta > 0 ? '+' : ''}{scoreDelta}
                </span>
              )}
            </div>
            {convictionBonus > 0 && (
              <div className="text-[10px] text-amber-400 font-mono font-bold">⟳+{convictionBonus}</div>
            )}
            {scoreInfo?.score_trend != null && (
              <div className={clsx('text-[10px] font-mono font-bold mt-0.5', scoreInfo.score_trend > 0 ? 'text-emerald-400' : scoreInfo.score_trend < 0 ? 'text-red-400' : 'text-slate-600')}>
                {scoreInfo.score_trend > 0 ? '↑+' : scoreInfo.score_trend < 0 ? '↓' : '→'}{scoreInfo.score_trend !== 0 ? Math.abs(scoreInfo.score_trend) : ''}
              </div>
            )}
          </div>
        ) : (
          <span className="ml-2 shrink-0 text-xs text-slate-500 bg-dark-600 border border-slate-700/40 rounded px-1.5 py-0.5 whitespace-nowrap">to score</span>
        )}
      </div>
      {convergenceCount > 0 && (
        <div className="flex items-center gap-1 mb-1.5 text-[10px] text-amber-400 bg-amber-900/10 border border-amber-700/20 rounded px-1.5 py-0.5">
          <span className="font-bold">⟳ ×{convergenceCount + 1}</span>
          <span className="text-amber-500/70">patterns convergents ·</span>
          <span className="truncate text-amber-400/70">{convergencePartners.slice(0, 2).map(p => p.name.split(' ')[0]).join(', ')}</span>
        </div>
      )}
      {effectiveScore !== null && (
        <>
          <div className="bg-dark-600 rounded-full h-1.5 mb-1.5">
            <div className={clsx('h-1.5 rounded-full', scoreBg(effectiveScore))} style={{ width: `${effectiveScore}%` }} />
          </div>
          {scoreInfo?.buckets?.length > 0 && <ScorePillars buckets={scoreInfo.buckets} />}
        </>
      )}
      {effectiveScore !== null && (
        <div className="flex items-center gap-1.5 mb-2 text-[10px] flex-wrap">
          <span className="text-slate-600">Gain:{' '}<span className={gainPct > 0 ? 'text-slate-300' : 'text-orange-400/80'}>{gainPct > 0 ? `${gainPct}%` : '?'}</span></span>
          {evNet !== null ? (
            <><span className="text-slate-700">·</span>
            <span className={clsx('font-mono font-semibold', evNet >= 0 ? 'text-emerald-400' : 'text-orange-400')}>EV {evNet >= 0 ? '+' : ''}{(evNet * 100).toFixed(0)}%</span></>
          ) : gainPct === 0 ? (
            <><span className="text-slate-700">·</span><span className="text-orange-400/70">EV ?</span></>
          ) : null}
          {matchedProfile !== undefined && (
            <><span className="text-slate-700">·</span>
            {matchedProfile
              ? <span className="font-semibold" style={{ color: matchedProfile.color }}>● {matchedProfile.name}</span>
              : <span className="text-red-400/80">✗ {gainPct === 0 ? 'Gain undefined' : 'No profile'}</span>
            }</>
          )}
        </div>
      )}
      {rankRationale && <div className="text-[10px] text-slate-600 italic mb-1.5 line-clamp-1">{rankRationale}</div>}
      {macroInfo && macroInfo.dominant !== 'incertain' && (() => {
        const bias = macroInfo.assetBias[assetClass] ?? 'neutral'
        const bd = BIAS_DISPLAY[bias] ?? BIAS_DISPLAY['neutral']
        return (
          <div className="flex items-center gap-1.5 mb-2 text-[10px]">
            <span style={{ color: macroInfo.color }}>{macroInfo.emoji} {macroInfo.label}</span>
            <span className="text-slate-700">·</span>
            <span style={{ color: bd.color }}>{bd.label}</span>
          </div>
        )
      })()}
      {rationale && <p className="text-xs text-slate-400 line-clamp-2 mb-2">{rationale}</p>}
      {timing && <div className="text-xs text-yellow-400/80 mb-2">⏱ {timing}</div>}
      {(maxLoss != null || target != null) && (
        <div className="flex gap-2 text-xs mb-2">
          {maxLoss != null && <span className="text-red-400">Max -{Math.abs(maxLoss)}€</span>}
          {target != null && <span className="text-emerald-400">Cible +{target}€</span>}
          {scoreInfo?.confidence && <span className="text-slate-600 ml-auto">conf. {scoreInfo.confidence}%</span>}
        </div>
      )}
      {effectiveScore !== null && (scoreInfo?.buckets?.length > 0 || Object.keys(breakdown).length > 0) && (
        <>
          <button onClick={() => setExpanded(!expanded)}
            className="flex items-center gap-1 text-xs text-slate-500 hover:text-slate-300 mb-1.5 transition-colors">
            {expanded ? <ChevronUp className="w-3 h-3" /> : <ChevronDown className="w-3 h-3" />}
            {expanded ? 'Hide breakdown' : '↳ Score breakdown'}
          </button>
          {expanded && (
            <div className="mb-2">
              {scoreInfo?.buckets?.length > 0 ? (
                <ScoreJustification buckets={scoreInfo.buckets} keyCatalyst={scoreInfo.key_catalyst} />
              ) : (
                <div className="space-y-1 bg-dark-700/50 rounded p-2">
                  {Object.entries(breakdown).map(([k, v]: [string, any]) => (
                    <div key={k} className="flex items-center justify-between text-xs">
                      <span className="text-slate-500 capitalize">{k.replace(/_/g, ' ')}</span>
                      <div className="flex items-center gap-1">
                        <div className="w-14 bg-dark-600 rounded-full h-1">
                          <div className="bg-blue-500 h-1 rounded-full" style={{ width: `${(v / 25) * 100}%` }} />
                        </div>
                        <span className="text-slate-300 w-5 text-right text-xs">{v}/25</span>
                      </div>
                    </div>
                  ))}
                </div>
              )}
            </div>
          )}
        </>
      )}
      {addedInfo && (
        <div className="flex items-center gap-1.5 mb-2 text-[10px] text-emerald-400 bg-emerald-900/20 border border-emerald-700/30 rounded px-2 py-1">
          <CheckCircle2 className="w-3 h-3 shrink-0" />
          <span>Added on {format(new Date(addedInfo.entry_date), "d MMM yyyy", { locale: fr })}</span>
        </div>
      )}
      <button onClick={() => onAdd(item)}
        className={clsx('w-full flex items-center justify-center gap-1 text-xs rounded py-1 transition-all border',
          addedInfo
            ? 'text-slate-500 border-slate-700/30 hover:text-slate-300 hover:border-slate-600'
            : 'text-blue-400 hover:text-white hover:bg-blue-600 border-blue-500/30 hover:border-blue-500'
        )}>
        <Plus className="w-3 h-3" />
        {addedInfo ? 'Add again' : 'Add to portfolio'}
      </button>
    </div>
  )
}

export function TradeRow({ item, onAdd, macroInfo, addedInfo, profiles, rank }: {
  item: TradeItem; onAdd: (item: TradeItem) => void
  macroInfo?: { dominant: string; label: string; color: string; emoji: string; assetBias: Record<string, string> } | null
  addedInfo?: { entry_date: string; expiry_days?: number; entry_price?: number; strike_guidance?: string } | null
  profiles?: any[]; rank: number
}) {
  const [expanded, setExpanded] = useState(false)
  const { trade, patternName, assetClass, category, signalDirection, score, scoreInfo, scoreDelta, rankRationale, expectedMovePct, convictionScore, convictionBonus, convergenceCount, convergencePartners } = item
  const effectiveScore = score !== null ? Math.max(0, Math.min(100, score + (scoreDelta ?? 0))) : null
  const effectiveConviction = convictionScore !== null ? Math.max(0, Math.min(100, convictionScore + (scoreDelta ?? 0))) : effectiveScore
  const gainPct = expectedMovePct ?? 0
  const evNet = effectiveConviction !== null && gainPct > 0
    ? (effectiveConviction / 100) * (gainPct / 100) - (1 - effectiveConviction / 100)
    : null
  const matchedProfile = useMemo(() => {
    if (effectiveConviction === null || !profiles?.length) return undefined
    return profiles.find(p => p.enabled && effectiveConviction >= p.min_score && gainPct >= p.min_gain_pct) ?? null
  }, [effectiveConviction, gainPct, profiles])
  const dirInfo = SIGNAL_DIR[signalDirection] ?? null
  const bias = macroInfo?.assetBias[assetClass] ?? 'neutral'
  const bd = BIAS_DISPLAY[bias] ?? BIAS_DISPLAY['neutral']
  const maxLoss = trade.max_loss_eur ?? scoreInfo?.recommended_trade?.max_loss_eur
  const target = maxLoss != null && gainPct > 0
    ? Math.round(Math.abs(maxLoss) * gainPct / 100)
    : (trade.target_gain_eur ?? scoreInfo?.recommended_trade?.target_gain_eur)
  const daysHeld = addedInfo?.entry_date
    ? Math.floor((Date.now() - new Date(addedInfo.entry_date).getTime()) / 86400000)
    : null
  const horizon = addedInfo?.expiry_days ?? null
  const maturityPct = daysHeld !== null && horizon ? Math.min(100, Math.round(daysHeld / horizon * 100)) : null
  const maturityColor = maturityPct === null ? 'text-slate-700'
    : maturityPct >= 75 ? 'text-orange-400'
    : maturityPct >= 35 ? 'text-emerald-400'
    : maturityPct >= 10 ? 'text-yellow-400'
    : 'text-slate-500'

  return (
    <Fragment>
      <tr
        className={clsx(
          'border-b border-slate-800/60 cursor-pointer transition-colors text-xs',
          expanded ? 'bg-dark-700/50' : 'hover:bg-dark-700/30',
          effectiveScore !== null && effectiveScore >= 70 && 'border-l-2 border-l-emerald-600/50',
          effectiveScore !== null && effectiveScore >= 50 && effectiveScore < 70 && 'border-l-2 border-l-yellow-600/30',
        )}
        onClick={() => setExpanded(v => !v)}
      >
        <td className="pl-3 py-2 text-slate-700 font-mono text-[10px] w-6">{rank}</td>
        <td className="px-2 py-2 max-w-[200px]">
          <div className="flex items-center gap-1">
            <span className="text-slate-400 truncate text-[10px] leading-tight flex-1">{patternName}</span>
            {category && <span className="text-[9px] text-violet-400 border border-violet-700/30 rounded px-0.5 shrink-0">{category.split('_')[0]}</span>}
          </div>
          <div className="flex items-center gap-1 mt-0.5">
            <span className="badge badge-blue text-[10px] py-0">{assetClass}</span>
            {dirInfo && <span className="text-[11px] font-bold" style={{ color: dirInfo.color }}>{dirInfo.label}</span>}
            {trade.underlying && <span className="font-mono text-white text-[11px] font-semibold">{trade.underlying}</span>}
            {trade.isRecommended && <span className="text-yellow-400 text-[10px]">★ IA</span>}
            {convergenceCount > 0 && <span className="text-[10px] text-amber-400 font-bold">⟳×{convergenceCount + 1}</span>}
          </div>
        </td>
        <td className="px-2 py-2">
          {trade.strategy
            ? <span className={clsx('badge text-[10px] py-0', trade.direction === 'bearish' ? 'badge-red' : 'badge-green')}>{trade.strategy}</span>
            : <span className="text-slate-700 text-[10px]">—</span>}
        </td>
        <td className="px-2 py-2 text-right">
          {effectiveConviction !== null ? (
            <div className="flex flex-col items-end gap-0.5">
              <span className={clsx('text-base font-bold leading-none', scoreColor(effectiveConviction))}>{effectiveConviction}</span>
              <div className="flex items-center gap-0.5">
                {scoreDelta !== null && scoreDelta !== 0 && (
                  <span className={clsx('text-[10px] font-mono', scoreDelta > 0 ? 'text-emerald-400' : 'text-red-400')}>
                    {scoreDelta > 0 ? '+' : ''}{scoreDelta}
                  </span>
                )}
                {convictionBonus > 0 && (
                  <span className="text-[10px] font-mono text-amber-400">⟳+{convictionBonus}</span>
                )}
              </div>
            </div>
          ) : <span className="text-slate-600 text-[10px]">to score</span>}
        </td>
        <td className="px-2 py-2 w-20">
          {effectiveConviction !== null && (
            <div className="bg-dark-600 rounded-full h-1.5 w-full">
              <div className={clsx('h-1.5 rounded-full', scoreBg(effectiveConviction))} style={{ width: `${effectiveConviction}%` }} />
            </div>
          )}
        </td>
        <td className="px-2 py-2 text-right font-mono">
          {evNet !== null
            ? <span className={clsx('text-[11px] font-semibold', evNet >= 0 ? 'text-emerald-400' : 'text-orange-400')}>EV {evNet >= 0 ? '+' : ''}{(evNet * 100).toFixed(0)}%</span>
            : <span className="text-slate-700 text-[10px]">—</span>}
        </td>
        <td className="px-2 py-2 text-right font-mono text-slate-400 text-[11px]">{gainPct > 0 ? `${gainPct}%` : '—'}</td>
        <td className="px-2 py-2 text-right text-[10px]">
          {maxLoss != null && <div className="text-red-400">-{Math.abs(maxLoss)}€</div>}
          {target != null && <div className="text-emerald-400">+{target}€</div>}
        </td>
        <td className="px-2 py-2 text-[10px]">
          {matchedProfile !== undefined && (
            matchedProfile
              ? <span className="font-semibold" style={{ color: matchedProfile.color }}>● {matchedProfile.name}</span>
              : <span className="text-red-400/70">✗ Aucun</span>
          )}
        </td>
        <td className="px-2 py-2 text-[10px]" style={{ color: bd.color }}>{bd.label}</td>
        <td className="px-2 py-2 text-right text-[10px] whitespace-nowrap">
          {addedInfo?.entry_date
            ? <span className="text-slate-500">{format(new Date(addedInfo.entry_date), "d MMM", { locale: fr })}</span>
            : <span className="text-slate-800">—</span>}
        </td>
        <td className="px-2 py-2 text-right text-[10px]">
          {daysHeld !== null ? (
            <div className="flex flex-col items-end gap-0.5">
              <span className={clsx('font-mono font-semibold', maturityColor)}>{daysHeld}j{horizon ? `/${horizon}j` : ''}</span>
              {maturityPct !== null && (
                <div className="w-12 h-1 bg-slate-800 rounded-full overflow-hidden">
                  <div className={clsx('h-full rounded-full', maturityPct >= 75 ? 'bg-orange-500' : maturityPct >= 35 ? 'bg-emerald-500' : 'bg-yellow-500')}
                    style={{ width: `${maturityPct}%` }} />
                </div>
              )}
            </div>
          ) : <span className="text-slate-800">—</span>}
        </td>
        <td className="pr-3 py-2 text-slate-600 text-right">
          {expanded ? <ChevronUp className="w-3.5 h-3.5 inline" /> : <ChevronDown className="w-3.5 h-3.5 inline" />}
        </td>
      </tr>
      {expanded && (
        <tr className="bg-dark-900/60">
          <td colSpan={13} className="px-0 py-0 border-b border-blue-700/20">
            <div className="p-4 space-y-3 text-xs">
              <div className="grid grid-cols-3 gap-4">
                <div className="col-span-2">
                  {scoreInfo?.key_catalyst && (
                    <div className="flex gap-1.5 bg-yellow-400/5 border border-yellow-400/20 rounded px-2 py-1.5 mb-3">
                      <span className="text-yellow-400 shrink-0">🔑</span>
                      <p className="text-yellow-200/80 leading-snug">{scoreInfo.key_catalyst}</p>
                    </div>
                  )}
                  {rankRationale && <p className="text-slate-500 italic mb-2">{rankRationale}</p>}
                  {scoreInfo?.summary && <p className="text-slate-400 mb-3">{scoreInfo.summary}</p>}
                  {scoreInfo?.buckets?.length > 0 && (
                    <ScoreJustification buckets={scoreInfo.buckets} keyCatalyst={undefined} />
                  )}
                </div>
                <div className="space-y-3">
                  {scoreInfo?.contra_signals?.length > 0 && (
                    <div className="bg-orange-900/10 border border-orange-700/20 rounded p-2 space-y-1">
                      <div className="text-orange-400 font-semibold uppercase text-[9px] tracking-wide">Contra signals</div>
                      {scoreInfo.contra_signals.slice(0, 3).map((cs: any, i: number) => (
                        <div key={i} className="text-orange-300/70 text-[10px] leading-snug">{cs.title ?? cs}</div>
                      ))}
                    </div>
                  )}
                  {macroInfo && macroInfo.dominant !== 'incertain' && (
                    <div className="text-[11px]">
                      <span style={{ color: macroInfo.color }}>{macroInfo.emoji} {macroInfo.label}</span>
                      <span className="text-slate-600 mx-1">·</span>
                      <span style={{ color: bd.color }}>{bd.label}</span>
                    </div>
                  )}
                  {addedInfo && (
                    <div className="flex items-center gap-1.5 text-[10px] text-emerald-400 bg-emerald-900/20 border border-emerald-700/30 rounded px-2 py-1">
                      <CheckCircle2 className="w-3 h-3 shrink-0" />
                      Added on {format(new Date(addedInfo.entry_date), "d MMM yyyy", { locale: fr })}
                    </div>
                  )}
                  <button
                    onClick={e => { e.stopPropagation(); onAdd(item) }}
                    className={clsx('w-full flex items-center justify-center gap-1 text-xs rounded py-1.5 transition-all border',
                      addedInfo
                        ? 'text-slate-500 border-slate-700/30 hover:text-slate-300 hover:border-slate-600'
                        : 'text-blue-400 hover:text-white hover:bg-blue-600 border-blue-500/30 hover:border-blue-500'
                    )}>
                    <Plus className="w-3 h-3" />
                    {addedInfo ? 'Add again' : 'Add to portfolio'}
                  </button>
                </div>
              </div>
              {trade.strategy && trade.underlying && (
                <IBKRTicket
                  strategy={trade.strategy}
                  underlying={trade.underlying}
                  strikeGuidance={trade.strike_guidance ?? scoreInfo?.recommended_trade?.strike_guidance ?? addedInfo?.strike_guidance}
                  expiryDays={scoreInfo?.recommended_trade?.expiry_days ?? trade.expiry_days ?? trade.horizon_days ?? addedInfo?.expiry_days}
                  entryPrice={addedInfo?.entry_price ?? null}
                  maxLoss={trade.max_loss_eur ?? scoreInfo?.recommended_trade?.max_loss_eur}
                  targetGain={trade.target_gain_eur ?? scoreInfo?.recommended_trade?.target_gain_eur}
                  timing={scoreInfo?.recommended_trade?.timing_note}
                  invalidation={scoreInfo?.recommended_trade?.invalidation}
                />
              )}
            </div>
          </td>
        </tr>
      )}
    </Fragment>
  )
}

// ── AI-proposed trades — awaiting explicit user confirmation ──────────────────
// Created by the chat widget's propose_trade tool call. Deliberately kept
// separate from TradeCard/TradeItem (those are derived from scored patterns —
// grafting an AI-sourced item onto that shape would be fragile).
function AiProposedTradesSection() {
  const { data: proposals } = useAiTradeProposals('pending')
  const { mutate: confirmProposal, isPending: confirming } = useConfirmAiTradeProposal()
  const { mutate: rejectProposal, isPending: rejecting } = useRejectAiTradeProposal()
  const [pendingId, setPendingId] = useState<string | null>(null)
  const [error, setError] = useState<string | null>(null)

  if (!proposals || proposals.length === 0) return null

  const handleConfirm = (id: string) => {
    setPendingId(id); setError(null)
    confirmProposal(id, {
      onError: (e: any) => setError(e?.response?.data?.detail ?? 'Erreur lors de la confirmation.'),
      onSettled: () => setPendingId(null),
    })
  }
  const handleReject = (id: string) => {
    setPendingId(id)
    rejectProposal(id, { onSettled: () => setPendingId(null) })
  }

  return (
    <div className="card border-blue-500/30">
      <h3 className="section-title flex items-center gap-1.5 mb-3">
        <Bot className="w-3.5 h-3.5 text-blue-400" /> Idées proposées par l'IA
        <span className="text-slate-600 font-normal">({proposals.length} en attente)</span>
      </h3>
      {error && <div className="text-xs text-red-400 mb-2">{error}</div>}
      <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
        {proposals.map(p => (
          <div key={p.id} className="rounded-lg border border-slate-700/40 bg-dark-900/60 p-3 space-y-2">
            <div className="flex items-start justify-between gap-2">
              <div>
                <div className="text-sm font-semibold text-white">{p.title}</div>
                <div className="text-[11px] text-slate-500">{p.underlying} · {p.strategy} · {p.capital_invested.toLocaleString('fr-FR')} €</div>
              </div>
              <span className="text-[10px] px-1.5 py-0.5 rounded bg-blue-900/40 text-blue-300 shrink-0">🤖 IA</span>
            </div>
            {p.rationale && <div className="text-[11px] text-slate-400 leading-snug">{p.rationale}</div>}
            <div className="flex items-center gap-2 pt-1">
              <button
                onClick={() => handleConfirm(p.id)}
                disabled={confirming && pendingId === p.id}
                className="flex-1 flex items-center justify-center gap-1 text-xs px-2 py-1.5 rounded bg-emerald-600/20 text-emerald-400 hover:bg-emerald-600/30 disabled:opacity-40 transition-colors"
              >
                <Check className="w-3 h-3" /> Confirmer
              </button>
              <button
                onClick={() => handleReject(p.id)}
                disabled={rejecting && pendingId === p.id}
                className="flex-1 flex items-center justify-center gap-1 text-xs px-2 py-1.5 rounded bg-slate-700/40 text-slate-400 hover:bg-slate-700/60 disabled:opacity-40 transition-colors"
              >
                <XIcon className="w-3 h-3" /> Rejeter
              </button>
            </div>
          </div>
        ))}
      </div>
    </div>
  )
}

// ── Self-contained Trade Ideas Tab ────────────────────────────────────────────
export function TradeIdeasTab() {
  const { data: allPatternsData } = useAllPatterns()
  const { data: lastScoresData } = useLastScores()
  const { data: aiStatus } = useAiStatus()
  const { data: positions, refetch: refetchPositions } = usePortfolioPositions('open')
  const { data: tradeMtmData } = useTradeMtm(30)
  const { data: riskProfilesData } = useRiskProfiles()
  const { data: macroData } = useMacroRegime()
  const { mutate: scorePatterns, isPending: scoring } = useScorePatterns()
  const { mutate: addPos } = useAddPosition()

  const riskProfiles: any[] = (riskProfilesData as any)?.profiles ?? []
  const allPatterns: any[] = allPatternsData ?? []
  const scoredAt: string | null = lastScoresData?.scored_at ?? null

  const { data: configData } = useConfig()
  const cs = (configData as any) ?? {}
  const tradeBudget: number = cs.trade_budget_eur ?? 5000
  const horizonMin: number  = cs.preferred_horizon_min ?? 30
  const horizonMax: number  = cs.preferred_horizon_max ?? 180

  const [categoryFilter, setCategoryFilter] = useState('all')
  const [thematicFilter, setThematicFilter] = useState('all')
  const [horizonFilter, setHorizonFilter]   = useState('all')
  const [topN, setTopN] = useState(10)
  const [viewMode, setViewMode] = useState<'cards' | 'grid'>('grid')
  const [toast, setToast] = useState<{ title: string; sub: string } | null>(null)

  useEffect(() => {
    if (!toast) return
    const t = setTimeout(() => setToast(null), 3500)
    return () => clearTimeout(t)
  }, [toast])

  const scoreMap = useMemo(() => {
    const map: Record<string, any> = {}
    for (const sp of (lastScoresData?.scored_patterns ?? [])) {
      if (sp.pattern_id) map[sp.pattern_id] = sp
    }
    return map
  }, [lastScoresData])

  const macroInfo = useMemo(() => {
    if (!macroData?.scenarios) return null
    const sc = macroData.scenarios
    const dom = sc.dominant ?? 'incertain'
    const m = sc.meta?.[dom] ?? { label: dom, color: '#94a3b8', emoji: '?' }
    return { dominant: dom, label: m.label, color: m.color, emoji: m.emoji, assetBias: sc.asset_bias?.[dom] ?? {} }
  }, [macroData])

  const addedMap = useMemo(() => {
    const map: Record<string, { entry_date: string; expiry_days?: number }> = {}
    const upsert = (key: string, entry_date: string, expiry_days?: number) => {
      if (!map[key] || entry_date > map[key].entry_date) map[key] = { entry_date, expiry_days }
    }
    for (const pos of (positions as any[] ?? [])) {
      const strategy  = (pos.strategy ?? '').toLowerCase()
      const trigger   = (pos.geo_trigger ?? '').toLowerCase()
      const underly   = (pos.underlying ?? '').toLowerCase()
      const expiry_days = pos.expiry_days ?? undefined
      if (trigger) upsert(`trigger:${trigger}:${strategy}`, pos.entry_date ?? '', expiry_days)
      if (underly) upsert(`ticker:${underly}:${strategy}`,  pos.entry_date ?? '', expiry_days)
    }
    return map
  }, [positions])

  const mtmMap = useMemo(() => {
    const map: Record<string, { entry_date: string; expiry_days?: number; entry_price?: number; strike_guidance?: string }> = {}
    for (const t of ((tradeMtmData as any)?.trades ?? [])) {
      const pid = t.pattern_id as string
      if (!pid) continue
      const entry_date = (t.entry_date as string) ?? ''
      if (!map[pid] || entry_date > map[pid].entry_date)
        map[pid] = { entry_date, expiry_days: t.expiry_days_at_entry ?? t.horizon_days, entry_price: t.entry_price, strike_guidance: t.strike_guidance }
    }
    return map
  }, [tradeMtmData])

  const HORIZON_BUCKETS = [
    { key: 'all',  label: 'All horizons' },
    { key: 'lt30', label: '< 1M',         min: 0,   max: 29  },
    { key: '1-3m', label: '1–3M',         min: 30,  max: 90  },
    { key: '3-6m', label: '3–6M',         min: 91,  max: 180 },
    { key: 'gt6m', label: '> 6M',         min: 181, max: 9999 },
  ]

  const { topScored, allUnscored } = useMemo(() => {
    const bucket = HORIZON_BUCKETS.find(b => b.key === horizonFilter)
    const filtered = allPatterns.filter(p => {
      if (categoryFilter !== 'all' && p.asset_class !== categoryFilter) return false
      if (thematicFilter !== 'all' && p.category !== thematicFilter) return false
      if (bucket && bucket.key !== 'all') {
        const hd = p.horizon_days ?? 0
        if (hd < bucket.min! || hd > bucket.max!) return false
      }
      return true
    })
    const scored: TradeItem[] = []
    const unscored: TradeItem[] = []
    for (const p of filtered) {
      const sp = scoreMap[p.id]
      const patCategory = p.category ?? ''
      const patDirection = p.signal_direction ?? ''
      if (sp) {
        const recUnderlying = sp.recommended_trade?.underlying
        const trades: any[] = p.suggested_trades ?? []
        const tradesOrFallback = trades.length > 0 ? trades : [sp.recommended_trade ?? {}]
        const rankings: any[] = sp.trade_rankings ?? []
        for (const t of tradesOrFallback) {
          const ranking = rankings.find((r: any) =>
            r.underlying === t.underlying && (!r.strategy || !t.strategy || r.strategy === t.strategy)
          )
          scored.push({
            trade: { ...t, isRecommended: recUnderlying && t.underlying === recUnderlying },
            patternName: p.name, patternId: p.id,
            assetClass: t.asset_class ?? p.asset_class,
            category: sp.category ?? patCategory,
            signalDirection: sp.signal_direction ?? patDirection,
            score: sp.score, scoreInfo: sp,
            scoreDelta: ranking?.score_delta ?? null,
            rankRationale: ranking?.rationale ?? null,
            expectedMovePct: t.expected_move_pct ?? p.expected_move_pct ?? 0,
            convictionScore: sp.conviction_score ?? null,
            convictionBonus: sp.conviction_bonus ?? 0,
            convergenceCount: sp.convergence_count ?? 0,
            convergencePartners: sp.convergence_partners ?? [],
          })
        }
      } else {
        const trades: any[] = p.suggested_trades ?? []
        const base = { category: patCategory, signalDirection: patDirection, score: null, scoreInfo: null, scoreDelta: null, rankRationale: null, convictionScore: null, convictionBonus: 0, convergenceCount: 0, convergencePartners: [] }
        if (trades.length === 0) {
          unscored.push({ trade: {}, patternName: p.name, patternId: p.id, assetClass: p.asset_class, expectedMovePct: p.expected_move_pct ?? 0, ...base })
        } else {
          for (const t of trades) {
            unscored.push({ trade: t, patternName: p.name, patternId: p.id, assetClass: t.asset_class ?? p.asset_class, expectedMovePct: t.expected_move_pct ?? p.expected_move_pct ?? 0, ...base })
          }
        }
      }
    }
    // Sort by conviction_score (includes convergence bonus) then effective trade score
    const effScore = (item: TradeItem) => Math.max(0, Math.min(100, (item.score ?? 0) + (item.scoreDelta ?? 0)))
    const convScore = (item: TradeItem) => item.convictionScore !== null ? Math.max(0, Math.min(100, item.convictionScore + (item.scoreDelta ?? 0))) : effScore(item)
    scored.sort((a, b) => convScore(b) - convScore(a))
    const sliced = topN === 0 ? scored : scored.slice(0, topN)
    return { topScored: sliced, allUnscored: unscored }
  }, [allPatterns, scoreMap, categoryFilter, thematicFilter, horizonFilter, topN])

  const handleAdd = (item: TradeItem) => {
    const t = item.trade
    const sp = item.scoreInfo
    addPos({
      title: `${t.strategy ?? ''} ${t.underlying ?? ''} — ${item.patternName}`.trim(),
      underlying: t.underlying ?? item.patternName,
      strategy: t.strategy ?? '',
      asset_class: item.assetClass,
      expiry_days: t.expiry_days ?? sp?.recommended_trade?.expiry_days ?? 90,
      capital_invested: Math.abs(t.max_loss_eur ?? sp?.recommended_trade?.max_loss_eur ?? 1000),
      geo_trigger: item.patternName,
      rationale: t.rationale ?? sp?.key_catalyst ?? '',
      legs: [{ option_type: (t.strategy ?? '').toLowerCase().includes('put') ? 'put' : 'call', quantity: 1, position: 'long' }],
    }, {
      onSuccess: () => {
        refetchPositions()
        setToast({ title: 'Added to portfolio', sub: `${t.strategy ?? ''} ${t.underlying ?? ''} · ${item.patternName}`.trim() })
      },
    })
  }

  const getAddedInfo = (item: TradeItem) => {
    const strategy = (item.trade.strategy ?? '').toLowerCase()
    const trigger  = (item.patternName ?? '').toLowerCase()
    const underly  = (item.trade.underlying ?? '').toLowerCase()
    return addedMap[`trigger:${trigger}:${strategy}`] ?? addedMap[`ticker:${underly}:${strategy}`] ?? mtmMap[item.patternId] ?? null
  }

  return (
    <div className="space-y-4">
      <AiProposedTradesSection />

      {/* Toolbar */}
      <div className="flex items-center justify-between gap-3 flex-wrap">
        <div className="flex items-center gap-2 flex-wrap">
          <h2 className="section-title flex items-center gap-1 mb-0">
            <Target className="w-3 h-3" /> Trade Ideas
          </h2>
          {scoredAt && (
            <span className="text-xs text-slate-600">— scored on {format(new Date(scoredAt), "d MMM HH:mm", { locale: fr })}</span>
          )}
          {topScored.length > 0 && (
            <span className="text-xs text-emerald-500">{topScored.length} scored · {allUnscored.length} to score</span>
          )}
        </div>
        <div className="flex items-center gap-2 flex-wrap">
          {/* Asset class filter */}
          <div className="flex items-center gap-0.5 bg-dark-700 rounded p-0.5">
            {CATEGORIES.map(c => (
              <button key={c.key} onClick={() => setCategoryFilter(c.key)}
                className={clsx('px-2 py-1 rounded text-xs transition-colors', {
                  'bg-blue-600 text-white': categoryFilter === c.key,
                  'text-slate-400 hover:text-slate-200': categoryFilter !== c.key,
                })}>
                {c.label}
              </button>
            ))}
          </div>
          {/* Thematic category filter */}
          <div className="flex items-center gap-0.5 bg-dark-700/60 rounded p-0.5 border border-slate-700/40">
            {THEMATIC_CATEGORIES.map(c => (
              <button key={c.key} onClick={() => setThematicFilter(c.key)}
                className={clsx('px-2 py-1 rounded text-xs transition-colors', {
                  'bg-violet-700 text-white': thematicFilter === c.key,
                  'text-slate-500 hover:text-slate-300': thematicFilter !== c.key,
                })}>
                {c.label}
              </button>
            ))}
          </div>
          {/* Horizon filter */}
          <div className="flex items-center gap-0.5 bg-dark-700/60 rounded p-0.5 border border-teal-700/30">
            {HORIZON_BUCKETS.map(b => (
              <button key={b.key} onClick={() => setHorizonFilter(b.key)}
                className={clsx('px-2 py-1 rounded text-xs transition-colors', {
                  'bg-teal-700 text-white': horizonFilter === b.key,
                  'text-slate-500 hover:text-slate-300': horizonFilter !== b.key,
                })}>
                {b.label}
              </button>
            ))}
          </div>
          {/* Budget indicator */}
          <div className="flex items-center gap-1.5 text-xs text-slate-400 border border-slate-700/40 rounded px-2 py-1">
            <span className="text-slate-500">Budget</span>
            <span className="font-semibold text-slate-200">€{tradeBudget.toLocaleString()}</span>
            <span className="text-slate-600">·</span>
            <span className="text-slate-500">{horizonMin}–{horizonMax}d</span>
          </div>
          {/* Top N */}
          <div className="flex items-center gap-0.5 bg-dark-700 rounded p-0.5">
            {[5, 10, 20, 0].map(n => (
              <button key={n} onClick={() => setTopN(n)}
                className={clsx('px-2 py-1 rounded text-xs transition-colors', {
                  'bg-slate-600 text-white': topN === n,
                  'text-slate-400 hover:text-slate-200': topN !== n,
                })}>
                {n === 0 ? 'All' : `Top ${n}`}
              </button>
            ))}
          </div>
          {/* View toggle */}
          <div className="flex items-center gap-0.5 bg-dark-700 rounded p-0.5">
            <button onClick={() => setViewMode('cards')}
              className={clsx('p-1.5 rounded transition-colors', viewMode === 'cards' ? 'bg-slate-600 text-white' : 'text-slate-500 hover:text-slate-300')}
              title="Vue cartes"><LayoutGrid className="w-3.5 h-3.5" /></button>
            <button onClick={() => setViewMode('grid')}
              className={clsx('p-1.5 rounded transition-colors', viewMode === 'grid' ? 'bg-slate-600 text-white' : 'text-slate-500 hover:text-slate-300')}
              title="Vue grille"><List className="w-3.5 h-3.5" /></button>
          </div>
          {/* Score button */}
          {aiStatus?.enabled ? (
            <button onClick={() => scorePatterns({})}
              disabled={scoring}
              className="flex items-center gap-1.5 bg-blue-600 hover:bg-blue-500 disabled:opacity-50 text-white px-3 py-1.5 rounded text-xs font-semibold transition-all">
              {scoring ? <><RefreshCw className="w-3 h-3 animate-spin" /> Scoring GPT-4o...</> : <><Brain className="w-3 h-3" /> Score patterns</>}
            </button>
          ) : (
            <span className="text-xs text-slate-600 border border-slate-700/30 rounded px-2 py-1">OpenAI key required</span>
          )}
        </div>
      </div>

      {/* Scored trades */}
      {topScored.length > 0 && (
        viewMode === 'cards' ? (
          <div className="grid grid-cols-5 gap-3 mb-5">
            {topScored.map((item, i) => (
              <TradeCard key={`${item.patternId}-scored-${i}`} item={item} onAdd={handleAdd} macroInfo={macroInfo} addedInfo={getAddedInfo(item)} profiles={riskProfiles} />
            ))}
          </div>
        ) : (
          <div className="overflow-x-auto rounded-lg border border-slate-700/40 mb-5">
            <table className="w-full text-xs">
              <thead>
                <tr className="border-b border-slate-700/60 text-slate-600 text-[10px] uppercase tracking-wide">
                  <th className="pl-3 py-2 text-left w-6">#</th>
                  <th className="px-2 py-2 text-left">Pattern / Ticker</th>
                  <th className="px-2 py-2 text-left">Strategy</th>
                  <th className="px-2 py-2 text-right">Score</th>
                  <th className="px-2 py-2 w-20"></th>
                  <th className="px-2 py-2 text-right">EV</th>
                  <th className="px-2 py-2 text-right">Move</th>
                  <th className="px-2 py-2 text-right">Max/Target</th>
                  <th className="px-2 py-2">Profile</th>
                  <th className="px-2 py-2">Macro</th>
                  <th className="px-2 py-2 text-right">Entry</th>
                  <th className="px-2 py-2 text-right">Duration</th>
                  <th className="pr-3 py-2 w-6"></th>
                </tr>
              </thead>
              <tbody>
                {topScored.map((item, i) => (
                  <TradeRow key={`${item.patternId}-scored-${i}`} item={item} onAdd={handleAdd} macroInfo={macroInfo} addedInfo={getAddedInfo(item)} profiles={riskProfiles} rank={i + 1} />
                ))}
              </tbody>
            </table>
          </div>
        )
      )}

      {/* Unscored trades */}
      {allUnscored.length > 0 && (
        <>
          {topScored.length > 0 && (
            <div className="flex items-center gap-2 text-xs text-slate-600 mb-3">
              <span className="border-b border-slate-700/30 flex-1"></span>
              {allUnscored.length} trade{allUnscored.length > 1 ? 's' : ''} to score
              <span className="border-b border-slate-700/30 flex-1"></span>
            </div>
          )}
          {viewMode === 'cards' ? (
            <div className="grid grid-cols-5 gap-3">
              {allUnscored.map((item, i) => (
                <TradeCard key={`${item.patternId}-unscored-${i}`} item={item} onAdd={handleAdd} macroInfo={macroInfo} addedInfo={getAddedInfo(item)} profiles={riskProfiles} />
              ))}
            </div>
          ) : (
            <div className="overflow-x-auto rounded-lg border border-slate-700/40">
              <table className="w-full text-xs">
                <tbody>
                  {allUnscored.map((item, i) => (
                    <TradeRow key={`${item.patternId}-unscored-${i}`} item={item} onAdd={handleAdd} macroInfo={macroInfo} addedInfo={getAddedInfo(item)} profiles={riskProfiles} rank={topScored.length + i + 1} />
                  ))}
                </tbody>
              </table>
            </div>
          )}
        </>
      )}

      {allPatterns.length === 0 && (
        <div className="card text-center py-8 text-slate-500 text-sm">
          Start the backend — patterns loading…
        </div>
      )}

      {/* Toast */}
      {toast && (
        <div className="fixed bottom-6 right-6 z-50 flex items-start gap-3 bg-emerald-950 border border-emerald-600/50 text-emerald-200 rounded-xl px-4 py-3 shadow-2xl min-w-[260px]">
          <CheckCircle2 className="w-5 h-5 text-emerald-400 shrink-0 mt-0.5" />
          <div>
            <div className="text-sm font-semibold text-emerald-300">{toast.title}</div>
            <div className="text-xs text-emerald-500 mt-0.5 line-clamp-2">{toast.sub}</div>
          </div>
        </div>
      )}
    </div>
  )
}
