import { useEffect, useMemo, useState } from 'react'
import {
  LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ReferenceLine, ResponsiveContainer,
} from 'recharts'
import { Layers, Plus, Trash2, RefreshCw, AlertTriangle, Search, Save, FolderOpen, X } from 'lucide-react'
import clsx from 'clsx'
import {
  useOptionChainSlice, usePriceStrategy, useOptimizeStrategy,
  useScenarios, useSaveScenario, useDeleteScenario,
  useSavedStrategies, useSaveStrategyRecord, useDeleteSavedStrategy,
  useWatchlistTickers, useSaxoCatalog, useIvForTrade,
  type StrategyLeg, type StrategyScenario, type PriceCombo, type StrategyCandidate,
  type OptimizeConstraints, type SavedScenario,
} from '../hooks/useApi'
import { fmtPrice, fmtAsOf } from '../lib/format'

type WatchlistEntry = { ticker: string; is_active: number | boolean; added_by: string }

const STRIKE_PCTS = [80, 85, 90, 95, 100, 105, 110, 115, 120]
const DELTA_NEUTRAL_THRESHOLD = 0.15

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

function emptyLeg(expiryDate: string, daysToExpiry: number, strike: number): StrategyLeg {
  return { expiry_date: expiryDate, days_to_expiry: daysToExpiry, strike, option_type: 'call', position: 'long', quantity: 1 }
}

function fmtMoney(v: number | null | undefined) {
  if (v == null) return '—'
  return `${v >= 0 ? '+' : ''}${v.toFixed(2)}`
}

function pnlColor(v: number | null | undefined) {
  if (v == null) return 'text-slate-400'
  return v >= 0 ? 'text-emerald-400' : 'text-red-400'
}

/** Rough client-side smile preview (avg call/put IV at nearest strike) — mirrors the
 * cubic-spline surface computed server-side closely enough to preview shocks live. */
function estimateBaseIv(chain: any, daysToExpiry: number, strikePct: number, spot: number): number | null {
  if (!chain) return null
  const exp = chain.expiries.reduce((best: any, e: any) =>
    Math.abs(e.days_to_expiry - daysToExpiry) < Math.abs((best?.days_to_expiry ?? Infinity) - daysToExpiry) ? e : best, null)
  if (!exp) return null
  const targetStrike = spot * (strikePct / 100)
  const rows = [...exp.calls, ...exp.puts].filter((r: any) => r.iv > 0.01)
  if (!rows.length) return null
  const nearest = rows.reduce((best: any, r: any) =>
    Math.abs(r.strike - targetStrike) < Math.abs(best.strike - targetStrike) ? r : best)
  return nearest.iv
}

// ── Payoff chart ──────────────────────────────────────────────────────────────

function PayoffChart({ priced, spot, scenarioSpot }: { priced: PriceCombo; spot: number; scenarioSpot: number }) {
  const data = priced.at_expiry.map((p, i) => ({
    underlying: p.underlying,
    expiry: p.pnl,
    scenario: priced.at_scenario[i]?.pnl,
  }))
  // Decimals scale with the underlying's own magnitude — an equity at ~740 reads fine
  // rounded to the unit, but an FX rate at ~1.15 needs several decimals or every tick
  // collapses to the same rounded label.
  const decimals = spot < 5 ? 4 : spot < 50 ? 2 : 0
  return (
    <ResponsiveContainer width="100%" height={280}>
      <LineChart data={data} margin={{ top: 8, right: 16, left: 0, bottom: 0 }}>
        <CartesianGrid strokeDasharray="3 3" stroke="#1e2d4d" />
        <XAxis dataKey="underlying" type="number" domain={['dataMin', 'dataMax']}
          tick={{ fill: '#475569', fontSize: 10 }} tickLine={false}
          tickFormatter={(v) => v.toFixed(decimals)} />
        <YAxis tick={{ fill: '#475569', fontSize: 10 }} tickLine={false} axisLine={false}
          tickFormatter={(v) => `${v}`} />
        <Tooltip
          contentStyle={{ background: '#0f1623', border: '1px solid #1e2d4d', fontSize: 11 }}
          labelFormatter={(v) => `Sous-jacent: ${Number(v).toFixed(decimals)}`}
          formatter={(v: number, name: string) => [fmtMoney(v), name]}
        />
        <Legend wrapperStyle={{ fontSize: 11 }} />
        <ReferenceLine y={0} stroke="#475569" strokeDasharray="4 4" />
        <ReferenceLine x={spot} stroke="#3b82f6" strokeDasharray="2 2" label={{ value: 'Spot', fill: '#3b82f6', fontSize: 9, position: 'top' }} />
        <ReferenceLine x={scenarioSpot} stroke="#f59e0b" strokeDasharray="2 2" label={{ value: 'Scénario J+8', fill: '#f59e0b', fontSize: 9, position: 'insideTopRight' }} />
        <Line type="monotone" dataKey="expiry" name="À échéance jambe proche" stroke="#3b82f6" strokeWidth={2} dot={false} />
        <Line type="monotone" dataKey="scenario" name="À J+8 (scénario)" stroke="#f59e0b" strokeWidth={2} dot={false} />
      </LineChart>
    </ResponsiveContainer>
  )
}

function GreeksTile({ label, now, scenario }: { label: string; now: number; scenario: number }) {
  return (
    <div className="card-sm">
      <div className="stat-label">{label}</div>
      <div className="flex items-baseline gap-2 mt-1">
        <span className="text-lg font-bold text-white">{now.toFixed(4)}</span>
        <span className="text-xs text-slate-500">→</span>
        <span className={clsx('text-sm font-semibold', scenario >= now ? 'text-emerald-400' : 'text-red-400')}>{scenario.toFixed(4)}</span>
      </div>
    </div>
  )
}

// ── Scenario panel ────────────────────────────────────────────────────────────

function ScenarioPanel({
  symbol, setSymbol, onCommitSymbol, horizonDays, setHorizonDays, scenario, setScenario, watchlistTickers,
}: {
  symbol: string; setSymbol: (v: string) => void; onCommitSymbol: (v?: string) => void
  horizonDays: number; setHorizonDays: (v: number) => void
  scenario: StrategyScenario; setScenario: (v: StrategyScenario) => void
  watchlistTickers: string[]
}) {
  const slider = (
    key: 'spot_shock_pct' | 'iv_level_shift' | 'skew_tilt' | 'term_shift',
    label: string, min: number, max: number, step: number, fmt: (v: number) => string,
  ) => (
    <div>
      <div className="flex items-center justify-between text-xs text-slate-400 mb-1">
        <span>{label}</span>
        <span className="text-white font-semibold">{fmt(scenario[key])}</span>
      </div>
      <input
        type="range" min={min} max={max} step={step} value={scenario[key]}
        onChange={(e) => setScenario({ ...scenario, [key]: parseFloat(e.target.value) })}
        className="w-full accent-blue-500"
      />
    </div>
  )

  return (
    <div className="card space-y-4">
      <div className="flex items-center gap-3">
        <div className="flex-1">
          <label className="stat-label block mb-1">Symbole</label>
          <input
            value={symbol}
            onChange={(e) => {
              const v = e.target.value.toUpperCase()
              setSymbol(v)
              // Native datalist pick lands here as a single onChange with the full value —
              // commit immediately rather than waiting for a blur that may not follow.
              if (watchlistTickers.includes(v)) onCommitSymbol(v)
            }}
            onBlur={() => onCommitSymbol()}
            onKeyDown={(e) => e.key === 'Enter' && onCommitSymbol()}
            className="w-full bg-dark-700 border border-slate-700/50 rounded px-2 py-1.5 text-sm text-white"
            placeholder="SPY, QQQ, GLD…"
            list="strategy-builder-watchlist"
          />
          <datalist id="strategy-builder-watchlist">
            {watchlistTickers.map(t => <option key={t} value={t} />)}
          </datalist>
        </div>
        <div className="w-28">
          <label className="stat-label block mb-1">Horizon (j)</label>
          <input
            type="number" min={1} max={90} value={horizonDays}
            onChange={(e) => setHorizonDays(parseInt(e.target.value) || 8)}
            className="w-full bg-dark-700 border border-slate-700/50 rounded px-2 py-1.5 text-sm text-white"
          />
        </div>
      </div>

      <div className="grid grid-cols-2 gap-4">
        {slider('spot_shock_pct', 'Choc spot', -20, 20, 0.5, (v) => `${v >= 0 ? '+' : ''}${v.toFixed(1)}%`)}
        {slider('iv_level_shift', 'Choc niveau IV', -0.15, 0.15, 0.005, (v) => `${v >= 0 ? '+' : ''}${(v * 100).toFixed(1)}pts`)}
        {slider('skew_tilt', 'Tilt skew', -0.1, 0.1, 0.005, (v) => v.toFixed(3))}
        {slider('term_shift', 'Choc terme (/30j)', -0.1, 0.1, 0.005, (v) => `${v >= 0 ? '+' : ''}${(v * 100).toFixed(1)}pts`)}
      </div>
    </div>
  )
}

// ── Manual grid override ──────────────────────────────────────────────────────

function ScenarioGrid({
  chain, spot, scenario, setScenario,
}: {
  chain: any; spot: number; scenario: StrategyScenario; setScenario: (v: StrategyScenario) => void
}) {
  if (!chain) return null
  const overrides = scenario.manual_grid || []

  const cellValue = (daysToExpiry: number, strikePct: number): number | null => {
    const hit = overrides.find(o => o.days_to_expiry === daysToExpiry && o.strike_pct === strikePct)
    if (hit) return hit.iv
    const base = estimateBaseIv(chain, daysToExpiry, strikePct, spot)
    if (base == null) return null
    const moneyness = Math.log(strikePct / 100)
    return Math.max(0.01, base + scenario.iv_level_shift + scenario.skew_tilt * moneyness + scenario.term_shift * (daysToExpiry / 30))
  }

  const setOverride = (daysToExpiry: number, strikePct: number, iv: number | null) => {
    const next = overrides.filter(o => !(o.days_to_expiry === daysToExpiry && o.strike_pct === strikePct))
    if (iv != null) next.push({ days_to_expiry: daysToExpiry, strike_pct: strikePct, iv })
    setScenario({ ...scenario, manual_grid: next })
  }

  const isOverridden = (daysToExpiry: number, strikePct: number) =>
    overrides.some(o => o.days_to_expiry === daysToExpiry && o.strike_pct === strikePct)

  return (
    <div className="card">
      <div className="flex items-center justify-between mb-2">
        <div className="stat-label">Grille IV scénario (éditable — clic sur une cellule)</div>
        {overrides.length > 0 && (
          <button onClick={() => setScenario({ ...scenario, manual_grid: [] })} className="text-xs text-slate-500 hover:text-slate-300">
            Réinitialiser overrides
          </button>
        )}
      </div>
      <div className="overflow-x-auto">
        <table className="w-full text-xs">
          <thead>
            <tr className="text-slate-500">
              <th className="text-left py-1 pr-3">Expiry / Strike%</th>
              {STRIKE_PCTS.map(p => <th key={p} className="px-2 py-1 text-right">{p}%</th>)}
            </tr>
          </thead>
          <tbody>
            {chain.expiries.map((exp: any) => (
              <tr key={exp.expiry_date} className="border-t border-slate-700/30">
                <td className="py-1 pr-3 text-slate-400 whitespace-nowrap">{exp.expiry_date} ({exp.days_to_expiry}j)</td>
                {STRIKE_PCTS.map(pct => {
                  const v = cellValue(exp.days_to_expiry, pct)
                  const overridden = isOverridden(exp.days_to_expiry, pct)
                  return (
                    <td key={pct} className="px-1 py-1">
                      <input
                        type="number" step={0.005}
                        value={v != null ? Math.round(v * 1000) / 1000 : ''}
                        onChange={(e) => {
                          const val = e.target.value === '' ? null : parseFloat(e.target.value)
                          setOverride(exp.days_to_expiry, pct, val)
                        }}
                        className={clsx(
                          'w-16 text-right bg-dark-700 border rounded px-1 py-0.5',
                          overridden ? 'border-amber-500 text-amber-300' : 'border-slate-700/40 text-slate-300',
                        )}
                      />
                    </td>
                  )
                })}
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  )
}

// ── Volatility surface heatmap ────────────────────────────────────────────────
// Sequential single-hue ramp (light → dark), light steps get dark text for contrast —
// magnitude encoding per dataviz convention, no rainbow, no dual-hue diverging misuse.
const IV_RAMP = ['#1e2d4d', '#1e3a6e', '#1d4f9e', '#1a63c4', '#3b82f6', '#60a5fa', '#93c5fd', '#bfdbfe']

function ivCellColor(value: number, min: number, max: number): { bg: string; fg: string } {
  const range = max - min || 1
  const t = Math.min(1, Math.max(0, (value - min) / range))
  const idx = Math.min(IV_RAMP.length - 1, Math.floor(t * IV_RAMP.length))
  const bg = IV_RAMP[IV_RAMP.length - 1 - idx] // low IV -> light, high IV -> dark
  const fg = idx >= IV_RAMP.length - 3 ? '#0f1623' : '#ffffff'
  return { bg, fg }
}

function VolSurfaceHeatmap({ chain, spot }: { chain: any; spot: number }) {
  const cells = useMemo(() => {
    if (!chain) return []
    const out: { expiry: string; days: number; pct: number; iv: number | null }[] = []
    for (const exp of chain.expiries) {
      for (const pct of STRIKE_PCTS) {
        const target = spot * (pct / 100)
        const rows = [...exp.calls, ...exp.puts].filter((r: any) => r.iv > 0.01)
        if (!rows.length) {
          out.push({ expiry: exp.expiry_date, days: exp.days_to_expiry, pct, iv: null })
          continue
        }
        const nearest = rows.reduce((best: any, r: any) =>
          Math.abs(r.strike - target) < Math.abs(best.strike - target) ? r : best)
        out.push({ expiry: exp.expiry_date, days: exp.days_to_expiry, pct, iv: nearest.iv })
      }
    }
    return out
  }, [chain, spot])

  if (!chain) return null
  const ivValues = cells.map(c => c.iv).filter((v): v is number => v != null)
  if (!ivValues.length) return <div className="card-sm text-xs text-slate-500">Pas assez de données d'IV pour construire la surface.</div>
  const min = Math.min(...ivValues)
  const max = Math.max(...ivValues)

  return (
    <div className="card">
      <div className="flex items-center justify-between mb-2">
        <div className="stat-label">Surface de volatilité (strike × expiry)</div>
        <div className="flex items-center gap-2 text-[10px] text-slate-500">
          <span>{(min * 100).toFixed(1)}%</span>
          <div className="flex h-2 w-24 rounded overflow-hidden">
            {[...IV_RAMP].reverse().map(c => <div key={c} className="flex-1" style={{ background: c }} />)}
          </div>
          <span>{(max * 100).toFixed(1)}%</span>
        </div>
      </div>
      <div className="overflow-x-auto">
        <table className="w-full text-[10px] border-separate" style={{ borderSpacing: 2 }}>
          <thead>
            <tr>
              <th className="text-left text-slate-500 pr-2">Expiry</th>
              {STRIKE_PCTS.map(p => <th key={p} className="text-slate-500 font-normal px-1">{p}%</th>)}
            </tr>
          </thead>
          <tbody>
            {chain.expiries.map((exp: any) => (
              <tr key={exp.expiry_date}>
                <td className="text-slate-400 pr-2 whitespace-nowrap">{exp.expiry_date} ({exp.days_to_expiry}j)</td>
                {STRIKE_PCTS.map(pct => {
                  const cell = cells.find(c => c.expiry === exp.expiry_date && c.pct === pct)
                  if (!cell || cell.iv == null) {
                    return <td key={pct} className="text-center text-slate-700 bg-dark-700/40 rounded">—</td>
                  }
                  const { bg, fg } = ivCellColor(cell.iv, min, max)
                  return (
                    <td key={pct} className="text-center rounded font-semibold" style={{ background: bg, color: fg }}
                      title={`${exp.expiry_date} · ${pct}% (${fmtPrice(spot * pct / 100)}) · IV ${(cell.iv * 100).toFixed(1)}%`}>
                      {(cell.iv * 100).toFixed(1)}
                    </td>
                  )
                })}
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  )
}

// ── Manual leg builder ────────────────────────────────────────────────────────

function LegRow({
  leg, chain, onChange, onRemove,
}: {
  leg: StrategyLeg; chain: any
  onChange: (leg: StrategyLeg) => void
  onRemove: () => void
}) {
  const expiry = chain?.expiries.find((e: any) => e.expiry_date === leg.expiry_date)
  const rows = expiry ? (leg.option_type === 'call' ? expiry.calls : expiry.puts) : []

  return (
    <div className="grid grid-cols-12 gap-2 items-center text-xs">
      <select
        className="col-span-3 bg-dark-700 border border-slate-700/50 rounded px-2 py-1.5 text-slate-200"
        value={leg.expiry_date}
        onChange={(e) => {
          const exp = chain.expiries.find((x: any) => x.expiry_date === e.target.value)
          onChange({ ...leg, expiry_date: e.target.value, days_to_expiry: exp?.days_to_expiry ?? leg.days_to_expiry })
        }}
      >
        {chain?.expiries.map((e: any) => (
          <option key={e.expiry_date} value={e.expiry_date}>{e.expiry_date} ({e.days_to_expiry}j)</option>
        ))}
      </select>
      <select
        className="col-span-2 bg-dark-700 border border-slate-700/50 rounded px-2 py-1.5 text-slate-200"
        value={leg.option_type}
        onChange={(e) => onChange({ ...leg, option_type: e.target.value as 'call' | 'put' })}
      >
        <option value="call">Call</option>
        <option value="put">Put</option>
      </select>
      <select
        className="col-span-3 bg-dark-700 border border-slate-700/50 rounded px-2 py-1.5 text-slate-200"
        value={leg.strike}
        onChange={(e) => onChange({ ...leg, strike: parseFloat(e.target.value) })}
      >
        {rows.map((r: any) => (
          <option key={r.strike} value={r.strike}>{fmtPrice(r.strike)} (bid {fmtPrice(r.bid)} / ask {fmtPrice(r.ask)})</option>
        ))}
      </select>
      <select
        className="col-span-2 bg-dark-700 border border-slate-700/50 rounded px-2 py-1.5 text-slate-200"
        value={leg.position}
        onChange={(e) => onChange({ ...leg, position: e.target.value as 'long' | 'short' })}
      >
        <option value="long">Achat</option>
        <option value="short">Vente</option>
      </select>
      <input
        type="number" min={1} value={leg.quantity}
        onChange={(e) => onChange({ ...leg, quantity: parseInt(e.target.value) || 1 })}
        className="col-span-1 bg-dark-700 border border-slate-700/50 rounded px-2 py-1.5 text-slate-200"
      />
      <button onClick={onRemove} className="col-span-1 text-slate-500 hover:text-red-400 flex justify-center">
        <Trash2 className="w-3.5 h-3.5" />
      </button>
    </div>
  )
}

// ── Optimizer panel ───────────────────────────────────────────────────────────

const OBJECTIVES: { value: OptimizeConstraints['objective']; label: string }[] = [
  { value: 'net_pnl', label: 'P&L net max' },
  { value: 'return_on_risk', label: 'Retour sur risque (P&L / perte max)' },
  { value: 'prob_weighted', label: 'Espérance pondérée par probabilité' },
]

function OptimizerPanel({
  constraints, setConstraints, onRun, isRunning,
}: {
  constraints: OptimizeConstraints; setConstraints: (v: OptimizeConstraints) => void
  onRun: () => void; isRunning: boolean
}) {
  return (
    <div className="card space-y-3">
      <div className="stat-label">Optimiseur — contraintes &amp; objectif</div>
      <div className="grid grid-cols-2 md:grid-cols-4 gap-3 text-xs">
        <div>
          <label className="text-slate-400 block mb-1">Max jambes</label>
          <select
            value={constraints.max_legs}
            onChange={(e) => setConstraints({ ...constraints, max_legs: parseInt(e.target.value) })}
            className="w-full bg-dark-700 border border-slate-700/50 rounded px-2 py-1.5 text-slate-200"
          >
            {[1, 2, 3, 4].map(n => <option key={n} value={n}>{n}</option>)}
          </select>
        </div>
        <div>
          <label className="text-slate-400 block mb-1">Seuil neutralité Δ (optionnel)</label>
          <input
            type="number" step={0.01} min={0} placeholder="illimité"
            value={constraints.delta_threshold ?? ''}
            onChange={(e) => setConstraints({ ...constraints, delta_threshold: e.target.value === '' ? null : parseFloat(e.target.value) })}
            className="w-full bg-dark-700 border border-slate-700/50 rounded px-2 py-1.5 text-slate-200"
          />
        </div>
        <div>
          <label className="text-slate-400 block mb-1">Plafond perte max (optionnel)</label>
          <input
            type="number" placeholder="illimité"
            value={constraints.max_loss_cap ?? ''}
            onChange={(e) => setConstraints({ ...constraints, max_loss_cap: e.target.value === '' ? null : parseFloat(e.target.value) })}
            className="w-full bg-dark-700 border border-slate-700/50 rounded px-2 py-1.5 text-slate-200"
          />
        </div>
        <div>
          <label className="text-slate-400 block mb-1">Objectif</label>
          <select
            value={constraints.objective}
            onChange={(e) => setConstraints({ ...constraints, objective: e.target.value as OptimizeConstraints['objective'] })}
            className="w-full bg-dark-700 border border-slate-700/50 rounded px-2 py-1.5 text-slate-200"
          >
            {OBJECTIVES.map(o => <option key={o.value} value={o.value}>{o.label}</option>)}
          </select>
        </div>
      </div>
      <button
        onClick={onRun}
        disabled={isRunning}
        className="flex items-center gap-1.5 text-xs bg-blue-600 hover:bg-blue-500 disabled:opacity-50 text-white px-3 py-1.5 rounded font-semibold"
      >
        <Search className={clsx('w-3.5 h-3.5', isRunning && 'animate-pulse')} />
        {isRunning ? 'Recherche parmi des milliers de combinaisons…' : 'Trouver la stratégie optimale'}
      </button>
    </div>
  )
}

function ResultsTable({ results, onSelect }: { results: StrategyCandidate[]; onSelect: (c: StrategyCandidate) => void }) {
  if (!results.length) return <div className="card-sm text-xs text-slate-500">Aucun candidat ne satisfait les contraintes — élargissez le seuil de delta ou le plafond de perte.</div>
  return (
    <div className="card overflow-x-auto">
      <div className="stat-label mb-2">{results.length} candidats classés</div>
      <table className="w-full text-xs">
        <thead>
          <tr className="text-slate-500 text-left">
            <th className="py-1 pr-3">Structure</th>
            <th className="py-1 pr-3">Jambes</th>
            <th className="py-1 pr-3 text-right">Score</th>
            <th className="py-1 pr-3 text-right">P&amp;L net</th>
            <th className="py-1 pr-3 text-right" title="À l'échéance de la jambe la plus proche, sous la même vue de vol que le scénario. Approximatif (balayage rapide pour classer des centaines de candidats) — se précise après «Charger».">Max gain</th>
            <th className="py-1 pr-3 text-right" title="À l'échéance de la jambe la plus proche, sous la même vue de vol que le scénario. Approximatif (balayage rapide pour classer des centaines de candidats) — se précise après «Charger».">Max perte</th>
            <th className="py-1 pr-3 text-right">Δ net</th>
            <th className="py-1 pr-1"></th>
          </tr>
        </thead>
        <tbody>
          {results.map((r, i) => (
            <tr key={i} className="border-t border-slate-700/30 hover:bg-dark-700/40 cursor-pointer" onClick={() => onSelect(r)}>
              <td className="py-1.5 pr-3 text-slate-200">{r.template_name}</td>
              <td className="py-1.5 pr-3 text-slate-400">{r.legs.length}</td>
              <td className="py-1.5 pr-3 text-right font-semibold text-white">{r.score.toFixed(2)}</td>
              <td className={clsx('py-1.5 pr-3 text-right font-semibold', pnlColor(r.net_pnl))}>{fmtMoney(r.net_pnl)}</td>
              <td className="py-1.5 pr-3 text-right text-emerald-400">{r.max_gain != null ? fmtMoney(r.max_gain) : '∞'}</td>
              <td className="py-1.5 pr-3 text-right text-red-400">{r.max_loss != null ? fmtMoney(r.max_loss) : '−∞'}</td>
              <td className="py-1.5 pr-3 text-right text-slate-400">{r.net_delta_now.toFixed(3)}</td>
              <td className="py-1.5 pr-1 text-blue-400 text-right">Charger →</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  )
}

// ── Scenario save/load ────────────────────────────────────────────────────────

function ScenarioLibrary({
  symbol, scenario, onLoad,
}: {
  symbol: string; scenario: StrategyScenario; onLoad: (s: SavedScenario) => void
}) {
  const { data: saved = [] } = useScenarios(symbol)
  const saveScenario = useSaveScenario()
  const deleteScenario = useDeleteScenario()
  const [label, setLabel] = useState('')

  return (
    <div className="card-sm space-y-2">
      <div className="flex items-center gap-2">
        <input
          value={label} onChange={(e) => setLabel(e.target.value)} placeholder="Nom du scénario…"
          className="flex-1 bg-dark-700 border border-slate-700/50 rounded px-2 py-1 text-xs text-white"
        />
        <button
          onClick={() => { saveScenario.mutate({ ...scenario, label }); setLabel('') }}
          disabled={!label || saveScenario.isPending}
          className="flex items-center gap-1 text-xs bg-dark-700 hover:bg-dark-600 border border-slate-700/50 text-slate-300 px-2 py-1 rounded disabled:opacity-40"
        >
          <Save className="w-3 h-3" /> Sauvegarder
        </button>
      </div>
      {saved.length > 0 && (
        <div className="flex flex-wrap gap-1.5">
          {saved.map(s => (
            <span key={s.id} className="badge-blue flex items-center gap-1">
              <button onClick={() => onLoad(s)} className="flex items-center gap-1">
                <FolderOpen className="w-3 h-3" /> {s.label || s.id}
              </button>
              <button onClick={() => deleteScenario.mutate(s.id)}><X className="w-3 h-3" /></button>
            </span>
          ))}
        </div>
      )}
    </div>
  )
}

// ── Saved strategies library ──────────────────────────────────────────────────

function SavedStrategiesLibrary({ symbol, onLoad }: { symbol: string; onLoad: (legs: StrategyLeg[], templateName: string) => void }) {
  const { data: saved = [] } = useSavedStrategies(symbol)
  const deleteStrategy = useDeleteSavedStrategy()

  if (!saved.length) return null

  return (
    <div className="card-sm space-y-2">
      <div className="stat-label">Stratégies sauvegardées ({symbol})</div>
      <div className="space-y-1">
        {saved.map(s => (
          <div key={s.id} className="flex items-center justify-between text-xs bg-dark-700/50 rounded px-2 py-1.5">
            <button onClick={() => onLoad(s.legs, s.template_name)} className="flex items-center gap-2 text-slate-300 hover:text-white">
              <FolderOpen className="w-3 h-3 text-blue-400" />
              <span>{s.template_name}</span>
              <span className={pnlColor(s.net_pnl_scenario)}>{fmtMoney(s.net_pnl_scenario)}</span>
              <span className="text-slate-600">{s.legs.length} jambes</span>
            </button>
            <button onClick={() => deleteStrategy.mutate(s.id)} className="text-slate-500 hover:text-red-400">
              <Trash2 className="w-3 h-3" />
            </button>
          </div>
        ))}
      </div>
    </div>
  )
}

// ── Page ──────────────────────────────────────────────────────────────────────

export default function StrategyBuilder() {
  const [symbol, setSymbol] = useState('')
  const [debouncedSymbol, setDebouncedSymbol] = useState('')
  const [horizonDays, setHorizonDays] = useState(8)
  const [scenario, setScenario] = useState<StrategyScenario>({
    symbol: '', horizon_days: 8, spot_shock_pct: 0, iv_level_shift: 0, skew_tilt: 0, term_shift: 0, manual_grid: [],
    contract_size: 100_000,
  })

  // Chain lookup only commits on blur/Enter/datalist-pick, never mid-keystroke — typing
  // "EUU" while aiming for "EUU:XCME" must never flash a "ticker not found" error. Takes
  // an optional explicit value so the datalist-pick path (which calls this synchronously
  // right after setSymbol in the same onChange) doesn't read a stale pre-update closure.
  const commitSymbol = (v?: string) => setDebouncedSymbol((v ?? symbol).trim())

  const [legs, setLegs] = useState<StrategyLeg[]>([])
  const [constraints, setConstraints] = useState<OptimizeConstraints>({
    max_legs: 4, delta_threshold: 0.15, max_loss_cap: null, objective: 'net_pnl', top_n: 20,
  })
  const [activeTemplate, setActiveTemplate] = useState<string | null>(null)

  const { data: watchlistData } = useWatchlistTickers()
  const { data: saxoCatalog } = useSaxoCatalog(undefined, undefined, { enabled: true, limit: 500 })
  const watchlistTickers = Array.from(new Set([
    ...((watchlistData?.tickers ?? []) as WatchlistEntry[]).filter(t => t.is_active).map(t => t.ticker),
    ...(saxoCatalog ?? []).map(c => c.symbol),
  ])).sort()

  const { data: chain, isLoading: chainLoading, isError: chainError, error: chainErrorObj, refetch: refetchChain, isFetching } =
    useOptionChainSlice(debouncedSymbol, horizonDays, 3)
  const { data: ivForTrade } = useIvForTrade(debouncedSymbol)

  useEffect(() => {
    setScenario(s => ({ ...s, symbol: debouncedSymbol, horizon_days: horizonDays }))
  }, [debouncedSymbol, horizonDays])

  useEffect(() => {
    if (chain && chain.expiries.length && legs.length === 0) {
      const exp = chain.expiries[0]
      const atm = exp.calls.reduce((best: any, r: any) =>
        Math.abs(r.strike - chain.spot) < Math.abs(best.strike - chain.spot) ? r : best, exp.calls[0])
      if (atm) setLegs([emptyLeg(exp.expiry_date, exp.days_to_expiry, atm.strike)])
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [chain])

  const priceMutation = usePriceStrategy()

  useEffect(() => {
    if (!chain || legs.length === 0) return
    const t = setTimeout(() => {
      priceMutation.mutate({ scenario, legs })
    }, 400)
    return () => clearTimeout(t)
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [JSON.stringify(scenario), JSON.stringify(legs), chain])

  const priced = priceMutation.data

  const isNonDirectional = useMemo(() => {
    if (!priced) return null
    return Math.abs(priced.net_delta_now) <= DELTA_NEUTRAL_THRESHOLD
  }, [priced])

  const addLeg = () => {
    if (!chain || legs.length >= 4) return
    const exp = chain.expiries[0]
    setLegs([...legs, emptyLeg(exp.expiry_date, exp.days_to_expiry, chain.spot)])
  }

  const optimizeMutation = useOptimizeStrategy()
  const saveStrategy = useSaveStrategyRecord()

  const handleOptimize = () => {
    setActiveTemplate(null)
    optimizeMutation.mutate({ scenario, constraints })
  }

  const handleSelectCandidate = (c: StrategyCandidate) => {
    setActiveTemplate(c.template_name)
    setLegs(c.legs)
  }

  const handleLoadScenario = (s: SavedScenario) => {
    setSymbol(s.symbol)
    setHorizonDays(s.horizon_days)
    setScenario(prev => ({
      symbol: s.symbol, horizon_days: s.horizon_days, spot_shock_pct: s.spot_shock_pct,
      iv_level_shift: s.iv_level_shift, skew_tilt: s.skew_tilt, term_shift: s.term_shift,
      manual_grid: s.manual_grid, contract_size: prev.contract_size,
    }))
  }

  const handleSaveStrategy = () => {
    if (!priced) return
    saveStrategy.mutate({
      symbol, template_name: activeTemplate || 'Manuel', objective: constraints.objective, legs,
      entry_cost: priced.entry_cost, max_gain: priced.max_gain, max_loss: priced.max_loss,
      net_pnl_scenario: priced.net_pnl, net_delta: priced.net_delta_now,
    })
  }

  return (
    <div className="p-6 space-y-5">
      <div className="flex items-center justify-between">
        <div>
          <h1 className="text-xl font-bold text-white flex items-center gap-2">
            <Layers className="w-5 h-5 text-blue-400" /> Strategy Builder
          </h1>
          <p className="text-xs text-slate-500 mt-0.5">
            Scénario spot/IV/surface à J+N · builder manuel 1-4 jambes · payoff &amp; greeks avec spread broker réel
          </p>
        </div>
        <button
          onClick={() => refetchChain()}
          disabled={isFetching}
          className="flex items-center gap-1.5 text-xs border border-slate-600 text-slate-400 hover:text-slate-200 hover:border-slate-500 px-3 py-1.5 rounded transition-all disabled:opacity-50"
        >
          <RefreshCw className={clsx('w-3.5 h-3.5', isFetching && 'animate-spin')} />
          {isFetching ? 'Chargement...' : 'Rafraîchir la chaîne'}
        </button>
      </div>

      {chainError && (
        <div className="px-4 py-3 rounded border border-red-700/40 bg-red-900/10 text-xs text-red-300 flex items-center gap-2">
          <AlertTriangle className="w-4 h-4" />
          {(chainErrorObj as any)?.response?.data?.detail ?? `Chaîne d'options indisponible pour ${debouncedSymbol}. Essayez un autre symbole.`}
        </div>
      )}

      <ScenarioPanel symbol={symbol} setSymbol={setSymbol} onCommitSymbol={commitSymbol} horizonDays={horizonDays} setHorizonDays={setHorizonDays}
        scenario={scenario} setScenario={setScenario} watchlistTickers={watchlistTickers} />

      <ScenarioLibrary symbol={debouncedSymbol} scenario={scenario} onLoad={handleLoadScenario} />
      <SavedStrategiesLibrary symbol={debouncedSymbol} onLoad={(legs, templateName) => { setActiveTemplate(templateName); setLegs(legs) }} />

      {chainLoading && <div className="card-sm text-xs text-slate-500">Chargement de la chaîne réelle ({debouncedSymbol})…</div>}

      {chain && <ScenarioGrid chain={chain} spot={chain.spot} scenario={scenario} setScenario={setScenario} />}
      {chain && <VolSurfaceHeatmap chain={chain} spot={chain.spot} />}

      {chain && (
        <div className="card space-y-3">
          <div className="flex items-center justify-between">
            <div className="stat-label flex items-center gap-2 flex-wrap">
              <span>Jambes (1-4) — Spot {fmtPrice(chain.spot)}</span>
              {fmtAsOf(chain.as_of) && (
                <span className="text-slate-600 font-normal text-[10px]" title="Date de capture de la chaîne d'options (dernier snapshot Saxo)">
                  · données du {fmtAsOf(chain.as_of)}
                </span>
              )}
              {ivForTrade?.iv_current_pct != null && (
                <span className="text-slate-500 font-normal text-[10px]">
                  · IV ATM {ivForTrade.iv_current_pct.toFixed(1)}%
                  {ivForTrade.iv_change_1d_pct != null && Math.abs(ivForTrade.iv_change_1d_pct) >= 0.1 && (
                    <span className={ivForTrade.iv_change_1d_pct > 0 ? 'text-orange-400' : 'text-blue-400'}>
                      {' '}({ivForTrade.iv_change_1d_pct >= 0 ? '+' : ''}{ivForTrade.iv_change_1d_pct.toFixed(1)}pt vs veille)
                    </span>
                  )}
                </span>
              )}
            </div>
            <div className="flex items-center gap-3">
              <label className="flex items-center gap-1.5 text-xs text-slate-400" title="Notionnel par contrat (ex. 100 000 = 1 lot standard EURUSD). S'applique à chaque jambe, multiplié par sa quantité.">
                Nominal (USD)
                <input
                  type="number" step={1000} min={1}
                  value={scenario.contract_size ?? 100_000}
                  onChange={(e) => setScenario(s => ({ ...s, contract_size: parseFloat(e.target.value) || 100_000 }))}
                  className="w-28 bg-dark-700 border border-slate-700/50 rounded px-2 py-1 text-slate-200"
                />
              </label>
              <button
                onClick={addLeg}
                disabled={legs.length >= 4}
                className="flex items-center gap-1 text-xs bg-blue-600 hover:bg-blue-500 disabled:opacity-40 text-white px-2.5 py-1 rounded"
              >
                <Plus className="w-3.5 h-3.5" /> Ajouter une jambe
              </button>
            </div>
          </div>
          <div className="space-y-2">
            {legs.map((leg, i) => (
              <LegRow
                key={i} leg={leg} chain={chain}
                onChange={(l) => setLegs(legs.map((x, j) => j === i ? l : x))}
                onRemove={() => setLegs(legs.filter((_, j) => j !== i))}
              />
            ))}
            {legs.length === 0 && <div className="text-xs text-slate-500">Aucune jambe — ajoutez-en une pour commencer.</div>}
          </div>
        </div>
      )}

      {chain && (
        <OptimizerPanel constraints={constraints} setConstraints={setConstraints} onRun={handleOptimize} isRunning={optimizeMutation.isPending} />
      )}

      {optimizeMutation.isError && (
        <div className="px-4 py-3 rounded border border-red-700/40 bg-red-900/10 text-xs text-red-300">
          {(optimizeMutation.error as any)?.response?.data?.detail ?? "Erreur lors de l'optimisation."}
        </div>
      )}
      {optimizeMutation.data && (
        <ResultsTable results={optimizeMutation.data} onSelect={handleSelectCandidate} />
      )}

      {priceMutation.isPending && <div className="card-sm text-xs text-slate-500">Calcul en cours…</div>}
      {priceMutation.isError && (
        <div className="px-4 py-3 rounded border border-red-700/40 bg-red-900/10 text-xs text-red-300">
          Erreur de pricing — vérifiez les jambes sélectionnées.
        </div>
      )}

      {priced && (
        <>
          <div className="grid grid-cols-2 md:grid-cols-4 gap-3">
            <div className="card-sm">
              <div className="stat-label">Coût d'entrée (spread inclus)</div>
              <div className={clsx('text-lg font-bold', priced.entry_cost >= 0 ? 'text-white' : 'text-emerald-400')}>{fmtMoney(priced.entry_cost)}</div>
            </div>
            <div className="card-sm">
              <div className="stat-label">P&amp;L net scénario J+{horizonDays}</div>
              <div className={clsx('text-lg font-bold', pnlColor(priced.net_pnl))}>{fmtMoney(priced.net_pnl)}</div>
            </div>
            <div className="card-sm">
              <div className="stat-label">Coût spread broker</div>
              <div className="text-lg font-bold text-amber-400">{fmtMoney(priced.broker_spread_cost)}</div>
            </div>
            <div className="card-sm" title="Borne théorique à l'échéance de la jambe la plus proche, sous la même vue de volatilité que votre scénario. Pour les structures mono-échéance (condor, butterfly...), c'est un calcul purement intrinsèque, indépendant de la vol. Le P&L net scénario est une valorisation avant échéance (mark-to-market) : sur une structure vendeuse de vega, un choc de vol important dans le scénario peut transitoirement le faire sortir de cette fourchette — risque vega réel, pas un bug.">
              <div className="stat-label">Max gain / Max perte (à échéance)</div>
              <div className="text-sm font-semibold">
                <span className="text-emerald-400">{priced.max_gain != null ? fmtMoney(priced.max_gain) : '∞'}</span>
                <span className="text-slate-500"> / </span>
                <span className="text-red-400">{priced.max_loss != null ? fmtMoney(priced.max_loss) : '−∞'}</span>
              </div>
            </div>
          </div>

          <div className="flex items-center gap-2">
            <span className={clsx('badge', priced.bounded_risk ? 'badge-green' : 'badge-red')}>
              {priced.bounded_risk ? 'Risque borné' : 'Risque non borné'}
            </span>
            <span className={clsx('badge', isNonDirectional ? 'badge-blue' : 'badge-orange')}>
              Δ net {priced.net_delta_now.toFixed(3)} — {isNonDirectional ? 'non-directionnel' : 'directionnel'}
            </span>
            <button
              onClick={handleSaveStrategy}
              disabled={saveStrategy.isPending}
              className="ml-auto flex items-center gap-1 text-xs bg-dark-700 hover:bg-dark-600 border border-slate-700/50 text-slate-300 px-2.5 py-1 rounded disabled:opacity-40"
            >
              <Save className="w-3 h-3" /> {saveStrategy.isSuccess ? 'Sauvegardé ✓' : 'Sauvegarder cette stratégie'}
            </button>
          </div>

          <div className="card">
            <div className="stat-label mb-2">Diagramme payoff</div>
            <PayoffChart priced={priced} spot={priced.spot} scenarioSpot={priced.scenario_spot} />
            <p className="text-[11px] text-slate-500 mt-1">
              Les deux courbes utilisent la même vue de volatilité (celle du scénario) — seule la date diffère : bleu = à l'échéance de la jambe la plus proche, orange = à J+{horizonDays}.
            </p>
          </div>

          <div className="grid grid-cols-2 md:grid-cols-4 gap-3">
            <GreeksTile label="Delta net" now={priced.greeks_now.delta} scenario={priced.greeks_scenario.delta} />
            <GreeksTile label="Gamma net" now={priced.greeks_now.gamma} scenario={priced.greeks_scenario.gamma} />
            <GreeksTile label="Theta net" now={priced.greeks_now.theta} scenario={priced.greeks_scenario.theta} />
            <GreeksTile label="Vega net" now={priced.greeks_now.vega} scenario={priced.greeks_scenario.vega} />
          </div>
        </>
      )}
    </div>
  )
}
