import { useState, useEffect, useCallback, useRef, KeyboardEvent, useId } from 'react'
import clsx from 'clsx'
import { RefreshCw, ChevronDown, AlertTriangle, Check, Upload } from 'lucide-react'

const API = ''

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

interface FFEvent {
  id: number
  event_date: string       // YYYY-MM-DD UTC
  event_time: string | null // HH:MM UTC
  currency: string          // USD, EUR, ...
  impact: string            // high / medium / low
  event_name: string
  actual_value: string | null
  forecast_value: string | null
  previous_value: string | null
  source: string
}

interface CalendarResponse {
  total: number
  period: string
  date_from: string | null
  date_to: string | null
  events: FFEvent[]
}

interface FFStats {
  total: number
  earliest?: string
  latest?: string
}

interface ImportStatus {
  running: boolean
  last_result: { inserted?: number; skipped?: number; total?: number; error?: string } | null
}


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

const PERIODS = [
  { key: 'recent',         label: 'Recent' },
  { key: 'today',          label: 'Today' },
  { key: 'tomorrow',       label: 'Tomorrow' },
  { key: 'this_week',      label: 'This Week' },
  { key: 'next_week',      label: 'Next Week' },
  { key: 'this_month',     label: 'This Month' },
  { key: 'next_month',     label: 'Next Month' },
  { key: 'yesterday',      label: 'Yesterday' },
  { key: 'previous_week',  label: 'Prev Week' },
  { key: 'previous_month', label: 'Prev Month' },
  { key: 'custom',         label: '📅 Custom' },
] as const

const ALL_CURRENCIES = ['USD', 'EUR', 'GBP', 'JPY', 'AUD', 'CAD', 'NZD', 'CHF', 'CNY']

const CURRENCY_FLAGS: Record<string, string> = {
  USD: '🇺🇸', EUR: '🇪🇺', GBP: '🇬🇧', JPY: '🇯🇵',
  AUD: '🇦🇺', CAD: '🇨🇦', NZD: '🇳🇿', CHF: '🇨🇭', CNY: '🇨🇳',
}

const IMPACT_COLOR: Record<string, string> = {
  high:   'bg-red-500',
  medium: 'bg-yellow-400',
  low:    'bg-slate-400',
}

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

function parseNumeric(s: string | null): number | null {
  if (!s) return null
  const clean = s.replace(/[,%KBMkbm]/g, '').trim()
  const n = parseFloat(clean)
  return isNaN(n) ? null : n
}

/** Compare actual vs forecast: +1 better, -1 worse, 0 neutral/unknown */
function direction(ev: FFEvent): 1 | -1 | 0 {
  const a = parseNumeric(ev.actual_value)
  const f = parseNumeric(ev.forecast_value)
  if (a === null || f === null) return 0
  if (Math.abs(a - f) < 0.0001) return 0
  // For now: higher = better (works for NFP, GDP, PMI; wrong for unemployment — acceptable for display)
  return a > f ? 1 : -1
}

function formatDate(dateStr: string): string {
  const d = new Date(dateStr + 'T00:00:00Z')
  return d.toLocaleDateString('en-US', { weekday: 'short', month: 'short', day: 'numeric', timeZone: 'UTC' })
}

function isToday(dateStr: string): boolean {
  const today = new Date()
  const utc = `${today.getUTCFullYear()}-${String(today.getUTCMonth()+1).padStart(2,'0')}-${String(today.getUTCDate()).padStart(2,'0')}`
  return dateStr === utc
}

function isPast(dateStr: string, timeStr: string | null): boolean {
  const now = new Date()
  const dt = new Date(`${dateStr}T${timeStr || '00:00'}:00Z`)
  return dt < now
}

// ── Sub-components ────────────────────────────────────────────────────────────

function ImpactDot({ impact }: { impact: string }) {
  return <span className={clsx('inline-block w-2 h-2 rounded-full', IMPACT_COLOR[impact] ?? 'bg-slate-400')} />
}

function ValueCell({ value, className }: { value: string | null; className?: string }) {
  if (!value) return <span className="text-slate-500">—</span>
  return <span className={className}>{value}</span>
}

function SurpriseChip({ ev }: { ev: FFEvent }) {
  if (!ev.actual_value || !ev.forecast_value) return null
  const d = direction(ev)
  if (d === 0) return null
  return (
    <span className={clsx(
      'ml-1 text-xs px-1 rounded',
      d === 1 ? 'bg-emerald-900/50 text-emerald-400' : 'bg-red-900/50 text-red-400'
    )}>
      {d === 1 ? '▲' : '▼'}
    </span>
  )
}

// ── Import Panel ──────────────────────────────────────────────────────────────

interface CalSyncStatus {
  running: boolean
  last_run: string | null
  last_result: Record<string, any> | null
  source: string | null
  next_run: string | null
}

function fmtAgo(iso: string | null): string | null {
  if (!iso) return null
  const ms = Date.now() - new Date(iso).getTime()
  const m = Math.floor(ms / 60000)
  if (m < 1) return 'just now'
  if (m < 60) return `${m}m ago`
  const h = Math.floor(m / 60)
  return `${h}h ${m % 60}m ago`
}

function fmtNext(iso: string | null): string | null {
  if (!iso) return null
  const ms = new Date(iso).getTime() - Date.now()
  if (ms <= 0) return 'imminent'
  const m = Math.floor(ms / 60000)
  if (m < 60) return `in ${m}m`
  const h = Math.floor(m / 60)
  return `in ${h}h ${m % 60}m`
}

function ImportPanel({ stats, onImported }: { stats: FFStats; onImported: () => void }) {
  const [importStatus, setImportStatus] = useState<ImportStatus>({ running: false, last_result: null })
  const pollRef = useRef<ReturnType<typeof setInterval> | null>(null)

  const [calStatus, setCalStatus] = useState<CalSyncStatus>({ running: false, last_run: null, last_result: null, source: null, next_run: null })
  const [syncMsg, setSyncMsg] = useState<string | null>(null)
  const [showSyncDebug, setShowSyncDebug] = useState(false)
  const calPollRef = useRef<ReturnType<typeof setInterval> | null>(null)

  const [ffPageMsg, setFfPageMsg] = useState<string | null>(null)
  const [ffPageBusy, setFfPageBusy] = useState(false)
  const ffPagePollRef = useRef<ReturnType<typeof setInterval> | null>(null)
  const fileInputId = useId()

  useEffect(() => {
    // Check startup auto-import status
    const pollImport = async () => {
      const r: ImportStatus = await fetch(`${API}/api/eco/ff-import/status`).then(x => x.json()).catch(() => ({ running: false, last_result: null }))
      setImportStatus(r)
      if (r.running) {
        pollRef.current = setInterval(async () => {
          const s: ImportStatus = await fetch(`${API}/api/eco/ff-import/status`).then(x => x.json()).catch(() => r)
          setImportStatus(s)
          if (!s.running) { clearInterval(pollRef.current!); onImported() }
        }, 2000)
      }
    }
    pollImport()

    // Load calendar sync status
    fetch(`${API}/api/eco/calendar-sync/status`).then(x => x.json()).then(setCalStatus).catch(() => {})

    return () => {
      if (pollRef.current) clearInterval(pollRef.current)
      if (calPollRef.current) clearInterval(calPollRef.current)
      if (ffPagePollRef.current) clearInterval(ffPagePollRef.current)
    }
  }, [onImported])

  const startCalendarSync = async () => {
    setSyncMsg('syncing…')
    setCalStatus(s => ({ ...s, running: true }))
    try {
      await fetch(`${API}/api/eco/calendar-sync?weeks=8`, { method: 'POST' })
      calPollRef.current = setInterval(async () => {
        const s: CalSyncStatus = await fetch(`${API}/api/eco/calendar-sync/status`).then(x => x.json())
        setCalStatus(s)
        if (!s.running) {
          clearInterval(calPollRef.current!)
          const r = s.last_result || {}
          if (r.error) {
            setSyncMsg(`Erreur: ${r.error}`)
          } else {
            const twStatus = r.live?.thisweek?.status
            const nwStatus = r.live?.nextweek?.status
            const tw = twStatus === 'ok' ? r.live.thisweek.events : (r.live?.thisweek?.error ? `err` : (twStatus ?? '?'))
            const nw = nwStatus === 'ok' ? r.live.nextweek.events : (r.live?.nextweek?.error ? `err` : (nwStatus ?? '?'))
            const sc = r.scrape?.total_upserted ?? 0
            let msg = r.scrape?.skipped === 'not_due'
              ? `✓ FF: ${tw}+${nw} live · scrape skipped (next in ~${r.scrape.next_due_in_h}h)`
              : `✓ FF: ${tw}+${nw} live, ${sc} upcoming`
            if (r.te_fallback) {
              if (r.te_fallback.skipped === 'no_key') msg += ' · TE fallback skipped (no key)'
              else if (r.te_fallback.error) msg += ` · TE error: ${r.te_fallback.error}`
              else msg += ` · TE +${r.te_fallback.total_upserted ?? 0}`
            }
            if (r.fmp_fallback) {
              if (r.fmp_fallback.skipped === 'no_key') msg += ' · FMP fallback skipped (no key)'
              else if (r.fmp_fallback.error) msg += ` · FMP error: ${r.fmp_fallback.error}`
              else msg += ` · FMP +${r.fmp_fallback.total_upserted ?? 0}`
            }
            setSyncMsg(msg)
          }
          onImported()
        }
      }, 2000)
    } catch { setSyncMsg('sync error'); setCalStatus(s => ({ ...s, running: false })) }
  }

  const uploadAndImportFfPage = async (file: File) => {
    setFfPageBusy(true)
    setFfPageMsg('uploading…')
    try {
      const form = new FormData()
      form.append('file', file)
      const up = await fetch(`${API}/api/eco/ff-page-upload`, { method: 'POST', body: form }).then(x => x.json())
      if (up.error) { setFfPageMsg(`Upload error: ${up.error}`); setFfPageBusy(false); return }
      setFfPageMsg(`uploaded (${up.size_mb} MB) — importing…`)
      await fetch(`${API}/api/eco/ff-page-import`, { method: 'POST' })
      ffPagePollRef.current = setInterval(async () => {
        const s = await fetch(`${API}/api/eco/ff-page-import/status`).then(x => x.json())
        if (!s.running) {
          clearInterval(ffPagePollRef.current!)
          setFfPageBusy(false)
          const r = s.last_result || {}
          if (r.error) { setFfPageMsg(`Error: ${r.error}`); return }
          setFfPageMsg(`✓ FF Page: ${r.inserted} events (${r.date_from} → ${r.date_to})`)
          onImported()
        }
      }, 2000)
    } catch { setFfPageBusy(false); setFfPageMsg('error') }
  }

  useEffect(() => () => { if (ffPagePollRef.current) clearInterval(ffPagePollRef.current) }, [])

  const hasData = stats.total > 0
  const res = importStatus.last_result
  const srcLabel = calStatus.source === 'ff' ? 'ForexFactory'
    : calStatus.source === 'ff+te' ? 'ForexFactory+TradingEconomics'
    : calStatus.source === 'ff+fmp' ? 'ForexFactory+FMP'
    : calStatus.source ?? null

  return (
    <div className="bg-slate-800 border border-slate-700 rounded-lg p-4 mb-4 flex flex-wrap items-center gap-4 text-sm">
      <div>
        <div className="text-slate-400 text-xs mb-0.5">FF Calendar DB</div>
        <div className="font-semibold">
          {importStatus.running
            ? <span className="text-yellow-400 animate-pulse">Importing CSV…</span>
            : hasData
              ? <><span className="text-emerald-400">{stats.total.toLocaleString()} events</span> <span className="text-slate-500 text-xs">({stats.earliest?.slice(0,7)} → {stats.latest?.slice(0,7)})</span></>
              : <span className="text-slate-400 text-xs">No data yet</span>
          }
        </div>
        {calStatus.last_run && (
          <div className="text-xs text-slate-500 mt-0.5">
            Last: {fmtAgo(calStatus.last_run)}{srcLabel ? ` via ${srcLabel}` : ''}
            {calStatus.next_run && <span className="ml-2">· Next: {fmtNext(calStatus.next_run)}</span>}
          </div>
        )}
      </div>

      {/* Unified sync button */}
      <button
        onClick={startCalendarSync}
        disabled={calStatus.running}
        className="px-3 py-1.5 rounded bg-indigo-700 hover:bg-indigo-600 disabled:opacity-50 flex items-center gap-1.5 text-white"
        title="Sync economic calendar (live JSON + HTML scraper for weeks 3+, always forced on manual sync regardless of its own throttle in Config)"
      >
        <RefreshCw size={13} className={clsx(calStatus.running && 'animate-spin')} />
        {calStatus.running ? 'Syncing…' : 'Sync Now'}
      </button>

      {/* FF Page import — save forexfactory.com/calendar as HTML (Ctrl+U) and upload */}
      <label
        htmlFor={fileInputId}
        className={clsx(
          'px-3 py-1.5 rounded flex items-center gap-1.5 text-white cursor-pointer',
          ffPageBusy
            ? 'bg-amber-800 opacity-50 cursor-not-allowed'
            : 'bg-amber-700 hover:bg-amber-600'
        )}
        title="Import Trading Economics HTML page (calendar_data.txt) — 1000 events with forecasts"
      >
        <Upload size={13} />
        Import FF Page
      </label>
      <input
        id={fileInputId}
        type="file"
        accept=".txt,.html"
        className="hidden"
        disabled={ffPageBusy}
        onChange={e => { const f = e.target.files?.[0]; if (f) uploadAndImportFfPage(f); e.target.value = '' }}
      />

      {res && !importStatus.running && (
        res.error
          ? <span className="text-red-400 text-xs">Import error: {res.error}</span>
          : <span className="text-emerald-400 text-xs flex items-center gap-1"><Check size={12}/> {res.inserted?.toLocaleString()} inserted</span>
      )}
      {syncMsg && (
        <span className={clsx('text-xs', syncMsg.startsWith('✓') ? 'text-emerald-400' : syncMsg.startsWith('Err') ? 'text-red-400' : 'text-yellow-400')}>
          {syncMsg}
        </span>
      )}
      {calStatus.last_result && (
        <button onClick={() => setShowSyncDebug(v => !v)} className="text-[10px] text-slate-500 hover:text-slate-300 underline">
          {showSyncDebug ? 'hide details' : 'details'}
        </button>
      )}
      {showSyncDebug && calStatus.last_result && (
        <pre className="basis-full text-[10px] text-slate-400 bg-dark-900 border border-slate-700/50 rounded p-2 overflow-x-auto max-h-64 overflow-y-auto">
          {JSON.stringify(calStatus.last_result, null, 2)}
        </pre>
      )}
      {ffPageMsg && (
        <span className={clsx('text-xs', ffPageMsg.startsWith('✓') ? 'text-emerald-400' : ffPageMsg.startsWith('Error') ? 'text-red-400' : 'text-yellow-400')}>
          {ffPageMsg}
        </span>
      )}
    </div>
  )
}

// ── Currency Filter ───────────────────────────────────────────────────────────

function CurrencyFilter({ selected, onChange }: { selected: string[]; onChange: (v: string[]) => void }) {
  const toggle = (c: string) =>
    onChange(selected.includes(c) ? selected.filter(x => x !== c) : [...selected, c])

  return (
    <div className="flex flex-wrap gap-1">
      {ALL_CURRENCIES.map(c => (
        <button
          key={c}
          onClick={() => toggle(c)}
          className={clsx(
            'px-2 py-0.5 rounded text-xs font-medium transition-colors',
            selected.includes(c)
              ? 'bg-blue-600 text-white'
              : 'bg-slate-700 text-slate-400 hover:bg-slate-600'
          )}
        >
          {CURRENCY_FLAGS[c]} {c}
        </button>
      ))}
      {selected.length > 0 && (
        <button onClick={() => onChange([])} className="px-2 py-0.5 rounded text-xs bg-slate-600 text-slate-300 hover:bg-slate-500">
          Clear
        </button>
      )}
    </div>
  )
}

// ── Impact Filter ─────────────────────────────────────────────────────────────

function ImpactFilter({ selected, onChange }: { selected: string[]; onChange: (v: string[]) => void }) {
  const opts = [
    { key: 'high',   label: 'High',   dot: 'bg-red-500' },
    { key: 'medium', label: 'Medium', dot: 'bg-yellow-400' },
    { key: 'low',    label: 'Low',    dot: 'bg-slate-400' },
  ]
  const toggle = (k: string) =>
    onChange(selected.includes(k) ? selected.filter(x => x !== k) : [...selected, k])

  return (
    <div className="flex gap-1">
      {opts.map(o => (
        <button
          key={o.key}
          onClick={() => toggle(o.key)}
          className={clsx(
            'px-2 py-0.5 rounded text-xs flex items-center gap-1 transition-colors',
            selected.includes(o.key)
              ? 'bg-slate-600 text-white'
              : 'bg-slate-700 text-slate-500 hover:bg-slate-600'
          )}
        >
          <span className={clsx('w-2 h-2 rounded-full', o.dot)} />
          {o.label}
        </button>
      ))}
    </div>
  )
}


// ── Main Page ─────────────────────────────────────────────────────────────────

export default function CalendarPage() {
  const [period, setPeriod]         = useState<string>('recent')
  const [customFrom, setCustomFrom] = useState<string>('')
  const [customTo, setCustomTo]     = useState<string>('')
  const [currencies, setCurrencies] = useState<string[]>(['USD'])
  const [impacts, setImpacts]       = useState<string[]>(['high', 'medium'])
  const [data, setData]             = useState<CalendarResponse | null>(null)
  const [loading, setLoading]       = useState(false)
  const [error, setError]           = useState<string | null>(null)
  const [stats, setStats]           = useState<FFStats>({ total: 0 })
  const [showFilters, setShowFilters] = useState(false)
  const autoRefRef = useRef<ReturnType<typeof setInterval> | null>(null)

  const fetchStats = useCallback(async () => {
    try {
      const r = await fetch(`${API}/api/eco/ff-stats`).then(x => x.json())
      setStats(r)
    } catch {}
  }, [])

  const fetchEvents = useCallback(async () => {
    // For custom period, require both dates
    if (period === 'custom' && (!customFrom || !customTo)) return
    setLoading(true)
    setError(null)
    try {
      const params = new URLSearchParams({ period, limit: '2000' })
      if (period === 'custom') {
        params.set('date_from', customFrom)
        params.set('date_to', customTo)
      }
      if (currencies.length > 0) params.set('currencies', currencies.join(','))
      if (impacts.length > 0)    params.set('impacts', impacts.join(','))
      const r: CalendarResponse = await fetch(`${API}/api/eco/calendar?${params}`).then(x => x.json())
      setData(r)
    } catch (e) {
      setError(String(e))
    } finally {
      setLoading(false)
    }
  }, [period, customFrom, customTo, currencies, impacts])

  useEffect(() => { fetchStats() }, [fetchStats])
  useEffect(() => { fetchEvents() }, [fetchEvents])

  // Auto-refresh every 60s for current/upcoming events
  useEffect(() => {
    if (autoRefRef.current) clearInterval(autoRefRef.current)
    if (['recent', 'today', 'this_week'].includes(period)) {
      autoRefRef.current = setInterval(() => {
        fetch(`${API}/api/eco/ff-sync`, { method: 'POST' }).catch(() => {})
        fetchEvents()
      }, 60_000)
    }
    return () => { if (autoRefRef.current) clearInterval(autoRefRef.current) }
  }, [period, fetchEvents])

  // Group events by date
  const grouped: { date: string; events: FFEvent[] }[] = []
  if (data?.events) {
    let cur: { date: string; events: FFEvent[] } | null = null
    for (const ev of data.events) {
      if (!cur || cur.date !== ev.event_date) {
        cur = { date: ev.event_date, events: [] }
        grouped.push(cur)
      }
      cur.events.push(ev)
    }
  }

  return (
    <div className="p-4 max-w-7xl mx-auto">
      {/* Header */}
      <div className="flex items-center justify-between mb-4">
        <div>
          <h1 className="text-xl font-bold text-white">Economic Calendar</h1>
          <p className="text-slate-400 text-xs mt-0.5">
            Forex Factory — past releases + upcoming events
          </p>
        </div>
        <div className="flex items-center gap-2">
          <button
            onClick={fetchEvents}
            disabled={loading}
            className="p-1.5 rounded bg-slate-700 hover:bg-slate-600 disabled:opacity-50"
            title="Refresh"
          >
            <RefreshCw size={14} className={clsx(loading && 'animate-spin')} />
          </button>
        </div>
      </div>

      {/* Import Panel */}
      <ImportPanel stats={stats} onImported={() => { fetchStats(); fetchEvents() }} />


      {/* Period Tabs */}
      <div className="flex flex-wrap gap-1 mb-3">
        {PERIODS.map(p => (
          <button
            key={p.key}
            onClick={() => setPeriod(p.key)}
            className={clsx(
              'px-3 py-1.5 rounded text-sm font-medium transition-colors',
              period === p.key
                ? 'bg-blue-600 text-white'
                : 'bg-slate-700 text-slate-300 hover:bg-slate-600'
            )}
          >
            {p.label}
          </button>
        ))}
      </div>

      {/* Custom date range picker */}
      {period === 'custom' && (
        <div className="flex flex-wrap items-center gap-3 mb-3 p-3 bg-slate-800 border border-slate-700 rounded-lg">
          <span className="text-slate-400 text-sm">From</span>
          <input
            type="date"
            value={customFrom}
            onChange={e => setCustomFrom(e.target.value)}
            className="bg-slate-700 border border-slate-600 rounded px-2 py-1 text-sm text-white focus:outline-none focus:border-blue-500"
          />
          <span className="text-slate-400 text-sm">To</span>
          <input
            type="date"
            value={customTo}
            onChange={e => setCustomTo(e.target.value)}
            className="bg-slate-700 border border-slate-600 rounded px-2 py-1 text-sm text-white focus:outline-none focus:border-blue-500"
          />
          <button
            onClick={fetchEvents}
            disabled={!customFrom || !customTo}
            className="px-3 py-1.5 rounded bg-blue-600 hover:bg-blue-500 disabled:opacity-40 text-sm text-white"
          >
            Apply
          </button>
          {customFrom && customTo && (
            <span className="text-slate-500 text-xs">
              {Math.round((new Date(customTo).getTime() - new Date(customFrom).getTime()) / 86400000) + 1} days
            </span>
          )}
        </div>
      )}

      {/* Filters toggle */}
      <div className="mb-3">
        <button
          onClick={() => setShowFilters(v => !v)}
          className="flex items-center gap-1 text-sm text-slate-400 hover:text-slate-200 mb-2"
        >
          <ChevronDown size={14} className={clsx('transition-transform', showFilters && 'rotate-180')} />
          Filters
        </button>
        {showFilters && (
          <div className="flex flex-wrap gap-4 p-3 bg-slate-800 rounded-lg border border-slate-700">
            <div>
              <div className="text-xs text-slate-500 mb-1">Currency</div>
              <CurrencyFilter selected={currencies} onChange={setCurrencies} />
            </div>
            <div>
              <div className="text-xs text-slate-500 mb-1">Impact</div>
              <ImpactFilter selected={impacts} onChange={setImpacts} />
            </div>
          </div>
        )}
      </div>

      {/* Summary bar */}
      {data && (
        <div className="text-xs text-slate-500 mb-2 flex items-center gap-3">
          <span>{data.total} events</span>
          {data.date_from && <span>{data.date_from} → {data.date_to}</span>}
          {['recent', 'today', 'this_week'].includes(period) && (
            <span className="text-sky-500">● auto-refresh 60s</span>
          )}
        </div>
      )}

      {/* Error */}
      {error && (
        <div className="flex items-center gap-2 p-3 bg-red-900/30 border border-red-700 rounded mb-3 text-red-400 text-sm">
          <AlertTriangle size={14} /> {error}
        </div>
      )}

      {/* Empty state */}
      {!loading && data?.total === 0 && stats.total === 0 && (
        <div className="text-center py-16 text-slate-500">
          <p className="text-lg mb-2">No data yet</p>
          <p className="text-sm">Click <strong className="text-white">Import CSV (2007-2025)</strong> above to load the Forex Factory historical calendar.</p>
        </div>
      )}

      {!loading && data?.total === 0 && stats.total > 0 && (
        <div className="text-center py-16 text-slate-500">
          <p>No events match your filters for this period.</p>
        </div>
      )}

      {/* Column header */}
      {grouped.length > 0 && (
        <div className="grid items-center gap-2 px-3 py-1.5 text-xs text-slate-500 border-b border-slate-700 mb-1 grid-cols-[60px_2rem_5rem_1fr_90px_90px_90px]">
          <span>Time (UTC)</span>
          <span></span>
          <span>Ccy</span>
          <span>Event</span>
          <span className="text-right">Actual</span>
          <span className="text-right">Forecast</span>
          <span className="text-right">Previous</span>
        </div>
      )}

      {/* Table */}
      {grouped.length > 0 && (
        <div className="space-y-4">
          {grouped.map(({ date, events }) => {
            const today = isToday(date)
            return (
              <div key={date}>
                {/* Date header */}
                <div className={clsx(
                  'sticky top-0 z-10 px-3 py-1.5 text-sm font-semibold border-b mb-0',
                  today
                    ? 'bg-blue-900/80 border-blue-700 text-blue-300'
                    : 'bg-slate-800/95 border-slate-700 text-slate-300'
                )}>
                  {formatDate(date)}
                  {today && <span className="ml-2 text-xs bg-blue-600 text-white px-1.5 py-0.5 rounded">TODAY</span>}
                </div>

                {/* Events for this date */}
                <div className="divide-y divide-slate-800">
                  {events.map(ev => {
                    const past = isPast(ev.event_date, ev.event_time)
                    const hasActual = !!ev.actual_value
                    const dir = direction(ev)
                    const actualClass = !hasActual
                      ? 'text-slate-400'
                      : dir === 1 ? 'text-emerald-400 font-semibold'
                      : dir === -1 ? 'text-red-400 font-semibold'
                      : 'text-slate-200'

                    return (
                      <div
                        key={ev.id}
                        className={clsx(
                          'grid items-center gap-2 px-3 py-2 text-sm hover:bg-slate-800/50 transition-colors',
                          'grid-cols-[60px_2rem_5rem_1fr_90px_90px_90px]',
                          !past && !hasActual && 'opacity-90'
                        )}
                      >
                        {/* Time */}
                        <span className="text-slate-400 tabular-nums text-xs">
                          {ev.event_time ?? '—'}
                        </span>

                        {/* Flag */}
                        <span className="text-base leading-none">
                          {CURRENCY_FLAGS[ev.currency] ?? ev.currency}
                        </span>

                        {/* Currency */}
                        <span className="text-slate-400 text-xs font-medium">{ev.currency}</span>

                        {/* Event name + impact */}
                        <div className="flex items-center gap-1.5 min-w-0">
                          <ImpactDot impact={ev.impact} />
                          <span className={clsx('truncate', hasActual ? 'text-slate-200' : 'text-slate-300')}>
                            {ev.event_name}
                          </span>
                        </div>

                        {/* Actual */}
                        <div className="text-right">
                          {hasActual ? (
                            <span className={actualClass}>
                              {ev.actual_value}
                              <SurpriseChip ev={ev} />
                            </span>
                          ) : (
                            <span className="text-slate-600 text-xs">—</span>
                          )}
                        </div>

                        {/* Forecast (consensus) */}
                        <div className="text-right">
                          <ValueCell value={ev.forecast_value} className="text-slate-300" />
                        </div>

                        {/* Previous */}
                        <div className="text-right">
                          <ValueCell value={ev.previous_value} className="text-slate-500" />
                        </div>
                      </div>
                    )
                  })}
                </div>
              </div>
            )
          })}
        </div>
      )}

    </div>
  )
}
