import { lazy, Suspense, useState, type ReactNode } from 'react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { PageHeader } from '../components/ui/PageHeader';
import { Callout } from '../components/ui/Callout';
import { Disclosure } from '../components/ui/Disclosure';
import { Badge } from '../components/ui/Badge';
import { SkeletonCard, SkeletonTable } from '../components/ui/Skeleton';
import { useAuthStore } from '../stores/authStore';
import {
  getEventStudy,
  getRegimeConfig,
  getRegimeFundamentals,
  getRegimeMonitor,
  refreshRegimeFundamentals,
  updateRegimeConfig,
  updateRegimeFundamentals,
} from '../api/regime';
import type {
  CapexState,
  EventStudyReport,
  GoodNewsReaction,
  RegimeBand,
  RegimeConfig,
  RegimeFundamentalOverlay,
  RegimeFundamentals,
  RegimeFundamentalsUpdate,
  RegimeMonitor,
  RegimeReading,
} from '../lib/types';

const RegimeChart = lazy(() => import('../components/regime/RegimeChart'));

const BAND_STYLES: Record<RegimeBand, { text: string; bar: string; ring: string; label: string }> = {
  stable: { text: 'text-emerald-400', bar: 'bg-emerald-400', ring: 'border-emerald-400/30', label: 'Stable' },
  watch: { text: 'text-amber-400', bar: 'bg-amber-400', ring: 'border-amber-400/30', label: 'Watch' },
  elevated: { text: 'text-orange-400', bar: 'bg-orange-400', ring: 'border-orange-400/30', label: 'Elevated' },
  breaking: { text: 'text-red-400', bar: 'bg-red-400', ring: 'border-red-400/30', label: 'High stress' },
};

function TrendChip({ label, delta }: { label: string; delta: number | null | undefined }) {
  if (delta == null) {
    return <span className="rounded-lg bg-white/[0.04] px-2.5 py-1 text-xs text-gray-500">{label}: n/a</span>;
  }
  const color = delta === 0 ? 'text-gray-400' : delta > 0 ? 'text-red-400' : 'text-emerald-400';
  const arrow = delta === 0 ? '→' : delta > 0 ? '↑' : '↓';
  return (
    <span className="rounded-lg bg-white/[0.04] px-2.5 py-1 text-xs text-gray-400">
      {label}: <span className={`font-medium ${color}`}>{arrow} {delta > 0 ? '+' : ''}{delta}</span>
    </span>
  );
}

function ScoreGauge({
  label,
  reading,
  footnote,
}: {
  label: string;
  reading: RegimeReading | undefined;
  footnote: ReactNode;
}) {
  const score = reading?.score;
  const complete = reading?.band != null;
  const style = complete ? BAND_STYLES[reading.band as RegimeBand] : null;
  const position = Math.min(100, Math.max(0, score ?? 0));
  const bands = reading?.bands;
  // No fallback ticks: the two axes have different thresholds, so guessing a
  // shared set would mislabel one of them. Render none rather than wrong ones.
  const ticks = bands ? [bands.watch, bands.elevated, bands.breaking] : [];
  return (
    <div className={`glass border p-6 ${style?.ring ?? 'border-white/[0.06]'}`}>
      <div className="flex flex-wrap items-end justify-between gap-3">
        <div>
          <div className="text-[11px] uppercase tracking-wider text-gray-500">{label}</div>
          <div className="mt-1 flex items-baseline gap-2">
            <span className={`font-display text-6xl font-bold ${style?.text ?? 'text-gray-500'}`}>
              {score == null ? '—' : Math.round(score)}
            </span>
            {score != null && <span className="text-sm text-gray-500">/ 100</span>}
          </div>
          <div className="mt-1 flex flex-wrap items-center gap-2">
            <span className={`text-sm font-medium ${style?.text ?? 'text-gray-500'}`}>
              {style?.label ?? 'Incomplete'}
            </span>
            <span className="text-xs text-gray-600">coverage {Math.round(reading?.coverage ?? 0)}%</span>
          </div>
        </div>
        <div className="flex gap-2">
          <TrendChip label="7d" delta={reading?.trend?.delta_7} />
          <TrendChip label="30d" delta={reading?.trend?.delta_30} />
        </div>
      </div>
      {score != null && (
        <>
          {/* The quadrant divider is each axis's watch/elevated boundary, so it
              is already the middle tick below — drawing it again was two marks
              for one threshold. */}
          <div className="relative mt-5 h-2 rounded-full bg-gradient-to-r from-emerald-500/30 via-amber-500/30 to-red-500/40">
            <div
              className={`absolute -top-1.5 h-5 w-5 -translate-x-1/2 rounded-full border-2 border-white/70 ${style?.bar ?? 'bg-gray-500'}`}
              style={{ left: `${position}%` }}
            />
          </div>
          {/* Thresholds come from the reading: the two axes no longer share them. */}
          <div className="relative mt-1.5 h-4 text-[10px] uppercase tracking-wider text-gray-600">
            <span className="absolute left-0">0</span>
            {ticks.map((tick) => (
              <span key={tick} className="absolute -translate-x-1/2 num" style={{ left: `${tick}%` }}>
                {tick}
              </span>
            ))}
            <span className="absolute right-0">100</span>
          </div>
        </>
      )}
      <p className="mt-4 text-xs text-gray-500">{footnote}</p>
    </div>
  );
}

const CAPEX_TONE: Record<CapexState, string> = {
  raising: 'text-emerald-400',
  holding: 'text-amber-400',
  cutting: 'text-red-400',
  unknown: 'text-gray-500',
};

const OVERLAY_TITLE = 'Fundamental overlay · context, not scored';

function FundamentalOverlayCard({ overlay }: { overlay: RegimeFundamentalOverlay }) {
  const capex = overlay.capex ?? {};
  const reaction = overlay.good_news_stock_down;

  // Nothing collected: the stored default is "unknown" for every hyperscaler
  // and "mixed" for the reaction, which are placeholders, not a reading.
  if (overlay.observed === false) {
    return (
      <div className="glass border border-white/[0.06] p-5">
        <div className="text-[11px] uppercase tracking-wider text-gray-500">{OVERLAY_TITLE}</div>
        <p className="mt-3 text-xs text-gray-500">
          No observation collected yet. An admin can collect one under Admin · Monitor settings. It is
          context only — it never enters State or Warning.
        </p>
      </div>
    );
  }

  return (
    <div className="glass border border-white/[0.06] p-5">
      <div className="flex flex-wrap items-baseline justify-between gap-2">
        <div className="text-[11px] uppercase tracking-wider text-gray-500">{OVERLAY_TITLE}</div>
        <div className="flex flex-wrap items-center gap-2 text-[11px] text-gray-500">
          {overlay.source && <span>{overlay.source}</span>}
          {/* When pending, the line below is the single carrier of this date. */}
          {overlay.effective_date && !overlay.pending && <span>· effective {overlay.effective_date}</span>}
          {overlay.pending && <Badge label="pending" variant="manual" />}
          {overlay.stale && <Badge label="stale" variant="manual" />}
        </div>
      </div>

      {/* A pending observation is still shown — it is the freshest read we
          have, and nothing here is scored. The date says when the stored
          point-in-time record picks it up. */}
      {overlay.pending && (
        <p className="mt-3 text-xs text-amber-400/90">
          Shown as collected. The point-in-time record picks it up{' '}
          {overlay.effective_date ?? 'next session'} — observations are never backdated.
        </p>
      )}
      <div className="mt-4 grid gap-4 sm:grid-cols-2">
        <div>
          <div className="mb-2 flex items-baseline justify-between text-xs">
            <span className="font-medium text-gray-300">Hyperscaler capex guidance</span>
            <span className="num text-gray-500">{overlay.capex_stress ?? 'n/a'}</span>
          </div>
          <div className="space-y-1">
            {Object.entries(capex).map(([symbol, state]) => (
              <div key={symbol} className="flex items-center justify-between text-xs">
                <span className="font-mono text-gray-400">{symbol}</span>
                <span className={CAPEX_TONE[state] ?? 'text-gray-500'}>{state}</span>
              </div>
            ))}
          </div>
        </div>
        <div>
          <div className="mb-2 flex items-baseline justify-between text-xs">
            <span className="font-medium text-gray-300">Good news, stock down</span>
            <span className="num text-gray-500">{overlay.earnings_stress ?? 'n/a'}</span>
          </div>
          <div className={`text-sm font-medium ${reaction === 'yes' ? 'text-red-400' : reaction === 'no' ? 'text-emerald-400' : 'text-gray-500'}`}>
            {reaction === 'yes' ? 'Yes — beats sold into' : reaction === 'no' ? 'No — ordinary reactions' : 'Mixed'}
          </div>
        </div>
      </div>
      {overlay.reasoning && <p className="mt-4 text-xs leading-relaxed text-gray-400">{overlay.reasoning}</p>}
    </div>
  );
}

/** One table for both axes — they share a shape, and two panels invited
 *  comparing numbers that are not on the same scale. */
function PillarTable({ state, warning }: { state: RegimeReading; warning: RegimeReading }) {
  const groups: { title: string; reading: RegimeReading }[] = [
    { title: 'State', reading: state },
    { title: 'Warning', reading: warning },
  ];
  return (
    <Disclosure summary="Pillars & sensors · what drives each score">
      <div className="overflow-x-auto rounded-lg border border-white/[0.06]">
        <table className="w-full text-sm">
          <thead>
            <tr className="border-b border-white/[0.06] text-left text-xs uppercase tracking-wider text-gray-500">
              <th className="px-4 py-3 font-medium">Pillar / sensor</th>
              <th className="px-4 py-3 text-right font-medium">Score</th>
              <th className="px-4 py-3 text-right font-medium">Weight</th>
              <th className="px-4 py-3 text-right font-medium">Contribution</th>
            </tr>
          </thead>
          {groups.map(({ title, reading }) => (
            <tbody key={title}>
              <tr className="border-b border-white/[0.06] bg-white/[0.02]">
                <td colSpan={4} className="px-4 py-2 text-[11px] uppercase tracking-wider text-gray-400">
                  {title}
                  <span className="ml-2 normal-case tracking-normal text-gray-600">
                    {reading.score ?? '—'} · {Math.round(reading.coverage)}% coverage
                  </span>
                </td>
              </tr>
              {reading.pillars.map((pillar) => (
                <tr key={pillar.id} className="border-b border-white/[0.04] align-top last:border-0">
                  <td className="px-4 py-3">
                    <div className="font-medium text-gray-200">{pillar.label}</div>
                    <div className="mt-1 space-y-0.5">
                      {pillar.sensors.map((sensor) => (
                        <div key={sensor.id} className="text-xs text-gray-500">
                          <span className="font-mono text-gray-600">{sensor.id}</span> {sensor.label}:{' '}
                          <span className="num text-gray-400">{sensor.score == null ? 'n/a' : sensor.score}</span>
                        </div>
                      ))}
                    </div>
                  </td>
                  <td className="px-4 py-3 text-right num text-gray-300">{pillar.score ?? '—'}</td>
                  <td className="px-4 py-3 text-right num text-gray-400">{pillar.weight}</td>
                  <td className="px-4 py-3 text-right num text-gray-300">
                    {pillar.available ? pillar.contribution.toFixed(1) : '—'}
                  </td>
                </tr>
              ))}
            </tbody>
          ))}
        </table>
      </div>
    </Disclosure>
  );
}

function MetaChip({ label, value, title }: { label: string; value: ReactNode; title?: string }) {
  return (
    <span className="rounded-lg bg-white/[0.03] px-2.5 py-1 text-[11px] text-gray-500" title={title}>
      {label} <span className="num text-gray-400">{value}</span>
    </span>
  );
}

/** Provenance strip — replaces three separate prose blocks. */
function MetaStrip({ data }: { data: RegimeMonitor }) {
  const quality = data.data_quality;
  const basket = data.basket;
  const days = (value: number | null | undefined) => (value == null ? '—' : `${value}d`);
  return (
    <div className="flex flex-wrap items-center gap-2">
      <MetaChip label="as of" value={data.date ?? '—'} />
      <MetaChip label="oldest input" value={days(quality?.oldest_market_input_age_days)} />
      {basket && (
        <MetaChip
          label="basket"
          value={`${basket.members_available ?? '—'}/${basket.members_expected} · frozen ${basket.basket_asof}`}
          title={`hash ${basket.hash}`}
        />
      )}
      <MetaChip
        label="credit history"
        value={days(quality?.credit_history_days)}
        title="Upstream span actually available. ICE caps the HY OAS series at 3 rolling years."
      />
      <MetaChip label="VIX history" value={days(quality?.vix_history_days)} />
    </div>
  );
}

function EventStudyBody({ report }: { report: EventStudyReport }) {
  const metrics = report.metrics;
  return (
    <div className="space-y-4">
      <div className="flex flex-wrap items-center gap-2">
        <Badge label={report.evaluation ?? 'exploratory'} variant={report.evaluation === 'holdout' ? 'auto' : 'manual'} />
        {report.generated_at && <span className="text-xs text-gray-500">generated {new Date(report.generated_at).toLocaleDateString()}</span>}
        {report.sample && <span className="text-xs text-gray-500">test {report.sample.test_start} → {report.sample.end}</span>}
      </div>
      <p className="text-sm leading-relaxed text-gray-300">{report.summary}</p>
      {metrics && (
        <div className="grid grid-cols-2 gap-2 sm:grid-cols-4">
          {[
            ['Warned', `${metrics.events_warned}/${metrics.events}`],
            ['Missed', metrics.events_missed],
            ['False alarms/year', metrics.false_alarms_per_year.toFixed(1)],
            ['Median lead', metrics.median_lead_days == null ? '—' : `${metrics.median_lead_days}d`],
          ].map(([label, value]) => (
            <div key={String(label)} className="rounded-lg border border-white/[0.06] bg-white/[0.02] px-3 py-2">
              <div className="text-[11px] text-gray-500">{label}</div>
              <div className="mt-0.5 text-lg font-semibold text-gray-200">{value}</div>
            </div>
          ))}
        </div>
      )}
      {report.events && report.events.length > 0 && (
        <div className="overflow-x-auto rounded-lg border border-white/[0.06]">
          <table className="w-full text-xs">
            <thead><tr className="border-b border-white/[0.06] text-left text-gray-500">
              <th className="px-3 py-2 font-medium">Correction</th>
              <th className="px-3 py-2 text-right font-medium">Warned</th>
              <th className="px-3 py-2 text-right font-medium">Lead</th>
            </tr></thead>
            <tbody>{report.events.map((event) => (
              <tr key={event.date} className="border-b border-white/[0.03] last:border-0">
                <td className="px-3 py-2 num text-gray-300">{event.date}</td>
                <td className={`px-3 py-2 text-right ${event.warned ? 'text-emerald-400' : 'text-gray-500'}`}>{event.warned ? 'yes' : 'no'}</td>
                <td className="px-3 py-2 text-right num text-gray-300">{event.lead_days == null ? '—' : `${event.lead_days}d`}</td>
              </tr>
            ))}</tbody>
          </table>
        </div>
      )}
      {report.reliability && (report.reliability.underpowered || report.reliability.sensor_coverage_mismatch) && (
        <Callout variant="warning">
          <div className="space-y-1.5">
            {report.reliability.underpowered && (
              <p>
                <strong>Underpowered.</strong> Only {report.reliability.events_in_holdout} of{' '}
                {report.reliability.events_detected} detected corrections fall in the test period (
                {report.reliability.minimum_events}+ needed). Read the direction, not the ratio.
              </p>
            )}
            {report.reliability.sensor_coverage_mismatch && (
              <p>
                <strong>Sensor coverage differs across the split.</strong>{' '}
                {report.reliability.train_full_sensor_share}% of training sessions had all{' '}
                {report.reliability.sensors_expected} Warning sensors versus{' '}
                {report.reliability.holdout_full_sensor_share}% of test sessions
                {report.params?.credit_sensor_from && ` — credit history begins ${report.params.credit_sensor_from}`}
                . The threshold was frozen on a partly different construct than it is measured against.
              </p>
            )}
          </div>
        </Callout>
      )}
    </div>
  );
}

function EventStudyPanel() {
  const study = useQuery({ queryKey: ['regime', 'event-study'], queryFn: getEventStudy });
  return (
    <Disclosure summary="Warning study · chronological correction alarms">
      {study.isLoading && <SkeletonCard className="h-24" />}
      {study.data === null && <Callout variant="empty">Not run yet — trigger “Event Study” in Admin → Jobs.</Callout>}
      {study.data && !study.data.available && <Callout variant="warning">{study.data.reason ?? 'No data'}</Callout>}
      {study.data?.available && <EventStudyBody report={study.data} />}
    </Disclosure>
  );
}

const SELECT_CLASS = 'rounded-md border border-white/[0.08] bg-white/[0.03] px-2 py-1.5 text-xs text-gray-200';

const CAPEX_OPTIONS: { value: CapexState; label: string }[] = [
  { value: 'raising', label: 'Raising' },
  { value: 'holding', label: 'Holding' },
  { value: 'cutting', label: 'Cutting' },
  { value: 'unknown', label: 'Unknown' },
];

function FundamentalsEditor({
  data,
  onSave,
  onRefresh,
  saving,
  refreshing,
}: {
  data: RegimeFundamentals;
  onSave: (body: RegimeFundamentalsUpdate) => void;
  onRefresh: () => void;
  saving: boolean;
  refreshing: boolean;
}) {
  const [capex, setCapex] = useState<Record<string, CapexState>>(() => ({ ...data.capex }));
  const [reaction, setReaction] = useState<GoodNewsReaction>(data.good_news_stock_down);
  const knownCapex = Object.values(capex).filter((state) => state !== 'unknown');
  // Mirrors _CAPEX_STATE_SCORES: raising 0, holding 50, cutting 100. Holding is
  // the deceleration case and used to score identically to raising.
  const capexPoints = knownCapex.reduce((sum, state) => sum + (state === 'cutting' ? 100 : state === 'holding' ? 50 : 0), 0);
  const derivedF1 = knownCapex.length >= 3 ? Math.round((capexPoints / knownCapex.length) * 10) / 10 : null;
  const derivedF3 = reaction === 'yes' ? 100 : reaction === 'no' ? 0 : null;
  return (
    <div className="space-y-4">
      <div className="flex flex-wrap items-center gap-2 text-xs text-gray-500">
        <span>Source: {data.source}</span>
        {data.fetched_at && <span>· fetched {new Date(data.fetched_at).toLocaleDateString()}</span>}
        {data.effective_date && <span>· effective {data.effective_date}</span>}
        {data.locked && <Badge label="locked" variant="manual" />}
      </div>
      {data.reasoning && <p className="text-xs leading-relaxed text-gray-400">{data.reasoning}</p>}
      <div>
        <div className="mb-2 flex items-center justify-between gap-3 text-xs">
          <span className="font-medium text-gray-300">F1 · Capex guidance by hyperscaler</span>
          <span className="num text-gray-500">score {derivedF1 ?? 'n/a'}</span>
        </div>
        <div className="grid grid-cols-2 gap-2">
          {Object.entries(capex).map(([symbol, state]) => (
            <label key={symbol} className="flex items-center justify-between gap-2 rounded-md bg-white/[0.02] px-2.5 py-2 text-xs text-gray-400">
              <span className="font-mono text-gray-300">{symbol}</span>
              <select
                className={SELECT_CLASS}
                value={state}
                onChange={(event) => setCapex((current) => ({ ...current, [symbol]: event.target.value as CapexState }))}
              >
                {CAPEX_OPTIONS.map((option) => <option key={option.value} value={option.value}>{option.label}</option>)}
              </select>
            </label>
          ))}
        </div>
        <p className="mt-1.5 text-[11px] text-gray-600">Raising = 0, holding = 50, cutting = 100; at least three known names required.</p>
      </div>
      <label className="flex items-center justify-between gap-3 text-xs text-gray-400">
        <span>
          <span className="font-medium text-gray-300">F3 · Good news, stock down</span>
          <span className="ml-2 num text-gray-600">score {derivedF3 ?? 'n/a'}</span>
        </span>
        <select className={SELECT_CLASS} value={reaction} onChange={(event) => setReaction(event.target.value as GoodNewsReaction)}>
          <option value="yes">Yes · stress</option>
          <option value="no">No · ordinary</option>
          <option value="mixed">Mixed · unavailable</option>
        </select>
      </label>
      <div className="flex flex-wrap gap-2">
        <button className="btn-primary px-3 py-1.5 text-sm disabled:opacity-50" disabled={saving} onClick={() => onSave({ capex, good_news_stock_down: reaction, locked: true })}>Save observations</button>
        <button className="rounded-lg px-3 py-1.5 text-sm text-gray-400 hover:bg-white/[0.04] disabled:opacity-50" disabled={refreshing} onClick={onRefresh}>{refreshing ? 'Refreshing…' : 'Refresh via LLM'}</button>
        {data.locked && <button className="rounded-lg px-3 py-1.5 text-sm text-gray-400 hover:bg-white/[0.04]" onClick={() => onSave({ locked: false })}>Unlock</button>}
      </div>
    </div>
  );
}

function ConfigEditor({ data, onSave, saving }: { data: RegimeConfig; onSave: (updates: Partial<RegimeConfig>) => void; saving: boolean }) {
  const [basket, setBasket] = useState(data.breadth_basket.join(', '));
  const [staleness, setStaleness] = useState(data.fundamental_staleness_days);
  const symbols = basket.split(/[\s,]+/).map((symbol) => symbol.trim().toUpperCase()).filter(Boolean);
  return (
    <div className="space-y-3">
      <label className="block text-xs text-gray-400">
        <span>Fixed breadth basket · {symbols.length} symbols</span>
        <textarea value={basket} onChange={(event) => setBasket(event.target.value)} rows={5} className="mt-1 w-full rounded-lg border border-white/[0.08] bg-white/[0.03] p-2 font-mono text-xs text-gray-200" />
      </label>
      <label className="flex items-center gap-2 text-xs text-gray-400">
        <span>Fundamental staleness</span>
        <input type="number" min={30} max={180} value={staleness} onChange={(event) => setStaleness(Number(event.target.value))} className="w-20 rounded-md border border-white/[0.08] bg-white/[0.03] px-2 py-1 text-right num text-gray-200" />
        <span>days</span>
      </label>
      <p className="text-[11px] text-gray-600">Changing the basket resets its freeze date and silently reseeds quadrant alerts.</p>
      <button className="btn-primary px-3 py-1.5 text-sm disabled:opacity-50" disabled={saving || symbols.length < 20} onClick={() => onSave({ breadth_basket: symbols, fundamental_staleness_days: staleness })}>Save basket &amp; freshness</button>
    </div>
  );
}

function AdminControls() {
  const queryClient = useQueryClient();
  const fundamentals = useQuery({ queryKey: ['regime', 'fundamentals'], queryFn: getRegimeFundamentals });
  const config = useQuery({ queryKey: ['regime', 'config'], queryFn: getRegimeConfig });
  const invalidate = () => queryClient.invalidateQueries({ queryKey: ['regime'] });
  const refresh = useMutation({ mutationFn: refreshRegimeFundamentals, onSuccess: invalidate });
  const saveFundamentals = useMutation({ mutationFn: updateRegimeFundamentals, onSuccess: invalidate });
  const saveConfig = useMutation({ mutationFn: updateRegimeConfig, onSuccess: invalidate });
  return (
    <Disclosure summary="Admin · Monitor settings">
      <div className="grid gap-5 xl:grid-cols-2 xl:gap-6">
        <section className="border-b border-white/[0.06] pb-5 xl:border-b-0 xl:border-r xl:pb-0 xl:pr-6">
          <div className="mb-3 text-[11px] uppercase tracking-wider text-gray-500">Fundamental observations</div>
          {fundamentals.isLoading && <SkeletonCard className="h-36" />}
          {fundamentals.data && <FundamentalsEditor key={fundamentals.dataUpdatedAt} data={fundamentals.data} onSave={(body) => saveFundamentals.mutate(body)} onRefresh={() => refresh.mutate()} saving={saveFundamentals.isPending} refreshing={refresh.isPending} />}
          {refresh.isError && <Callout variant="error">Refresh failed: {(refresh.error as Error).message}</Callout>}
        </section>
        <section>
          <div className="mb-3 text-[11px] uppercase tracking-wider text-gray-500">Fixed basket &amp; freshness</div>
          {config.isLoading && <SkeletonCard className="h-36" />}
          {config.data && <ConfigEditor key={config.dataUpdatedAt} data={config.data} onSave={(updates) => saveConfig.mutate(updates)} saving={saveConfig.isPending} />}
          {saveConfig.isError && <Callout variant="error">Save failed: {(saveConfig.error as Error).message}</Callout>}
        </section>
      </div>
    </Disclosure>
  );
}

export default function RegimePage() {
  const isAdmin = useAuthStore((state) => state.role) === 'admin';
  const monitor = useQuery({ queryKey: ['regime', 'monitor'], queryFn: getRegimeMonitor });
  const data = monitor.data;
  const inputs = data?.inputs;
  return (
    <div className="space-y-6 animate-slide-up">
      <PageHeader
        title="AI/Tech Risk Monitor"
        subtitle="AI/Tech risk thermometer — observational only, feeds no entry, exit, or sizing decision"
      />

      {monitor.isLoading && <><SkeletonCard className="h-44" /><SkeletonTable rows={6} cols={4} /></>}
      {monitor.isError && <Callout variant="error" onRetry={() => monitor.refetch()}>Failed to load: {(monitor.error as Error).message}</Callout>}
      {data && !data.available && <Callout variant="empty">Not computed yet — run “AI/Tech Risk Monitor” from Admin → Jobs or wait for the daily pipeline.</Callout>}

      {data?.available && data.state && data.warning && (
        <>
          {(!data.data_quality?.is_fresh || data.state.band == null || data.warning.band == null) && (
            <Callout variant="warning">
              Reading is incomplete or stale. State coverage {Math.round(data.state.coverage)}%, Warning coverage {Math.round(data.warning.coverage)}%
              {data.data_quality?.stale_inputs?.length ? ` · stale: ${data.data_quality.stale_inputs.join(', ')}` : ''}.
            </Callout>
          )}

          <div className="grid gap-4 lg:grid-cols-2">
            <ScoreGauge
              label="State · stress right now"
              reading={data.state}
              footnote={
                <>
                  Price, breadth, credit and volatility levels · VIX{' '}
                  <span className="num text-gray-400">{inputs?.vix ?? '—'}</span> · HY OAS{' '}
                  <span className="num text-gray-400">{inputs?.hy_oas ?? '—'}</span> · breadth{' '}
                  <span className="num text-gray-400">
                    {inputs?.breadth_pct_above_200 == null ? '—' : `${inputs.breadth_pct_above_200}%`}
                  </span>
                </>
              }
            />
            <ScoreGauge
              label="Warning · deterioration & divergence"
              reading={data.warning}
              footnote="Breadth divergence, SMH/SPY rollover, and HY credit impulse. Missing sensors reduce coverage; they never default to 50."
            />
          </div>

          <Suspense fallback={<SkeletonCard className="h-80" />}><RegimeChart /></Suspense>

          <PillarTable state={data.state} warning={data.warning} />

          {data.fundamental_context && <FundamentalOverlayCard overlay={data.fundamental_context} />}

          <MetaStrip data={data} />
        </>
      )}

      <EventStudyPanel />
      {isAdmin && <AdminControls />}
    </div>
  );
}
