import { useState, useEffect, useCallback, useMemo } from 'react'
import { useParams, useNavigate } from 'react-router-dom'
import {
  Sparkles, RefreshCw, ChevronDown, TrendingUp, TrendingDown,
  Minus, BarChart2, Clock, Calendar, AlertCircle, Pencil, Save, X, Plus, Trash2,
} from 'lucide-react'
import axios from 'axios'
import clsx from 'clsx'
import InstrumentChart from '../components/InstrumentChart'

const api = axios.create({ baseURL: '/api' })

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

interface Driver {
  key: string; label: string; weight: number; keywords: string[]; type?: string
}

interface InstrumentConfig {
  id: string; name: string; yf_ticker: string; category: string; currency: string
  description: string; drivers: Driver[]
  regime_labels: string[]
  chart: { ma_periods: number[]; show_volume: boolean }
  correlation_instruments: string[]
}

interface PriceCandle { time: string; open: number; high: number; low: number; close: number; volume: number }
interface LinePoint    { time: string; value: number }

interface SnapshotEvent {
  date: string; end_date: string | null; title: string; level: string
  category: string; sub_type?: string; description: string; impact_score: number
  expected_value?: string | null; actual_value?: string | null
  surprise_pct?: number | null; unit?: string | null; absorption_pct?: number | null
}

interface RegimeSignals {
  ma50_above_ma200: boolean | null; ma50_slope_pct: number; ma200_slope_pct: number
  momentum_20d_pct: number; dist_ma200_pct: number; vol_ratio_pct: number
}

interface TrendMetrics {
  ma50_slope_5d: number; ma200_slope_20d: number; rsi14_current: number
  atr14_current: number; atr_vs_3m_avg_pct: number; momentum_1m_pct: number
  momentum_3m_pct: number; dist_ma50_pct: number | null; dist_ma200_pct: number | null
  current_price: number; high_52w: number; low_52w: number
}

interface MacroRegime {
  dominant: string
  label: string
  color: string
  emoji: string
  scores: Record<string, number>
  ranked: string[]
  asset_bias: Record<string, any>
}

interface GaugeValue {
  id: string; label: string; value: number | null; change_pct: number | null
  unit: string; bloc: string; note?: string
}

interface MacroGaugeSnap {
  snapshot_date: string
  dominant: string
  regime_scores: Record<string, number>
  gauges: Record<string, GaugeValue>
}

interface Snapshot {
  instrument: InstrumentConfig
  price_data: PriceCandle[]
  indicators: Record<string, LinePoint[]>
  regime: { current: string; confidence: number; scores: Record<string, number>; signals: RegimeSignals }
  macro_regime: MacroRegime
  trend: TrendMetrics
  events: SnapshotEvent[]
  current_price: number; change_pct: number; change_abs: number; period: string
}

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

const CATEGORY_ORDER = ['equity_index', 'equity_intl', 'metal', 'energy', 'bond', 'credit', 'fx', 'volatility', 'stock', 'crypto']
const CATEGORY_LABELS: Record<string, string> = {
  equity_index: 'Indices US', equity_intl: 'Intl Equity', metal: 'Métaux',
  energy: 'Énergie', bond: 'Obligataire', credit: 'Crédit',
  fx: 'Forex', volatility: 'Volatilité', stock: 'Actions', crypto: 'Crypto',
}

function regimeColor(label: string): string {
  const l = label.toLowerCase()
  if (/bull|expan|recov|rally|strength|risk.on|inflow|demand|falling|weakness/i.test(l)) return 'emerald'
  if (/bear|crash|crunch|shock|squeeze|fear|risk.off|selloff|crisis|deficit/i.test(l)) return 'red'
  if (/volatil|spike|stress/i.test(l)) return 'orange'
  if (/range|neutral|consolid|compress/i.test(l)) return 'slate'
  return 'blue'
}

function pctColor(v: number | null, inverse = false): string {
  if (v === null || v === undefined) return 'text-slate-400'
  const pos = inverse ? v < 0 : v > 0
  const neg = inverse ? v > 0 : v < 0
  return pos ? 'text-emerald-400' : neg ? 'text-red-400' : 'text-slate-400'
}

function Arrow({ v }: { v: number }) {
  if (v > 0.3) return <TrendingUp className="w-3.5 h-3.5 text-emerald-400" />
  if (v < -0.3) return <TrendingDown className="w-3.5 h-3.5 text-red-400" />
  return <Minus className="w-3.5 h-3.5 text-slate-500" />
}

function fmt(v: number | null, digits = 2): string {
  if (v === null || v === undefined) return '—'
  return (v >= 0 ? '+' : '') + v.toFixed(digits)
}

function fmtDateFR(s: string | null): string {
  if (!s) return '—'
  const [y, m, d] = s.split('-')
  return `${d}/${m}/${y}`
}

function pctN(a: number | undefined, b: number | undefined): number {
  if (a === undefined || b === undefined || b === 0) return 0
  return ((a - b) / b) * 100
}

// ── Driver type config ────────────────────────────────────────────────────────

const DRIVER_TYPES = ['event_calendar', 'report', 'geopolitical', 'fundamental', 'sentiment', 'technical'] as const
type DriverType = typeof DRIVER_TYPES[number]

const DRIVER_TYPE_CFG: Record<string, { short: string; tw: string }> = {
  event_calendar: { short: 'Cal.',  tw: 'text-amber-400 bg-amber-900/30 border-amber-700/30' },
  report:         { short: 'Rep.',  tw: 'text-blue-400 bg-blue-900/30 border-blue-700/30' },
  geopolitical:   { short: 'Geo.',  tw: 'text-red-400 bg-red-900/30 border-red-700/30' },
  fundamental:    { short: 'Fund.', tw: 'text-emerald-400 bg-emerald-900/30 border-emerald-700/30' },
  sentiment:      { short: 'Sent.', tw: 'text-violet-400 bg-violet-900/30 border-violet-700/30' },
  technical:      { short: 'Tech.', tw: 'text-cyan-400 bg-cyan-900/30 border-cyan-700/30' },
}

// ── Macro regime colour mapping ───────────────────────────────────────────────

const MACRO_COLOR_MAP: Record<string, string> = {
  goldilocks:       'emerald',
  desinflation:     'cyan',
  soft_landing:     'blue',
  reflation:        'amber',
  stagflation:      'orange',
  inflation_shock:  'red',
  recession:        'red',
  crise_liquidite:  'red',
  incertain:        'slate',
}

function macroRegimeColor(dominant: string): string {
  return MACRO_COLOR_MAP[dominant] ?? 'slate'
}

// ── RegimeCard ────────────────────────────────────────────────────────────────

function RegimeCard({
  regime, macroRegime, signalsAt, dateLabel,
}: {
  regime: Snapshot['regime']
  macroRegime: MacroRegime | null
  signalsAt: RegimeSignals | null
  dateLabel: string
}) {
  const signals = signalsAt ?? regime.signals
  const col = regimeColor(regime.current)

  const borderMap: Record<string, string> = {
    emerald: 'border-emerald-700/40 bg-emerald-950/30',
    red:     'border-red-700/40 bg-red-950/30',
    orange:  'border-orange-700/40 bg-orange-950/30',
    slate:   'border-slate-700/40 bg-slate-900/30',
    blue:    'border-blue-700/40 bg-blue-950/30',
    cyan:    'border-cyan-700/40 bg-cyan-950/30',
    amber:   'border-amber-700/40 bg-amber-950/30',
  }

  const ma50Color = signals.ma50_above_ma200 ? 'text-emerald-400' : 'text-red-400'
  const volColor  = (signals.vol_ratio_pct ?? 0) > 130 ? 'text-orange-400' : (signals.vol_ratio_pct ?? 0) < 70 ? 'text-cyan-400' : 'text-slate-300'

  const metrics: { label: string; value: string; sub?: string; color: string }[] = [
    {
      label: 'MA50 / MA200',
      value: signals.ma50_above_ma200 === true ? 'Au-dessus' : signals.ma50_above_ma200 === false ? 'En-dessous' : '—',
      sub: signals.ma50_above_ma200 === true ? '↑ Golden cross' : signals.ma50_above_ma200 === false ? '↓ Death cross' : '',
      color: ma50Color,
    },
    { label: 'Slope MA50 (10j)',  value: fmt(signals.ma50_slope_pct) + '%',  color: pctColor(signals.ma50_slope_pct) },
    { label: 'Slope MA200 (10j)', value: fmt(signals.ma200_slope_pct) + '%', color: pctColor(signals.ma200_slope_pct) },
    { label: 'Momentum 20j',      value: fmt(signals.momentum_20d_pct) + '%', color: pctColor(signals.momentum_20d_pct) },
    {
      label: 'Distance MA200',
      value: fmt(signals.dist_ma200_pct) + '%',
      sub: signals.dist_ma200_pct > 10 ? 'Surextension' : signals.dist_ma200_pct < -10 ? 'Survendu' : 'Neutre',
      color: pctColor(signals.dist_ma200_pct),
    },
    {
      label: 'Volatilité ATR',
      value: (signals.vol_ratio_pct ?? 0).toFixed(0) + '%',
      sub: (signals.vol_ratio_pct ?? 0) > 130 ? 'Élevée' : (signals.vol_ratio_pct ?? 0) < 70 ? 'Comprimée' : 'Normale',
      color: volColor,
    },
  ]

  const macroCol = macroRegime ? macroRegimeColor(macroRegime.dominant) : 'slate'
  const top3Macro = macroRegime?.ranked?.slice(0, 3) ?? []

  return (
    <div className={clsx('rounded-xl border p-4 space-y-3', borderMap[col])}>
      <div className="flex items-center justify-between">
        <div className="flex items-center gap-2">
          <BarChart2 className={clsx('w-4 h-4', `text-${col}-400`)} />
          <span className="text-xs font-semibold text-slate-400 uppercase tracking-wide">Régime</span>
        </div>
        <span className="text-xs text-slate-600">{dateLabel}</span>
      </div>

      {macroRegime && macroRegime.dominant !== 'incertain' && (
        <div className="rounded-lg border border-slate-700/30 bg-dark-700/40 px-3 py-2 space-y-1.5">
          <div className="flex items-center justify-between">
            <span className="text-xs text-slate-500 uppercase tracking-wide">Cycle macro global</span>
            <span className="text-xs text-slate-600">Fed cycle</span>
          </div>
          <div className="flex items-center gap-2">
            <span className="text-base">{macroRegime.emoji || '🌍'}</span>
            <span className={clsx('text-sm font-bold', `text-${macroCol}-300`)}>{macroRegime.label || macroRegime.dominant}</span>
          </div>
          {top3Macro.length > 0 && (
            <div className="flex flex-wrap gap-1 pt-0.5">
              {top3Macro.map((key, i) => {
                const score = macroRegime.scores?.[key]
                const c = macroRegimeColor(key)
                return (
                  <span key={key} className={clsx(
                    'text-xs px-1.5 py-0.5 rounded border',
                    i === 0 ? `text-${c}-300 bg-${c}-900/30 border-${c}-700/40` : 'text-slate-500 bg-dark-800/40 border-slate-700/30'
                  )}>
                    {key} {score !== undefined ? Math.round(score) + '%' : ''}
                  </span>
                )
              })}
            </div>
          )}
        </div>
      )}

      <div className={clsx('rounded-lg px-3 py-2', `bg-${col}-900/20`)}>
        <div className="text-xs text-slate-500 mb-0.5">Régime technique</div>
        <div className={clsx('text-sm font-bold leading-tight', `text-${col}-300`)}>{regime.current}</div>
        <div className="flex items-center gap-2 mt-1">
          <div className="flex-1 h-1.5 bg-slate-800 rounded-full overflow-hidden">
            <div className={clsx('h-full rounded-full', `bg-${col}-500`)}
              style={{ width: `${Math.round(regime.confidence * 100)}%` }} />
          </div>
          <span className="text-xs text-slate-500">{Math.round(regime.confidence * 100)}%</span>
        </div>
      </div>

      <div className="grid grid-cols-2 gap-2">
        {metrics.map(m => (
          <div key={m.label} className="rounded-lg bg-dark-700/50 px-2.5 py-2">
            <div className="text-xs text-slate-600 mb-0.5 truncate">{m.label}</div>
            <div className={clsx('text-sm font-bold', m.color)}>{m.value}</div>
            {m.sub && <div className="text-xs text-slate-600 mt-0.5">{m.sub}</div>}
          </div>
        ))}
      </div>
    </div>
  )
}

// ── TrendCard ─────────────────────────────────────────────────────────────────

function TrendCard({ trend, dateLabel }: { trend: TrendMetrics; dateLabel: string }) {
  const rsi = trend.rsi14_current ?? 50
  const rsiColor = rsi > 70 ? 'text-orange-400' : rsi < 30 ? 'text-cyan-400' : 'text-slate-300'
  const rsiZone  = rsi > 70 ? 'Suracheté' : rsi < 30 ? 'Survendu' : 'Neutre'

  const items = [
    { group: 'Tendance', rows: [
      { label: 'Slope MA50 (5j)',   value: fmt(trend.ma50_slope_5d) + '%',   arrow: trend.ma50_slope_5d   },
      { label: 'Slope MA200 (20j)', value: fmt(trend.ma200_slope_20d) + '%', arrow: trend.ma200_slope_20d },
      { label: 'Distance MA50',     value: trend.dist_ma50_pct  != null ? fmt(trend.dist_ma50_pct)  + '%' : '—', arrow: trend.dist_ma50_pct  ?? 0 },
      { label: 'Distance MA200',    value: trend.dist_ma200_pct != null ? fmt(trend.dist_ma200_pct) + '%' : '—', arrow: trend.dist_ma200_pct ?? 0, bold: true },
    ]},
    { group: 'Momentum', rows: [
      { label: 'Momentum 1M', value: fmt(trend.momentum_1m_pct) + '%', arrow: trend.momentum_1m_pct },
      { label: 'Momentum 3M', value: fmt(trend.momentum_3m_pct) + '%', arrow: trend.momentum_3m_pct, bold: true },
    ]},
  ]

  const pct52w = trend.high_52w && trend.low_52w && (trend.high_52w - trend.low_52w) > 0
    ? ((trend.current_price - trend.low_52w) / (trend.high_52w - trend.low_52w)) * 100 : null

  return (
    <div className="rounded-xl border border-slate-700/40 bg-dark-800/60 p-4 space-y-3">
      <div className="flex items-center justify-between">
        <div className="flex items-center gap-2">
          <TrendingUp className="w-4 h-4 text-blue-400" />
          <span className="text-xs font-semibold text-slate-400 uppercase tracking-wide">Indicateurs de Tendance</span>
        </div>
        <span className="text-xs text-slate-600">{dateLabel}</span>
      </div>

      <div className="flex items-center justify-between bg-dark-700/40 rounded-lg px-3 py-1.5">
        <span className="text-xs text-slate-500">Prix</span>
        <span className="text-sm font-bold text-white">
          {trend.current_price?.toLocaleString('fr-FR', { maximumFractionDigits: 4 })}
        </span>
      </div>

      {items.map(group => (
        <div key={group.group}>
          <div className="text-xs text-slate-600 uppercase tracking-wide mb-1.5">{group.group}</div>
          {group.rows.map(row => (
            <div key={row.label} className="flex items-center justify-between text-xs py-0.5">
              <span className="text-slate-500">{row.label}</span>
              <span className={clsx('flex items-center gap-1', (row as any).bold ? 'font-semibold' : '', pctColor(row.arrow ?? 0))}>
                <Arrow v={row.arrow ?? 0} />{row.value}
              </span>
            </div>
          ))}
        </div>
      ))}

      <div className="border-t border-slate-700/30 pt-3">
        <div className="flex items-center justify-between text-xs mb-1.5">
          <span className="text-slate-500">RSI(14)</span>
          <span className={clsx('font-semibold', rsiColor)}>{rsi.toFixed(0)} — {rsiZone}</span>
        </div>
        <div className="relative h-2 bg-slate-800 rounded-full overflow-hidden">
          <div className="absolute left-0 top-0 h-full w-[30%] bg-cyan-900/50" />
          <div className="absolute right-0 top-0 h-full w-[30%] bg-orange-900/50" />
          <div className="absolute top-0 h-full w-2 bg-white rounded-full transition-all"
            style={{ left: `calc(${Math.min(98, rsi)}% - 4px)` }} />
        </div>
        <div className="flex justify-between text-xs text-slate-600 mt-0.5">
          <span>0 Survendu</span><span>50</span><span>Suracheté 100</span>
        </div>
      </div>

      {pct52w !== null && (
        <div>
          <div className="flex items-center justify-between text-xs mb-1">
            <span className="text-slate-500">Range 52 semaines</span>
            <span className="text-slate-400">{Math.round(pct52w)}e percentile</span>
          </div>
          <div className="relative h-2 bg-slate-800 rounded-full overflow-hidden">
            <div className="absolute top-0 h-full bg-blue-500/60 rounded-full" style={{ width: `${pct52w}%` }} />
          </div>
          <div className="flex justify-between text-xs text-slate-600 mt-0.5">
            <span>{trend.low_52w?.toFixed(2)}</span><span>{trend.high_52w?.toFixed(2)}</span>
          </div>
        </div>
      )}

      <div className="flex items-center justify-between text-xs">
        <span className="text-slate-500">ATR(14) vs moy. 3M</span>
        <span className={clsx((trend.atr_vs_3m_avg_pct ?? 100) > 130 ? 'text-orange-400' : (trend.atr_vs_3m_avg_pct ?? 100) < 70 ? 'text-cyan-400' : 'text-slate-400')}>
          {(trend.atr_vs_3m_avg_pct ?? 100).toFixed(0)}%
        </span>
      </div>
    </div>
  )
}

// ── EventsCard ────────────────────────────────────────────────────────────────

function EventsCard({ events, selectedDate }: { events: SnapshotEvent[]; selectedDate: string | null }) {
  const navigate = useNavigate()
  const LEVEL_C: Record<string, string> = {
    long:   'text-violet-400 bg-violet-900/30 border-violet-700/30',
    medium: 'text-blue-400 bg-blue-900/30 border-blue-700/30',
    short:  'text-emerald-400 bg-emerald-900/30 border-emerald-700/30',
  }

  function isActiveAt(ev: SnapshotEvent): boolean {
    if (!selectedDate) return false
    const evStart = ev.date
    const evEnd   = ev.end_date
    if (evStart > selectedDate) return false
    if (evEnd) return evEnd >= selectedDate
    // point event: active if within 30 days after
    const diffDays = (new Date(selectedDate).getTime() - new Date(evStart).getTime()) / 86400000
    return diffDays <= 30
  }

  function surpriseColor(v: number | null | undefined): string {
    if (v === null || v === undefined) return 'text-slate-400'
    if (v > 5) return 'text-emerald-400'
    if (v < -5) return 'text-red-400'
    return 'text-slate-400'
  }

  return (
    <div className="rounded-xl border border-slate-700/40 bg-dark-800/60 p-4 space-y-3">
      <div className="flex items-center gap-2">
        <Calendar className="w-4 h-4 text-amber-400" />
        <span className="text-xs font-semibold text-slate-400 uppercase tracking-wide">Événements Macro</span>
      </div>
      {events.length === 0 ? (
        <div className="text-xs text-slate-600 italic py-2">Aucun événement lié trouvé</div>
      ) : (
        <div className="space-y-2 max-h-[380px] overflow-y-auto pr-1">
          {events.map((ev, i) => {
            const active = isActiveAt(ev)
            return (
              <div key={i}
                className={clsx(
                  'rounded-lg border p-2 cursor-pointer transition-colors',
                  active
                    ? 'border-amber-600/50 bg-amber-900/20 hover:bg-amber-900/30'
                    : 'border-slate-700/30 bg-dark-700/40 hover:bg-dark-700/70'
                )}
                onClick={() => navigate(`/timeline?date=${ev.date}`)}
              >
                <div className="flex items-start justify-between gap-2">
                  <div className="flex items-center gap-1.5 min-w-0">
                    {active && <span className="shrink-0 w-1.5 h-1.5 rounded-full bg-amber-400 mt-0.5" />}
                    <span className="text-xs font-medium text-slate-300 leading-tight truncate">{ev.title}</span>
                  </div>
                  <div className="flex items-center gap-1 shrink-0">
                    {ev.sub_type && (
                      <span className="text-xs px-1 py-0 rounded bg-slate-700/50 text-slate-400 border border-slate-600/30"
                        style={{ fontSize: 8, paddingTop: 1, paddingBottom: 1 }}>
                        {ev.sub_type}
                      </span>
                    )}
                    <span className={clsx('text-xs px-1.5 py-0.5 rounded border', LEVEL_C[ev.level] ?? 'text-slate-400 bg-slate-800 border-slate-700/30')}>
                      {ev.level === 'long' ? 'LT' : ev.level === 'medium' ? 'MT' : 'CT'}
                    </span>
                  </div>
                </div>

                {/* Eco calendar fields */}
                {(ev.expected_value || ev.actual_value) && (
                  <div className="flex items-center gap-3 mt-1.5 text-xs">
                    {ev.expected_value && (
                      <span className="text-slate-500">Att. <span className="text-slate-300">{ev.expected_value}</span></span>
                    )}
                    {ev.actual_value && (
                      <span className="text-slate-500">Réel <span className="font-semibold text-white">{ev.actual_value}</span></span>
                    )}
                    {ev.surprise_pct !== null && ev.surprise_pct !== undefined && (
                      <span className={clsx('font-semibold', surpriseColor(ev.surprise_pct))}>
                        {ev.surprise_pct > 0 ? '+' : ''}{ev.surprise_pct.toFixed(0)}%
                        {ev.surprise_pct > 5 ? ' ↑' : ev.surprise_pct < -5 ? ' ↓' : ''}
                      </span>
                    )}
                    {ev.unit && <span className="text-slate-600">{ev.unit}</span>}
                  </div>
                )}

                {ev.absorption_pct !== null && ev.absorption_pct !== undefined && (
                  <div className="mt-1.5">
                    <div className="flex items-center justify-between text-xs text-slate-600 mb-0.5">
                      <span>Absorption marché</span><span>{ev.absorption_pct}%</span>
                    </div>
                    <div className="h-1 bg-slate-800 rounded-full overflow-hidden">
                      <div className="h-full bg-amber-600/60 rounded-full" style={{ width: `${ev.absorption_pct}%` }} />
                    </div>
                  </div>
                )}

                <div className="flex items-center gap-2 mt-1 text-xs text-slate-500">
                  <Clock className="w-3 h-3" />
                  {fmtDateFR(ev.date)}{ev.end_date ? ` → ${fmtDateFR(ev.end_date)}` : ''}
                  {ev.impact_score > 0.7 && <AlertCircle className="w-3 h-3 text-orange-400" />}
                </div>
                {ev.description && <p className="text-xs text-slate-600 mt-1 line-clamp-1">{ev.description}</p>}
              </div>
            )
          })}
        </div>
      )}
    </div>
  )
}

// ── NarrativeCard ─────────────────────────────────────────────────────────────

function NarrativeCard({ narrative, loading, onLoad, instrument }: {
  narrative: string; loading: boolean; onLoad: () => void; instrument: InstrumentConfig
}) {
  return (
    <div className="rounded-xl border border-slate-700/40 bg-dark-800/60 p-4">
      <div className="flex items-center justify-between mb-3">
        <div className="flex items-center gap-2">
          <Sparkles className="w-4 h-4 text-violet-400" />
          <span className="text-xs font-semibold text-slate-400 uppercase tracking-wide">Narration IA — {instrument.name}</span>
        </div>
        <button onClick={onLoad} disabled={loading}
          className="flex items-center gap-1.5 text-xs px-3 py-1.5 bg-violet-800/30 hover:bg-violet-800/50 text-violet-300 border border-violet-700/40 rounded-lg transition-colors disabled:opacity-40"
        >
          {loading ? <RefreshCw className="w-3 h-3 animate-spin" /> : <Sparkles className="w-3 h-3" />}
          {loading ? 'Génération...' : narrative ? 'Actualiser' : 'Générer'}
        </button>
      </div>
      {narrative ? (
        <p className="text-sm text-slate-300 leading-relaxed">{narrative}</p>
      ) : loading ? (
        <div className="space-y-2">
          {[95, 88, 70].map(w => <div key={w} className="h-3 bg-slate-800 rounded animate-pulse" style={{ width: `${w}%` }} />)}
        </div>
      ) : (
        <div className="text-sm text-slate-600 italic">
          Cliquez "Générer" pour une analyse IA pour {instrument.name}.
        </div>
      )}
    </div>
  )
}

// ── DriversPanel — édition inline ────────────────────────────────────────────

function DriversPanel({ instrumentId, drivers, onSave, onClose }: {
  instrumentId: string
  drivers: Driver[]
  onSave: (drivers: Driver[]) => void
  onClose: () => void
}) {
  const [local, setLocal] = useState<Driver[]>(() => drivers.map(d => ({ ...d, keywords: [...(d.keywords ?? [])] })))
  const [saving, setSaving] = useState(false)
  const [error, setError] = useState('')

  function updateDriver(i: number, field: keyof Driver, val: any) {
    setLocal(prev => prev.map((d, idx) => idx === i ? { ...d, [field]: val } : d))
  }

  function updateKeywords(i: number, raw: string) {
    const kws = raw.split(',').map(s => s.trim()).filter(Boolean)
    updateDriver(i, 'keywords', kws)
  }

  function addDriver() {
    setLocal(prev => [...prev, {
      key: `driver_${Date.now()}`, label: 'Nouveau driver',
      weight: 0.5, keywords: [], type: 'fundamental',
    }])
  }

  function removeDriver(i: number) {
    setLocal(prev => prev.filter((_, idx) => idx !== i))
  }

  async function save() {
    setSaving(true); setError('')
    try {
      await api.put(`/instruments/${instrumentId}/drivers`, { drivers: local })
      onSave(local)
      onClose()
    } catch (e: any) {
      setError(e?.response?.data?.detail ?? 'Erreur de sauvegarde')
    } finally {
      setSaving(false)
    }
  }

  return (
    <div className="rounded-xl border border-blue-700/40 bg-dark-800/80 p-4 space-y-4">
      <div className="flex items-center justify-between">
        <span className="text-sm font-semibold text-white">Éditer les drivers — {instrumentId}</span>
        <div className="flex items-center gap-2">
          <button onClick={addDriver}
            className="flex items-center gap-1 text-xs px-2.5 py-1 bg-blue-800/30 hover:bg-blue-800/50 text-blue-300 border border-blue-700/40 rounded-lg transition-colors">
            <Plus className="w-3 h-3" /> Ajouter
          </button>
          <button onClick={save} disabled={saving}
            className="flex items-center gap-1.5 text-xs px-3 py-1 bg-emerald-800/30 hover:bg-emerald-800/50 text-emerald-300 border border-emerald-700/40 rounded-lg transition-colors disabled:opacity-40">
            {saving ? <RefreshCw className="w-3 h-3 animate-spin" /> : <Save className="w-3 h-3" />}
            {saving ? 'Sauvegarde...' : 'Sauvegarder'}
          </button>
          <button onClick={onClose} className="p-1 text-slate-500 hover:text-white transition-colors">
            <X className="w-4 h-4" />
          </button>
        </div>
      </div>

      {error && <div className="text-xs text-red-400 bg-red-900/20 border border-red-700/30 rounded-lg px-3 py-1.5">{error}</div>}

      <div className="space-y-2 max-h-[420px] overflow-y-auto pr-1">
        {local.map((d, i) => (
          <div key={i} className="rounded-lg border border-slate-700/30 bg-dark-700/40 p-3 space-y-2">
            <div className="flex items-center gap-2">
              <input
                className="flex-1 text-xs bg-dark-600 border border-slate-700/40 rounded px-2 py-1 text-white placeholder-slate-600"
                placeholder="Label"
                value={d.label}
                onChange={e => updateDriver(i, 'label', e.target.value)}
              />
              {/* Type selector */}
              <select
                className="text-xs bg-dark-600 border border-slate-700/40 rounded px-1.5 py-1 text-slate-300"
                value={d.type ?? 'fundamental'}
                onChange={e => updateDriver(i, 'type', e.target.value)}
              >
                {DRIVER_TYPES.map(t => (
                  <option key={t} value={t}>{DRIVER_TYPE_CFG[t]?.short ?? t}</option>
                ))}
              </select>
              <div className="flex items-center gap-1.5">
                <span className="text-xs text-slate-500">Poids</span>
                <input
                  type="number" min="0" max="1" step="0.05"
                  className="w-16 text-xs bg-dark-600 border border-slate-700/40 rounded px-2 py-1 text-white"
                  value={d.weight}
                  onChange={e => updateDriver(i, 'weight', parseFloat(e.target.value))}
                />
              </div>
              <button onClick={() => removeDriver(i)} className="p-1 text-slate-600 hover:text-red-400 transition-colors">
                <Trash2 className="w-3.5 h-3.5" />
              </button>
            </div>
            <div>
              <label className="text-xs text-slate-600 mb-0.5 block">Mots-clés (séparés par virgule)</label>
              <input
                className="w-full text-xs bg-dark-600 border border-slate-700/40 rounded px-2 py-1 text-slate-300 placeholder-slate-600"
                placeholder="Fed, FOMC, pivot, QE, taux directeur..."
                value={(d.keywords ?? []).join(', ')}
                onChange={e => updateKeywords(i, e.target.value)}
              />
            </div>
          </div>
        ))}
      </div>
    </div>
  )
}

// ── Macro gauge helpers ───────────────────────────────────────────────────────

const SCENARIO_META: Record<string, { label: string; emoji: string; color: string }> = {
  goldilocks:       { label: 'Goldilocks',             emoji: '🟢', color: '#10b981' },
  desinflation:     { label: 'Désinflation',           emoji: '🔵', color: '#3b82f6' },
  soft_landing:     { label: 'Soft Landing',           emoji: '🔷', color: '#06b6d4' },
  reflation:        { label: 'Reflation',              emoji: '🟠', color: '#f97316' },
  stagflation:      { label: 'Stagflation',            emoji: '🟡', color: '#f59e0b' },
  inflation_shock:  { label: 'Choc Inflationniste',    emoji: '🔥', color: '#dc2626' },
  recession:        { label: 'Récession',              emoji: '🔴', color: '#ef4444' },
  crise_liquidite:  { label: 'Crise de liquidité',     emoji: '🟣', color: '#7c3aed' },
  incertain:        { label: 'Incertain',              emoji: '⬜', color: '#64748b' },
}

function snapToMacroRegime(snap: MacroGaugeSnap): MacroRegime {
  const scores = snap.regime_scores ?? {}
  const meta   = SCENARIO_META[snap.dominant] ?? SCENARIO_META.incertain
  const ranked = Object.entries(scores)
    .sort(([, a], [, b]) => b - a)
    .map(([k]) => k)
  return {
    dominant:   snap.dominant,
    label:      meta.label,
    color:      meta.color,
    emoji:      meta.emoji,
    scores,
    ranked,
    asset_bias: {},
  }
}

const BLOC_LABELS: Record<string, string> = {
  liquidite:  'Liquidité / Taux',
  credit:     'Crédit / Vol',
  energie:    'Énergie',
  metaux:     'Métaux',
  croissance: 'Croissance US',
  secteurs:   'Secteurs',
  volatilite: 'Volatilité surf.',
  global:     'Global / EM',
  forex_ro:   'Forex Risk-Off',
  derive:     'Dérivés',
}

function MacroGaugePanel({ snap, dateLabel }: { snap: MacroGaugeSnap; dateLabel: string }) {
  const byBloc: Record<string, GaugeValue[]> = {}
  for (const g of Object.values(snap.gauges)) {
    if (g.value === null && g.change_pct === null) continue
    const b = g.bloc ?? 'derive'
    if (!byBloc[b]) byBloc[b] = []
    byBloc[b].push(g)
  }
  const meta = SCENARIO_META[snap.dominant] ?? SCENARIO_META.incertain

  return (
    <div className="bg-dark-800/60 rounded-xl border border-slate-700/30 p-4 space-y-3">
      <div className="flex items-center justify-between">
        <div className="flex items-center gap-2">
          <span className="text-base">{meta.emoji}</span>
          <span className="text-sm font-semibold text-white">{meta.label}</span>
          <span className="text-xs text-slate-500">— contexte macro au {dateLabel}</span>
        </div>
        <span className="text-xs text-slate-600">{snap.snapshot_date}</span>
      </div>

      {/* Regime scores mini bar */}
      <div className="flex gap-1 h-1.5">
        {Object.entries(snap.regime_scores)
          .sort(([,a],[,b]) => b - a)
          .slice(0, 6)
          .map(([k, v]) => (
            <div key={k} title={`${SCENARIO_META[k]?.label ?? k}: ${(v*100).toFixed(0)}%`}
              style={{ width: `${v*100}%`, background: SCENARIO_META[k]?.color ?? '#64748b' }}
              className="rounded-full transition-all"
            />
          ))}
      </div>

      {/* Gauges by bloc */}
      <div className="grid grid-cols-2 gap-x-6 gap-y-3">
        {Object.entries(byBloc).map(([bloc, gauges]) => (
          <div key={bloc}>
            <div className="text-xs text-slate-600 uppercase tracking-wide mb-1">{BLOC_LABELS[bloc] ?? bloc}</div>
            <div className="space-y-0.5">
              {gauges.map(g => (
                <div key={g.id} className="flex items-center justify-between text-xs">
                  <span className="text-slate-400 truncate max-w-[120px]" title={g.label}>{g.label}</span>
                  <div className="flex items-center gap-2">
                    {g.value !== null && (
                      <span className="text-white font-mono">
                        {g.unit === '%' ? g.value.toFixed(2) + '%'
                          : g.unit === 'pts' ? g.value.toFixed(1)
                          : g.unit === 'ratio' ? g.value.toFixed(3)
                          : g.value.toFixed(2)}
                      </span>
                    )}
                    {g.change_pct !== null && g.change_pct !== undefined && (
                      <span className={clsx('font-mono', g.change_pct >= 0 ? 'text-emerald-400' : 'text-red-400')}>
                        {g.change_pct >= 0 ? '+' : ''}{g.change_pct.toFixed(1)}%
                      </span>
                    )}
                  </div>
                </div>
              ))}
            </div>
          </div>
        ))}
      </div>
    </div>
  )
}

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

const PERIODS = [
  { key: '3mo', label: '3M' }, { key: '6mo', label: '6M' },
  { key: '1y', label: '1Y'  }, { key: '2y', label: '2Y' }, { key: '5y', label: '5Y' },
]

export default function InstrumentDashboard() {
  const { id = 'SPY' } = useParams<{ id: string }>()
  const navigate = useNavigate()
  const [period, setPeriod]           = useState('1y')
  const [instruments, setInstruments] = useState<InstrumentConfig[]>([])
  const [snapshot, setSnapshot]       = useState<Snapshot | null>(null)
  const [narrative, setNarrative]     = useState('')
  const [loading, setLoading]         = useState(false)
  const [loadingNarr, setLoadingNarr] = useState(false)
  const [selectorOpen, setSelectorOpen] = useState(false)
  const [selectedDate, setSelectedDate] = useState<string | null>(null)
  const [editDrivers, setEditDrivers] = useState(false)
  const [localDrivers, setLocalDrivers] = useState<Driver[] | null>(null)
  const [macroAtDate, setMacroAtDate] = useState<MacroGaugeSnap | null>(null)

  const instrumentId = id.toUpperCase()

  useEffect(() => {
    api.get('/instruments').then(r => setInstruments(r.data)).catch(() => {})
  }, [])

  useEffect(() => {
    setLoading(true)
    setSnapshot(null)
    setNarrative('')
    setSelectedDate(null)
    setLocalDrivers(null)
    setEditDrivers(false)
    setMacroAtDate(null)
    api.get(`/instruments/${instrumentId}/snapshot?period=${period}`)
      .then(r => {
        setSnapshot(r.data)
        const pd: PriceCandle[] = r.data.price_data
        if (pd?.length) setSelectedDate(pd[pd.length - 1].time)
      })
      .catch(() => {})
      .finally(() => setLoading(false))
  }, [instrumentId, period])

  const loadNarrative = useCallback(() => {
    setLoadingNarr(true)
    api.post(`/instruments/${instrumentId}/narrative`)
      .then(r => setNarrative(r.data.narrative))
      .catch(() => {})
      .finally(() => setLoadingNarr(false))
  }, [instrumentId])

  const handleDateHover = useCallback((date: string | null) => {
    if (date) setSelectedDate(date)
  }, [])

  const { priceMap, indMap, sortedDates, dateIndex } = useMemo(() => {
    if (!snapshot) return { priceMap: {} as Record<string, PriceCandle>, indMap: {} as Record<string, Record<string, number>>, sortedDates: [] as string[], dateIndex: {} as Record<string, number> }
    const priceMap:   Record<string, PriceCandle>            = {}
    const indMap:     Record<string, Record<string, number>> = {}
    const sortedDates: string[] = []
    const dateIndex:  Record<string, number>                  = {}
    for (const c of snapshot.price_data) { priceMap[c.time] = c; sortedDates.push(c.time) }
    sortedDates.forEach((d, i) => { dateIndex[d] = i })
    for (const [key, pts] of Object.entries(snapshot.indicators)) {
      for (const pt of pts) { if (!indMap[pt.time]) indMap[pt.time] = {}; indMap[pt.time][key] = pt.value }
    }
    return { priceMap, indMap, sortedDates, dateIndex }
  }, [snapshot])

  // Fetch macro gauge context when crosshair date changes (after sortedDates is available)
  useEffect(() => {
    if (!selectedDate || !sortedDates.length) return
    const isLast = selectedDate === sortedDates[sortedDates.length - 1]
    if (isLast) { setMacroAtDate(null); return }
    api.get(`/market/macro-gauges/at?date=${selectedDate}`)
      .then(r => { if (r.data?.snapshot_date) setMacroAtDate(r.data) })
      .catch(() => {})
  }, [selectedDate, sortedDates])

  const effectiveDate = useMemo(() => {
    if (selectedDate && dateIndex[selectedDate] !== undefined) return selectedDate
    return sortedDates[sortedDates.length - 1] ?? null
  }, [selectedDate, sortedDates, dateIndex])

  const dateTrend = useMemo((): TrendMetrics | null => {
    if (!effectiveDate || !snapshot) return null
    const candle = priceMap[effectiveDate]; if (!candle) return null
    const idx = dateIndex[effectiveDate], price = candle.close, ind = indMap[effectiveDate] ?? {}
    const ma50 = ind.ma50, ma200 = ind.ma200, atr14 = ind.atr14 ?? 0
    const d5  = idx >= 5  ? sortedDates[idx - 5]  : null
    const d20 = idx >= 20 ? sortedDates[idx - 20] : null
    const d21 = idx >= 21 ? sortedDates[idx - 21] : null
    const d63 = idx >= 63 ? sortedDates[idx - 63] : null
    let atrSum = 0, atrCnt = 0
    for (let i = Math.max(0, idx - 65); i <= idx; i++) { const v = indMap[sortedDates[i]]?.atr14; if (v) { atrSum += v; atrCnt++ } }
    let high52 = candle.high, low52 = candle.low
    for (let i = Math.max(0, idx - 252); i <= idx; i++) { const c = priceMap[sortedDates[i]]; if (c) { if (c.high > high52) high52 = c.high; if (c.low < low52) low52 = c.low } }
    return {
      ma50_slope_5d:    pctN(ma50, d5  ? indMap[d5]?.ma50  : undefined),
      ma200_slope_20d:  pctN(ma200, d20 ? indMap[d20]?.ma200 : undefined),
      rsi14_current:    ind.rsi14 ?? 50,
      atr14_current:    atr14,
      atr_vs_3m_avg_pct: atrCnt > 0 && atr14 ? (atr14 / (atrSum / atrCnt)) * 100 : 100,
      momentum_1m_pct:  d21 && priceMap[d21] ? pctN(price, priceMap[d21].close) : 0,
      momentum_3m_pct:  d63 && priceMap[d63] ? pctN(price, priceMap[d63].close) : 0,
      dist_ma50_pct:    ma50  ? pctN(price, ma50)  : null,
      dist_ma200_pct:   ma200 ? pctN(price, ma200) : null,
      current_price: price, high_52w: high52, low_52w: low52,
    }
  }, [effectiveDate, priceMap, indMap, sortedDates, dateIndex, snapshot])

  const dateSignals = useMemo((): RegimeSignals | null => {
    if (!effectiveDate || !snapshot) return null
    const candle = priceMap[effectiveDate]; if (!candle) return null
    const idx = dateIndex[effectiveDate], price = candle.close, ind = indMap[effectiveDate] ?? {}
    const ma50 = ind.ma50, ma200 = ind.ma200, atr14 = ind.atr14
    const d10 = idx >= 10 ? sortedDates[idx - 10] : null
    const d20 = idx >= 20 ? sortedDates[idx - 20] : null
    let atrSum = 0, atrCnt = 0
    for (let i = Math.max(0, idx - 65); i <= idx; i++) { const v = indMap[sortedDates[i]]?.atr14; if (v) { atrSum += v; atrCnt++ } }
    return {
      ma50_above_ma200: ma50 !== undefined && ma200 !== undefined ? ma50 > ma200 : null,
      ma50_slope_pct:   pctN(ma50,  d10 ? indMap[d10]?.ma50  : undefined),
      ma200_slope_pct:  pctN(ma200, d10 ? indMap[d10]?.ma200 : undefined),
      momentum_20d_pct: d20 && priceMap[d20] ? pctN(price, priceMap[d20].close) : 0,
      dist_ma200_pct:   ma200 ? pctN(price, ma200) : 0,
      vol_ratio_pct:    atrCnt > 0 && atr14 ? (atr14 / (atrSum / atrCnt)) * 100 : 0,
    }
  }, [effectiveDate, priceMap, indMap, sortedDates, dateIndex, snapshot])

  const grouped = CATEGORY_ORDER.map(cat => ({
    cat, label: CATEGORY_LABELS[cat] ?? cat,
    items: instruments.filter(i => i.category === cat),
  })).filter(g => g.items.length > 0)

  const selected     = instruments.find(i => i.id === instrumentId)
  const isLastDate   = effectiveDate === sortedDates[sortedDates.length - 1]
  const dateLabel    = effectiveDate ? fmtDateFR(effectiveDate) : '—'
  const displayPrice = dateTrend?.current_price ?? snapshot?.current_price

  const activeDrivers = localDrivers ?? snapshot?.instrument?.drivers ?? []

  return (
    <div className="p-6 max-w-screen-xl mx-auto space-y-4">

      {/* ── Header ── */}
      <div className="flex flex-wrap items-center gap-3">
        <div className="relative">
          <button onClick={() => setSelectorOpen(s => !s)}
            className="flex items-center gap-2 px-4 py-2 bg-dark-800 border border-slate-700/40 rounded-xl text-white hover:bg-dark-700 transition-colors"
          >
            <BarChart2 className="w-4 h-4 text-blue-400" />
            <span className="font-semibold">{selected?.name ?? instrumentId}</span>
            <span className="text-xs text-slate-500">{selected?.id}</span>
            <ChevronDown className="w-3.5 h-3.5 text-slate-500" />
          </button>
          {selectorOpen && (
            <div className="absolute top-full left-0 mt-1 z-50 bg-dark-800 border border-slate-700/40 rounded-xl shadow-2xl p-3 w-[480px] max-h-[60vh] overflow-y-auto">
              {grouped.map(g => (
                <div key={g.cat} className="mb-3">
                  <div className="text-xs text-slate-500 uppercase tracking-wide mb-1.5 px-1">{g.label}</div>
                  <div className="grid grid-cols-3 gap-1">
                    {g.items.map(inst => (
                      <button key={inst.id}
                        onClick={() => { navigate(`/instruments/${inst.id}`); setSelectorOpen(false) }}
                        className={clsx('text-left px-2.5 py-1.5 rounded-lg text-xs transition-colors',
                          inst.id === instrumentId
                            ? 'bg-blue-800/40 text-blue-300 border border-blue-700/40'
                            : 'text-slate-400 hover:bg-dark-700 hover:text-white'
                        )}
                      >
                        <div className="font-semibold">{inst.id}</div>
                        <div className="text-slate-500 truncate">{inst.name}</div>
                      </button>
                    ))}
                  </div>
                </div>
              ))}
            </div>
          )}
        </div>

        {selected && (
          <span className="text-xs px-2 py-1 bg-dark-700 border border-slate-700/30 rounded-lg text-slate-400">
            {CATEGORY_LABELS[selected.category] ?? selected.category}
          </span>
        )}

        {displayPrice !== undefined && (
          <div className="flex items-center gap-3">
            <span className="text-xl font-bold text-white">
              {displayPrice.toLocaleString('fr-FR', { maximumFractionDigits: 4 })}
            </span>
            {isLastDate && snapshot && (
              <span className={clsx('text-sm font-semibold', (snapshot.change_pct ?? 0) >= 0 ? 'text-emerald-400' : 'text-red-400')}>
                {(snapshot.change_pct ?? 0) >= 0 ? '+' : ''}{snapshot.change_abs?.toFixed(2)} ({(snapshot.change_pct ?? 0) >= 0 ? '+' : ''}{snapshot.change_pct?.toFixed(2)}%)
              </span>
            )}
          </div>
        )}

        <div className="ml-auto flex items-center gap-2">
          {snapshot && (
            <button onClick={() => setEditDrivers(e => !e)}
              className={clsx('flex items-center gap-1.5 text-xs px-3 py-1.5 border rounded-lg transition-colors',
                editDrivers
                  ? 'bg-blue-800/40 text-blue-300 border-blue-700/40'
                  : 'bg-dark-800 text-slate-400 border-slate-700/40 hover:text-white hover:bg-dark-700'
              )}
            >
              <Pencil className="w-3 h-3" />
              Drivers
            </button>
          )}
          <div className="flex items-center gap-1 bg-dark-800 border border-slate-700/40 rounded-xl p-1">
            {PERIODS.map(p => (
              <button key={p.key} onClick={() => setPeriod(p.key)}
                className={clsx('px-3 py-1 text-xs rounded-lg transition-colors',
                  period === p.key ? 'bg-blue-700/50 text-blue-300' : 'text-slate-400 hover:text-white hover:bg-dark-700'
                )}
              >
                {p.label}
              </button>
            ))}
          </div>
        </div>

        {selected && <p className="w-full text-xs text-slate-500">{selected.description}</p>}
      </div>

      {/* ── Loading ── */}
      {loading && (
        <div className="space-y-4">
          <div className="bg-dark-800/60 rounded-xl border border-slate-700/40 animate-pulse" style={{ height: 420 }} />
          <div className="bg-dark-800/60 rounded-xl border border-slate-700/40 animate-pulse h-20" />
          <div className="grid grid-cols-3 gap-4">
            {[1, 2, 3].map(i => <div key={i} className="bg-dark-800/60 rounded-xl border border-slate-700/40 animate-pulse h-72" />)}
          </div>
        </div>
      )}

      {/* ── Content ── */}
      {!loading && snapshot && (
        <>
          <InstrumentChart
            priceData={snapshot.price_data}
            indicators={snapshot.indicators}
            events={snapshot.events}
            height={420}
            onDateHover={handleDateHover}
          />

          {/* Date badge */}
          <div className="flex items-center gap-2">
            <div className={clsx(
              'flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-medium border',
              isLastDate
                ? 'bg-slate-800 border-slate-700/40 text-slate-400'
                : 'bg-blue-900/40 border-blue-700/50 text-blue-300'
            )}>
              <Calendar className="w-3 h-3" />
              Snapshot au {dateLabel}
              {!isLastDate && <span className="text-blue-500 ml-1">← survol du graphe</span>}
            </div>
          </div>

          {/* 3-column grid */}
          <div className="grid grid-cols-3 gap-4">
            <RegimeCard
              regime={snapshot.regime}
              macroRegime={macroAtDate ? snapToMacroRegime(macroAtDate) : (snapshot.macro_regime ?? null)}
              signalsAt={dateSignals}
              dateLabel={dateLabel}
            />
            <TrendCard
              trend={dateTrend ?? snapshot.trend}
              dateLabel={dateLabel}
            />
            <EventsCard
              events={snapshot.events}
              selectedDate={effectiveDate}
            />
          </div>

          {/* Macro gauge detail panel — shown when on a historical date */}
          {macroAtDate && (
            <MacroGaugePanel snap={macroAtDate} dateLabel={dateLabel} />
          )}

          {editDrivers && (
            <DriversPanel
              instrumentId={instrumentId}
              drivers={activeDrivers}
              onSave={saved => { setLocalDrivers(saved); setEditDrivers(false) }}
              onClose={() => setEditDrivers(false)}
            />
          )}

          <NarrativeCard
            narrative={narrative}
            loading={loadingNarr}
            onLoad={loadNarrative}
            instrument={snapshot.instrument}
          />
        </>
      )}

      {!loading && !snapshot && (
        <div className="text-center py-20 text-slate-500">
          <BarChart2 className="w-8 h-8 mx-auto mb-3 opacity-30" />
          <p>Aucune donnée disponible pour {instrumentId}</p>
        </div>
      )}

      {selectorOpen && <div className="fixed inset-0 z-40" onClick={() => setSelectorOpen(false)} />}
    </div>
  )
}
