import { useMemo, useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import {
  CartesianGrid,
  Cell,
  Line,
  LineChart,
  ReferenceArea,
  ReferenceLine,
  ResponsiveContainer,
  Scatter,
  ScatterChart,
  Tooltip,
  XAxis,
  YAxis,
  ZAxis,
} from 'recharts';
import { getRegimeHistory, getRegimeMonitor } from '../../api/regime';
import { Callout } from '../ui/Callout';
import { SkeletonCard } from '../ui/Skeleton';
import { formatDate } from '../../lib/format';

// Lazy-loaded (see RegimePage) so recharts stays in the regime-tab chunk.
// Time and Path are two projections of one series, so they share a card and a
// query rather than sitting in two panels that look like different data.

const VIEWS = ['Time', 'Path'] as const;
type View = (typeof VIEWS)[number];

const RANGES = [
  { key: '1M', days: 30 },
  { key: '3M', days: 90 },
  { key: '6M', days: 182 },
  { key: 'All', days: Number.POSITIVE_INFINITY },
] as const;
type RangeKey = (typeof RANGES)[number]['key'];

/** Sessions drawn in Path view. The full series is unreadable as a path. */
const PATH_TRAIL = 60;

const STATE_COLOR = '#60a5fa';
const WARNING_COLOR = '#fb923c';

// Fall back to the v3 constants, not v2's shared 60/60, so a missing
// quadrant_config cannot draw dividers that disagree with the alert path.
const DEFAULT_STATE_DIVIDER = 50;
const DEFAULT_WARNING_DIVIDER = 40;

interface PathPoint {
  x: number;
  y: number;
  date: string;
}

/** Centered moving average to de-noise the path; today (last) kept exact. */
function smoothTrail(points: PathPoint[], half = 2): PathPoint[] {
  const n = points.length;
  return points.map((p, i) => {
    if (i === n - 1) return { ...p };
    let sx = 0;
    let sy = 0;
    let c = 0;
    for (let j = Math.max(0, i - half); j <= Math.min(n - 1, i + half); j++) {
      sx += points[j].x;
      sy += points[j].y;
      c += 1;
    }
    return { x: sx / c, y: sy / c, date: p.date };
  });
}

/** Recency gradient: 0 = oldest (muted slate), 1 = newest (bright blue). */
function recencyColor(t: number): string {
  const lerp = (a: number, b: number) => Math.round(a + (b - a) * t);
  return `rgba(${lerp(71, 96)}, ${lerp(85, 165)}, ${lerp(105, 250)}, ${(0.3 + 0.7 * t).toFixed(2)})`;
}

function SegmentedControl<T extends string>({
  options,
  value,
  onChange,
  label,
}: {
  options: readonly T[];
  value: T;
  onChange: (next: T) => void;
  label: string;
}) {
  return (
    <div className="flex gap-1" role="group" aria-label={label}>
      {options.map((option) => (
        <button
          key={option}
          type="button"
          aria-pressed={value === option}
          onClick={() => onChange(option)}
          className={`rounded px-2 py-1 text-[11px] font-medium tabular-nums transition-colors ${
            value === option ? 'bg-white/10 text-blue-300' : 'text-gray-500 hover:text-gray-300'
          }`}
        >
          {option}
        </button>
      ))}
    </div>
  );
}

function PathTip({ active, payload }: { active?: boolean; payload?: { payload: PathPoint }[] }) {
  if (!active || !payload?.length) return null;
  const p = payload[0].payload;
  return (
    <div className="glass px-2.5 py-1.5 text-[11px]">
      <div className="text-gray-300">{formatDate(p.date)}</div>
      <div className="text-gray-400">
        State <span style={{ color: STATE_COLOR }}>{Math.round(p.x)}</span> · Warning{' '}
        <span style={{ color: WARNING_COLOR }}>{Math.round(p.y)}</span>
      </div>
    </div>
  );
}

export default function RegimeChart() {
  const [view, setView] = useState<View>('Time');
  const [range, setRange] = useState<RangeKey>('3M');
  const history = useQuery({ queryKey: ['regime', 'history'], queryFn: () => getRegimeHistory(800) });
  const monitor = useQuery({ queryKey: ['regime', 'monitor'], queryFn: getRegimeMonitor });

  const xDiv = monitor.data?.quadrant_config?.state_divider ?? DEFAULT_STATE_DIVIDER;
  const yDiv = monitor.data?.quadrant_config?.warning_divider ?? DEFAULT_WARNING_DIVIDER;
  const basketAsOf = monitor.data?.basket?.basket_asof;

  const series = useMemo(() => {
    const data = history.data ?? [];
    if (view === 'Path') {
      return data
        .filter((p) => p.state != null && p.warning != null)
        .slice(-PATH_TRAIL);
    }
    const days = RANGES.find((r) => r.key === range)!.days;
    if (!Number.isFinite(days)) return data;
    const cutoff = new Date();
    cutoff.setDate(cutoff.getDate() - days);
    return data.filter((p) => new Date(p.date) >= cutoff);
  }, [history.data, view, range]);

  const pathPoints = useMemo<PathPoint[]>(
    () => series.map((p) => ({ x: p.state as number, y: p.warning as number, date: p.date })),
    [series],
  );
  const trail = useMemo(() => (view === 'Path' ? smoothTrail(pathPoints) : []), [pathPoints, view]);
  const latest = view === 'Path' && pathPoints.length ? pathPoints[pathPoints.length - 1] : null;

  // Only warn about pre-freeze history when the drawn window actually reaches
  // back past the freeze date.
  const crossesFreeze = Boolean(basketAsOf && series.length && series[0].date < basketAsOf);
  const enoughData = view === 'Path' ? pathPoints.length > 0 : series.length >= 2;

  return (
    <div className="glass p-5">
      <div className="flex flex-wrap items-center justify-between gap-3">
        <div className="flex items-center gap-3">
          <span className="text-[11px] uppercase tracking-wider text-gray-500">
            {view === 'Time' ? 'State & Warning over time' : `State × Warning path · last ${PATH_TRAIL} sessions`}
          </span>
          <SegmentedControl options={VIEWS} value={view} onChange={setView} label="Chart view" />
        </div>
        {view === 'Time' ? (
          <SegmentedControl options={RANGES.map((r) => r.key)} value={range} onChange={setRange} label="Time range" />
        ) : (
          latest && (
            <span className="text-[11px] text-gray-500">
              now: State <span style={{ color: STATE_COLOR }}>{Math.round(latest.x)}</span> · Warning{' '}
              <span style={{ color: WARNING_COLOR }}>{Math.round(latest.y)}</span>
            </span>
          )
        )}
      </div>

      {history.isLoading ? (
        <SkeletonCard className="mt-3 h-72" />
      ) : !enoughData ? (
        <Callout variant="empty">Not enough coverage-qualified history yet — it accumulates as the daily job runs.</Callout>
      ) : (
        <>
          <div className="mt-3 h-72">
            <ResponsiveContainer width="100%" height="100%">
              {view === 'Time' ? (
                <LineChart data={series} margin={{ top: 6, right: 8, left: 0, bottom: 0 }}>
                  <CartesianGrid stroke="rgba(255,255,255,0.05)" vertical={false} />
                  <XAxis
                    dataKey="date"
                    tick={{ fill: '#6b7280', fontSize: 10 }}
                    tickFormatter={(d) => formatDate(String(d))}
                    minTickGap={28}
                    tickLine={false}
                    axisLine={{ stroke: 'rgba(255,255,255,0.08)' }}
                  />
                  {/* width must clear a 3-digit label: the old chart paired
                      width 28 with margin.left -18 and clipped every tick. */}
                  <YAxis
                    domain={[0, 100]}
                    ticks={[0, 25, 50, 75, 100]}
                    tick={{ fill: '#6b7280', fontSize: 10 }}
                    width={34}
                    tickLine={false}
                    axisLine={false}
                  />
                  {/* The two axes have different thresholds, so each divider is
                      drawn in its series' colour rather than as shared gridlines. */}
                  <ReferenceLine y={xDiv} stroke={STATE_COLOR} strokeOpacity={0.25} strokeDasharray="4 4" />
                  <ReferenceLine y={yDiv} stroke={WARNING_COLOR} strokeOpacity={0.25} strokeDasharray="4 4" />
                  <Tooltip
                    contentStyle={{
                      background: 'rgba(17,24,39,0.95)',
                      border: '1px solid rgba(255,255,255,0.1)',
                      borderRadius: 8,
                      fontSize: 12,
                    }}
                    labelStyle={{ color: '#9ca3af' }}
                    labelFormatter={(l) => formatDate(String(l))}
                    formatter={(value) => (value == null ? '—' : Math.round(Number(value)))}
                  />
                  <Line type="monotone" dataKey="state" name="State" stroke={STATE_COLOR} dot={false} strokeWidth={1.5} isAnimationActive={false} />
                  <Line type="monotone" dataKey="warning" name="Warning" stroke={WARNING_COLOR} dot={false} strokeWidth={1.5} isAnimationActive={false} />
                </LineChart>
              ) : (
                <ScatterChart margin={{ top: 10, right: 16, bottom: 22, left: 0 }}>
                  <ReferenceArea x1={0} x2={xDiv} y1={yDiv} y2={100} fill="#f59e0b" fillOpacity={0.07} stroke="none" />
                  <ReferenceArea x1={xDiv} x2={100} y1={yDiv} y2={100} fill="#f97316" fillOpacity={0.07} stroke="none" />
                  <ReferenceArea x1={0} x2={xDiv} y1={0} y2={yDiv} fill="#10b981" fillOpacity={0.07} stroke="none" />
                  <ReferenceArea x1={xDiv} x2={100} y1={0} y2={yDiv} fill="#ef4444" fillOpacity={0.08} stroke="none" />
                  <CartesianGrid stroke="rgba(255,255,255,0.04)" />
                  <ReferenceLine x={xDiv} stroke="rgba(255,255,255,0.12)" />
                  <ReferenceLine y={yDiv} stroke="rgba(255,255,255,0.12)" />
                  <XAxis
                    type="number"
                    dataKey="x"
                    domain={[0, 100]}
                    ticks={[0, 20, 40, 60, 80, 100]}
                    tick={{ fill: '#6b7280', fontSize: 10 }}
                    tickLine={false}
                    axisLine={{ stroke: 'rgba(255,255,255,0.08)' }}
                    label={{ value: 'State →', position: 'insideBottom', offset: -12, fill: '#6b7280', fontSize: 10 }}
                  />
                  <YAxis
                    type="number"
                    dataKey="y"
                    domain={[0, 100]}
                    ticks={[0, 20, 40, 60, 80, 100]}
                    tick={{ fill: '#6b7280', fontSize: 10 }}
                    width={30}
                    tickLine={false}
                    axisLine={false}
                    label={{ value: 'Warning', angle: -90, position: 'insideLeft', fill: '#6b7280', fontSize: 10 }}
                  />
                  <ZAxis range={[13, 13]} />
                  <Tooltip cursor={{ strokeDasharray: '3 3', stroke: 'rgba(255,255,255,0.2)' }} content={<PathTip />} />
                  <Scatter data={trail} line={{ stroke: 'rgba(96,165,250,0.18)', strokeWidth: 1.5 }} isAnimationActive={false}>
                    {trail.map((_, i) => (
                      <Cell key={i} fill={recencyColor(trail.length <= 1 ? 1 : i / (trail.length - 1))} />
                    ))}
                  </Scatter>
                  {latest && (
                    <Scatter
                      data={[latest]}
                      isAnimationActive={false}
                      shape={(props: { cx?: number; cy?: number }) => (
                        <circle cx={props.cx} cy={props.cy} r={6} fill="#ffffff" stroke={STATE_COLOR} strokeWidth={2} />
                      )}
                    />
                  )}
                </ScatterChart>
              )}
            </ResponsiveContainer>
          </div>

          {view === 'Time' ? (
            <div className="mt-2 flex flex-wrap items-center gap-4 text-[11px] text-gray-400">
              <span className="flex items-center gap-1.5">
                <span className="inline-block h-2 w-3 rounded-sm" style={{ background: STATE_COLOR }} />
                State
              </span>
              <span className="flex items-center gap-1.5">
                <span className="inline-block h-2 w-3 rounded-sm" style={{ background: WARNING_COLOR }} />
                Warning
              </span>
              <span className="text-gray-600">dashed = each axis's elevated threshold ({xDiv} / {yDiv})</span>
            </div>
          ) : (
            <div className="mt-2 grid grid-cols-1 gap-x-4 gap-y-1 text-[11px] text-gray-500 sm:grid-cols-2">
              <span><span className="text-amber-400">Early warning</span> — calm, fragility rising</span>
              <span><span className="text-orange-400">Active stress</span> — damaged and deteriorating</span>
              <span><span className="text-emerald-400">Healthy</span> — calm, broadly supported</span>
              <span><span className="text-red-400">Stabilizing</span> — damage remains, warning lower</span>
              <span className="text-gray-600 sm:col-span-2">White dot = today; trail brightens toward the present, smoothed.</span>
            </div>
          )}

          {crossesFreeze && (
            <p className="mt-2 text-[11px] text-gray-600">
              History before {basketAsOf} is reconstructed against today's basket — retrospective, not a live record.
            </p>
          )}
        </>
      )}
    </div>
  );
}
