import { useMemo, useState, type ReactNode } from 'react';
import { formatUSD, formatUSDWhole, asDollars } from '@/lib/money';
import { Printer, RefreshCw, RotateCcw } from 'lucide-react';
import { useAnalyticsSummary } from '@/hooks/useQueries';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';

interface TrendRow { month: string; label?: string; total: number }
interface ExpectedActualRow { month: string; label?: string; expected: number; actual: number }
interface CategorySpendRow { category_name: string; total: number }
interface HeatmapCell { month: string; status: string; amount_paid?: number }
interface HeatmapMonth { key: string; label: string }
interface HeatmapRow { bill_id: number; bill_name: string; category_name?: string; cells: HeatmapCell[] }
interface HeatmapData { rows?: HeatmapRow[]; months?: HeatmapMonth[] }
interface ForecastRow { month: string; label: string; total: number; low: number; high: number }
interface AnalyticsRange { start?: string; end?: string }
interface AnalyticsData {
  monthly_spending?: TrendRow[];
  expected_vs_actual?: ExpectedActualRow[];
  category_spend?: CategorySpendRow[];
  heatmap?: HeatmapData;
  categories?: { id: number | string; name: string }[];
  bills?: { id: number | string; name: string; active?: number }[];
  range?: AnalyticsRange;
  generated_at?: string;
}

interface ChartVisibility {
  monthlyTrend: boolean;
  expectedActual: boolean;
  categorySpend: boolean;
  heatmap: boolean;
  forecast: boolean;
  [key: string]: boolean;
}

const RANGE_OPTIONS = [6, 12, 24, 36];
const MONTH_OPTIONS: [string, string][] = [
  ['1', 'January'], ['2', 'February'], ['3', 'March'], ['4', 'April'],
  ['5', 'May'], ['6', 'June'], ['7', 'July'], ['8', 'August'],
  ['9', 'September'], ['10', 'October'], ['11', 'November'], ['12', 'December'],
];
const CHART_OPTIONS: [string, string][] = [
  ['monthlyTrend', 'Monthly trend'],
  ['expectedActual', 'Expected vs actual'],
  ['categorySpend', 'Category spend'],
  ['heatmap', 'Pay heatmap'],
  ['forecast', 'Spending forecast'],
];
const PALETTE = ['#7c3aed', '#10b981', '#ec4899', '#3b82f6', '#f59e0b', '#14b8a6', '#ef4444', '#8b5cf6'];

function currentMonth() {
  const now = new Date();
  return { year: now.getFullYear(), month: now.getMonth() + 1 };
}

function money(value: number | null | undefined): string {
  return formatUSDWhole(asDollars(Number(value) || 0));
}

function fullMoney(value: number | null | undefined): string {
  return formatUSD(asDollars(Number(value) || 0));
}

function formatRange(range?: AnalyticsRange): string {
  if (!range?.start || !range?.end) return 'Selected range';
  return `${range.start.slice(0, 7)} through ${range.end.slice(0, 7)}`;
}

function hasData(rows: readonly unknown[] | undefined, keys: string[]): boolean {
  return !!rows?.some(row => keys.some(key => Number((row as Record<string, unknown>)[key]) > 0));
}

function EmptyState({ label = 'No analytics data for this selection.' }: { label?: string }) {
  return (
    <div className="flex min-h-[220px] items-center justify-center rounded-lg border border-dashed border-border/70 bg-muted/20 px-4 text-center text-sm text-muted-foreground">
      {label}
    </div>
  );
}

function ChartCard({ title, subtitle, children, summary }: {
  title: ReactNode;
  subtitle?: ReactNode;
  children: ReactNode;
  summary?: ReactNode;
}) {
  return (
    <section className="analytics-chart surface-elevated p-5">
      <div className="mb-4 flex items-start justify-between gap-4">
        <div>
          <h2 className="text-sm font-semibold tracking-tight">{title}</h2>
          {subtitle && <p className="mt-0.5 text-xs text-muted-foreground">{subtitle}</p>}
        </div>
        {summary && <div className="shrink-0 text-right text-sm font-semibold tabular-nums">{summary}</div>}
      </div>
      {children}
    </section>
  );
}

function SvgFrame({ children, height = 260, label = 'Chart' }: { children: ReactNode; height?: number; label?: string }) {
  return (
    <div className="w-full overflow-hidden rounded-lg border border-border/60 bg-background/60">
      <svg viewBox={`0 0 720 ${height}`} role="img" aria-label={label} className="h-auto w-full">
        {children}
      </svg>
    </div>
  );
}

function LineChart({ rows, area = false }: { rows: TrendRow[]; area?: boolean }) {
  if (!hasData(rows, ['total'])) return <EmptyState />;

  const width = 720;
  const height = 260;
  const pad = { left: 58, right: 24, top: 24, bottom: 46 };
  const chartW = width - pad.left - pad.right;
  const chartH = height - pad.top - pad.bottom;
  const max = Math.max(...rows.map(r => r.total), 1);
  const points = rows.map((row, index) => {
    const x = pad.left + (rows.length === 1 ? chartW / 2 : (index / (rows.length - 1)) * chartW);
    const y = pad.top + chartH - (row.total / max) * chartH;
    return { ...row, x, y };
  });
  const line = points.map(p => `${p.x},${p.y}`).join(' ');
  const areaPoints = `${pad.left},${pad.top + chartH} ${line} ${pad.left + chartW},${pad.top + chartH}`;

  return (
    <SvgFrame height={height} label="Trend line chart">
      {[0, 0.25, 0.5, 0.75, 1].map(tick => {
        const y = pad.top + chartH - tick * chartH;
        return (
          <g key={tick}>
            <line x1={pad.left} x2={pad.left + chartW} y1={y} y2={y} stroke="currentColor" opacity="0.09" />
            <text x="12" y={y + 4} fontSize="12" fill="currentColor" opacity="0.58">{money(max * tick)}</text>
          </g>
        );
      })}
      {area && <polygon points={areaPoints} fill="#7c3aed" opacity="0.16" />}
      <polyline points={line} fill="none" stroke="#7c3aed" strokeWidth="4" strokeLinecap="round" strokeLinejoin="round" />
      {points.map((point, index) => (
        <g key={point.month}>
          <circle cx={point.x} cy={point.y} r="4.5" fill="#7c3aed" />
          {(rows.length <= 12 || index % 3 === 0) && (
            <text x={point.x} y={height - 18} fontSize="12" fill="currentColor" opacity="0.65" textAnchor="middle">
              {point.label}
            </text>
          )}
          <title>{`${point.label}: ${fullMoney(point.total)}`}</title>
        </g>
      ))}
    </SvgFrame>
  );
}

function GroupedBarChart({ rows }: { rows: ExpectedActualRow[] }) {
  if (!hasData(rows, ['expected', 'actual'])) return <EmptyState />;

  const width = 720;
  const height = 280;
  const pad = { left: 58, right: 24, top: 24, bottom: 50 };
  const chartW = width - pad.left - pad.right;
  const chartH = height - pad.top - pad.bottom;
  const max = Math.max(...rows.flatMap(r => [r.expected, r.actual]), 1);
  const groupW = chartW / rows.length;
  const barW = Math.max(5, Math.min(17, groupW * 0.28));

  return (
    <SvgFrame height={height} label="Grouped bar chart">
      {[0, 0.5, 1].map(tick => {
        const y = pad.top + chartH - tick * chartH;
        return (
          <g key={tick}>
            <line x1={pad.left} x2={pad.left + chartW} y1={y} y2={y} stroke="currentColor" opacity="0.09" />
            <text x="12" y={y + 4} fontSize="12" fill="currentColor" opacity="0.58">{money(max * tick)}</text>
          </g>
        );
      })}
      {rows.map((row, index) => {
        const center = pad.left + index * groupW + groupW / 2;
        const expectedH = (row.expected / max) * chartH;
        const actualH = (row.actual / max) * chartH;
        return (
          <g key={row.month}>
            <rect x={center - barW - 1} y={pad.top + chartH - expectedH} width={barW} height={expectedH} rx="4" fill="#8b5cf6">
              <title>{`${row.label} expected: ${fullMoney(row.expected)}`}</title>
            </rect>
            <rect x={center + 1} y={pad.top + chartH - actualH} width={barW} height={actualH} rx="4" fill="#10b981">
              <title>{`${row.label} actual: ${fullMoney(row.actual)}`}</title>
            </rect>
            {(rows.length <= 12 || index % 3 === 0) && (
              <text x={center} y={height - 18} fontSize="12" fill="currentColor" opacity="0.65" textAnchor="middle">
                {row.label}
              </text>
            )}
          </g>
        );
      })}
      <g transform={`translate(${width - 190}, 18)`} fontSize="12" fill="currentColor">
        <rect width="10" height="10" rx="2" fill="#8b5cf6" /><text x="16" y="10">Expected</text>
        <rect x="92" width="10" height="10" rx="2" fill="#10b981" /><text x="108" y="10">Actual</text>
      </g>
    </SvgFrame>
  );
}

function DonutChart({ rows }: { rows: CategorySpendRow[] }) {
  const total = rows.reduce((sum, row) => sum + Number(row.total || 0), 0);
  if (!total) return <EmptyState />;

  let cumulative = 0;
  const radius = 78;
  const circumference = 2 * Math.PI * radius;

  return (
    <div className="grid gap-5 md:grid-cols-[260px_1fr] md:items-center">
      <div className="flex justify-center">
        <svg viewBox="0 0 220 220" role="img" aria-label="Category breakdown donut chart" className="h-56 w-56">
          <circle cx="110" cy="110" r={radius} fill="none" stroke="currentColor" strokeWidth="30" opacity="0.08" />
          {rows.map((row, index) => {
            const value = Number(row.total || 0);
            const dash = (value / total) * circumference;
            const segment = (
              <circle
                key={row.category_name}
                cx="110"
                cy="110"
                r={radius}
                fill="none"
                stroke={PALETTE[index % PALETTE.length]}
                strokeWidth="30"
                strokeDasharray={`${dash} ${circumference - dash}`}
                strokeDashoffset={-cumulative}
                transform="rotate(-90 110 110)"
              >
                <title>{`${row.category_name}: ${fullMoney(value)}`}</title>
              </circle>
            );
            cumulative += dash;
            return segment;
          })}
          <text x="110" y="104" textAnchor="middle" fontSize="13" fill="currentColor" opacity="0.65">Total</text>
          <text x="110" y="126" textAnchor="middle" fontSize="22" fontWeight="700" fill="currentColor">{money(total)}</text>
        </svg>
      </div>
      <div className="space-y-2">
        {rows.map((row, index) => (
          <div key={row.category_name} className="flex items-center justify-between gap-3 rounded-lg border border-border/60 bg-muted/20 px-3 py-2 text-sm">
            <span className="flex min-w-0 items-center gap-2">
              <span className="h-3 w-3 shrink-0 rounded-sm" style={{ backgroundColor: PALETTE[index % PALETTE.length] }} />
              <span className="truncate">{row.category_name}</span>
            </span>
            <span className="shrink-0 font-medium tabular-nums">{fullMoney(row.total)}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

const HEATMAP_CLASS: Record<string, string> = {
  paid: 'bg-emerald-500/85 border-emerald-400/40',
  skipped: 'bg-sky-500/70 border-sky-400/40',
  missed: 'bg-red-500/75 border-red-400/40',
  no_data: 'bg-muted border-border',
};

function Heatmap({ heatmap }: { heatmap?: HeatmapData }) {
  const rows = heatmap?.rows || [];
  const months = heatmap?.months || [];
  if (!rows.length || !months.length) return <EmptyState />;

  return (
    <div className="overflow-x-auto">
      <div className="space-y-4 min-w-[760px]">
        <div className="rounded-lg border border-border/60">
          <div
            className="grid border-b border-border/60 bg-muted/30 text-[11px] font-semibold uppercase tracking-wide text-muted-foreground"
            style={{ gridTemplateColumns: `180px repeat(${months.length}, minmax(38px, 1fr))` }}
          >
            <div className="px-3 py-2">Bill</div>
            {months.map(month => <div key={month.key} className="px-1 py-2 text-center">{month.label}</div>)}
          </div>
          {rows.map(row => (
            <div
              key={row.bill_id}
              className="grid border-b border-border/40 last:border-b-0"
              style={{ gridTemplateColumns: `180px repeat(${months.length}, minmax(38px, 1fr))` }}
            >
              <div className="min-w-0 px-3 py-2">
                <p className="truncate text-sm font-medium">{row.bill_name}</p>
                <p className="truncate text-[11px] text-muted-foreground">{row.category_name}</p>
              </div>
              {months.map(month => {
                const cell: HeatmapCell = row.cells.find(item => item.month === month.key) || { month: month.key, status: 'no_data', amount_paid: 0 };
                return (
                  <div key={`${row.bill_id}-${month.key}`} className="flex items-center justify-center px-1 py-2">
                    <span
                      className={cn('h-5 w-5 rounded border', HEATMAP_CLASS[cell.status] || HEATMAP_CLASS.no_data)}
                      title={`${row.bill_name}, ${month.label}: ${cell.status.replace('_', ' ')}${cell.amount_paid ? ` (${fullMoney(cell.amount_paid)})` : ''}`}
                    />
                  </div>
                );
              })}
            </div>
          ))}
        </div>
      </div>
      <div className="flex flex-wrap gap-3 text-xs text-muted-foreground">
        {([
          ['paid', 'Paid'],
          ['skipped', 'Skipped'],
          ['missed', 'Missed'],
          ['no_data', 'No data'],
        ] as [string, string][]).map(([status, label]) => (
          <span key={status} className="inline-flex items-center gap-1.5">
            <span className={cn('h-3 w-3 rounded border', HEATMAP_CLASS[status])} />
            {label}
          </span>
        ))}
      </div>
    </div>
  );
}

// ─── Forecast ────────────────────────────────────────────────────────────────

function linearForecast(rows: TrendRow[], horizonMonths: number): ForecastRow[] {
  if (rows.length < 3) return [];

  const n = rows.length;
  const ys = rows.map(r => Number(r.total) || 0);
  const xMean = (n - 1) / 2;
  const yMean = ys.reduce((a, b) => a + b, 0) / n;

  const sxy = ys.reduce((sum, y, i) => sum + (i - xMean) * (y - yMean), 0);
  const sxx = ys.reduce((sum, _, i) => sum + (i - xMean) ** 2, 0);
  const slope = sxy / sxx;
  const intercept = yMean - slope * xMean;

  const residuals = ys.map((y, i) => y - (slope * i + intercept));
  const stdDev = Math.sqrt(residuals.reduce((s, r) => s + r * r, 0) / n);

  const lastRow = rows[n - 1]!;
  const parts = lastRow.month.split('-').map(Number);
  const lastYear = parts[0] ?? 0;
  const lastMonthNum = parts[1] ?? 1;

  return Array.from({ length: horizonMonths }, (_, i) => {
    const x = n + i;
    const projected = Math.max(0, slope * x + intercept);
    const d = new Date(Date.UTC(lastYear, lastMonthNum - 1 + i + 1, 1));
    const month = `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, '0')}`;
    const label = d.toLocaleString('en-US', { month: 'short', year: '2-digit', timeZone: 'UTC' });
    return {
      month,
      label,
      total: Number(projected.toFixed(2)),
      low: Number(Math.max(0, projected - stdDev).toFixed(2)),
      high: Number((projected + stdDev).toFixed(2)),
    };
  });
}

function ForecastChart({ historical, forecast }: { historical: TrendRow[]; forecast: ForecastRow[] }) {
  const allRows = [...historical, ...forecast];
  if (!allRows.length) return <EmptyState />;

  const width = 720;
  const height = 260;
  const pad = { left: 58, right: 24, top: 36, bottom: 46 };
  const chartW = width - pad.left - pad.right;
  const chartH = height - pad.top - pad.bottom;

  const maxVal = Math.max(...historical.map(r => r.total), ...forecast.map(r => r.high), 1);

  function toXY(index: number, value: number) {
    const x = pad.left + (allRows.length === 1 ? chartW / 2 : (index / (allRows.length - 1)) * chartW);
    const y = pad.top + chartH - (value / maxVal) * chartH;
    return { x, y };
  }

  const histPoints = historical.map((row, i) => ({ ...row, ...toXY(i, row.total) }));
  const forePoints = forecast.map((row, i) => ({
    ...row,
    ...toXY(historical.length + i, row.total),
    yHigh: toXY(historical.length + i, row.high).y,
    yLow:  toXY(historical.length + i, row.low).y,
  }));

  const histLine = histPoints.map(p => `${p.x},${p.y}`).join(' ');
  const lastHist = histPoints[histPoints.length - 1];

  const dividerX = lastHist ? lastHist.x : null;

  const foreLine = forePoints.length && lastHist
    ? `${lastHist.x},${lastHist.y} ` +
      forePoints.map(p => `${p.x},${p.y}`).join(' ')
    : '';

  const bandPoly = forePoints.length > 1
    ? [...forePoints.map(p => `${p.x},${p.yHigh}`), ...forePoints.slice().reverse().map(p => `${p.x},${p.yLow}`)].join(' ')
    : '';

  const showLabel = (index: number) => allRows.length <= 14 || index % Math.ceil(allRows.length / 14) === 0;

  return (
    <SvgFrame height={height} label="Forecast chart">
      {/* Grid */}
      {[0, 0.25, 0.5, 0.75, 1].map(tick => {
        const y = pad.top + chartH - tick * chartH;
        return (
          <g key={tick}>
            <line x1={pad.left} x2={pad.left + chartW} y1={y} y2={y} stroke="currentColor" opacity="0.08" />
            <text x="12" y={y + 4} fontSize="12" fill="currentColor" opacity="0.58">{money(maxVal * tick)}</text>
          </g>
        );
      })}

      {/* Forecast region shading */}
      {dividerX && (
        <rect
          x={dividerX} y={pad.top}
          width={pad.left + chartW - dividerX} height={chartH}
          fill="currentColor" opacity="0.025"
        />
      )}

      {/* Confidence band */}
      {bandPoly && <polygon points={bandPoly} fill="#7c3aed" opacity="0.13" />}

      {/* Historical line + area fill */}
      <polygon
        points={`${pad.left},${pad.top + chartH} ${histLine} ${lastHist?.x ?? pad.left},${pad.top + chartH}`}
        fill="#7c3aed" opacity="0.10"
      />
      <polyline points={histLine} fill="none" stroke="#7c3aed" strokeWidth="3.5" strokeLinecap="round" strokeLinejoin="round" />

      {/* Forecast line (dashed, connects from last historical point) */}
      {foreLine && (
        <polyline
          points={foreLine}
          fill="none" stroke="#7c3aed" strokeWidth="2.5"
          strokeDasharray="9,6" strokeLinecap="round" strokeLinejoin="round"
          opacity="0.7"
        />
      )}

      {/* Divider tick */}
      {dividerX && (
        <line
          x1={dividerX} x2={dividerX}
          y1={pad.top} y2={pad.top + chartH}
          stroke="currentColor" strokeDasharray="4,4" opacity="0.2"
        />
      )}

      {/* Historical dots + labels */}
      {histPoints.map((p, i) => (
        <g key={p.month}>
          <circle cx={p.x} cy={p.y} r="4" fill="#7c3aed" />
          {showLabel(i) && (
            <text x={p.x} y={height - 18} fontSize="11" fill="currentColor" opacity="0.6" textAnchor="middle">
              {p.label}
            </text>
          )}
          <title>{`${p.label}: ${fullMoney(p.total)}`}</title>
        </g>
      ))}

      {/* Forecast dots + labels */}
      {forePoints.map((p, i) => (
        <g key={p.month}>
          <circle cx={p.x} cy={p.y} r="4" fill="none" stroke="#7c3aed" strokeWidth="2" opacity="0.75" />
          {showLabel(historical.length + i) && (
            <text x={p.x} y={height - 18} fontSize="11" fill="currentColor" opacity="0.45" textAnchor="middle">
              {p.label}
            </text>
          )}
          <title>{`${p.label} (projected): ${fullMoney(p.total)} · range ${fullMoney(p.low)}–${fullMoney(p.high)}`}</title>
        </g>
      ))}

      {/* Legend */}
      <g transform={`translate(${pad.left + 8}, 18)`} fontSize="11" fill="currentColor" opacity="0.7">
        <line x1="0" x2="16" y1="5" y2="5" stroke="#7c3aed" strokeWidth="3" />
        <text x="21" y="9">Historical</text>
        <line x1="97" x2="113" y1="5" y2="5" stroke="#7c3aed" strokeWidth="2.5" strokeDasharray="5,4" />
        <rect x="120" y="0" width="10" height="10" rx="2" fill="#7c3aed" opacity="0.15" />
        <text x="135" y="9">Projected ± 1 σ</text>
      </g>
    </SvgFrame>
  );
}

function Field({ label, children }: { label: ReactNode; children: ReactNode }) {
  return (
    <label className="space-y-1.5">
      <span className="block text-xs font-medium text-muted-foreground">{label}</span>
      {children}
    </label>
  );
}

function ControlSelect({ value, onChange, children, className }: {
  value: string;
  onChange: (value: string) => void;
  children: ReactNode;
  className?: string;
}) {
  return (
    <select
      value={value}
      onChange={e => onChange(e.target.value)}
      className={cn('h-9 rounded-md border border-input bg-background px-3 text-sm shadow-sm focus:outline-none focus:ring-[3px] focus:ring-ring/50', className)}
    >
      {children}
    </select>
  );
}

export default function AnalyticsPage() {
  const initial = currentMonth();
  const [year, setYear] = useState(initial.year);
  const [month, setMonth] = useState(initial.month);
  const [months, setMonths] = useState(12);
  const [categoryId, setCategoryId] = useState('');
  const [billId, setBillId] = useState('');
  const [includeInactive, setIncludeInactive] = useState(false);
  const [includeSkipped, setIncludeSkipped] = useState(true);
  const [trendMode, setTrendMode] = useState('line');
  const [visible, setVisible] = useState<ChartVisibility>({
    monthlyTrend: true,
    expectedActual: true,
    categorySpend: true,
    heatmap: true,
    forecast: true,
  });
  const [forecastHorizon, setForecastHorizon] = useState(6);

  const params = useMemo(() => ({
    year,
    month,
    months,
    category_id: categoryId,
    bill_id: billId,
    include_inactive: includeInactive,
    include_skipped: includeSkipped,
  }), [billId, categoryId, includeInactive, includeSkipped, month, months, year]);

  // React Query handles caching, dedup, cancellation, and out-of-order responses.
  const { data: dataRaw, isPending, isFetching, error: queryError, refetch } = useAnalyticsSummary(params);
  const data = dataRaw as AnalyticsData | undefined;
  const loading = isPending;
  const error = queryError ? (queryError.message || 'Failed to load analytics.') : '';

  const forecastRows = useMemo(
    () => linearForecast(data?.monthly_spending || [], forecastHorizon),
    [data?.monthly_spending, forecastHorizon],
  );

  const reset = () => {
    const next = currentMonth();
    setYear(next.year);
    setMonth(next.month);
    setMonths(12);
    setCategoryId('');
    setBillId('');
    setIncludeInactive(false);
    setIncludeSkipped(true);
    setTrendMode('line');
    setForecastHorizon(6);
    setVisible({ monthlyTrend: true, expectedActual: true, categorySpend: true, heatmap: true, forecast: true });
  };

  const totalCategorySpend = data?.category_spend?.reduce((sum, row) => sum + Number(row.total || 0), 0) || 0;
  const activeCharts = CHART_OPTIONS.filter(([key]) => visible[key]).map(([, label]) => label).join(', ') || 'None';
  const filterSummary = [
    categoryId ? `Category: ${data?.categories?.find(c => String(c.id) === String(categoryId))?.name || categoryId}` : 'All categories',
    billId ? `Bill: ${data?.bills?.find(b => String(b.id) === String(billId))?.name || billId}` : 'All bills',
    includeInactive ? 'Includes inactive bills' : 'Active bills only',
    includeSkipped ? 'Shows skipped months' : 'Hides skipped months',
  ].join(' | ');

  return (
    <div className="analytics-page space-y-6">
      <div className="analytics-report-meta hidden">
        <h1>BillTracker Analytics</h1>
        <p>{formatRange(data?.range)}</p>
        <p>{filterSummary}</p>
        <p>Visible charts: {activeCharts}</p>
        <p>Generated {new Date(data?.generated_at || Date.now()).toLocaleString()}</p>
      </div>

      <div className="analytics-screen-header flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
        <div>
          <h1 className="text-2xl font-bold tracking-tight">Analytics</h1>
          <p className="mt-0.5 text-sm text-muted-foreground">
            Spending trends, category breakdowns, and payment history.
          </p>
        </div>
        <div className="analytics-actions flex flex-wrap gap-2">
          <Button variant="outline" size="sm" onClick={() => refetch()} disabled={isFetching} className="flex-1 sm:flex-none">
            <RefreshCw className={cn('h-3.5 w-3.5', isFetching && 'animate-spin')} />
            Refresh
          </Button>
          <Button variant="outline" size="sm" onClick={() => window.print()} className="flex-1 sm:flex-none">
            <Printer className="h-3.5 w-3.5" />
            Print
          </Button>
          <Button size="sm" onClick={() => window.print()} className="flex-1 sm:flex-none">
            <Printer className="h-3.5 w-3.5" />
            Print / Save PDF
          </Button>
        </div>
      </div>

      <section className="analytics-controls surface-elevated p-4">
        <div className="grid gap-4 lg:grid-cols-[1fr_auto] lg:items-end">
          <div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-7">
            <Field label="Ending month">
              <ControlSelect value={String(month)} onChange={value => setMonth(Number(value))}>
                {MONTH_OPTIONS.map(([value, label]) => <option key={value} value={value}>{label}</option>)}
              </ControlSelect>
            </Field>
            <Field label="Ending year">
              <input
                type="number"
                min="2000"
                max="2100"
                value={year}
                onChange={e => setYear(Number(e.target.value))}
                className="h-9 w-full rounded-md border border-input bg-background px-3 text-sm shadow-sm focus:outline-none focus:ring-[3px] focus:ring-ring/50"
              />
            </Field>
            <Field label="Range">
              <ControlSelect value={String(months)} onChange={value => setMonths(Number(value))}>
                {RANGE_OPTIONS.map(value => <option key={value} value={value}>{value} months</option>)}
              </ControlSelect>
            </Field>
            <Field label="Category">
              <ControlSelect value={categoryId} onChange={value => { setCategoryId(value); setBillId(''); }}>
                <option value="">All categories</option>
                {(data?.categories || []).map(category => (
                  <option key={category.id} value={category.id}>{category.name}</option>
                ))}
              </ControlSelect>
            </Field>
            <Field label="Bill">
              <ControlSelect value={billId} onChange={setBillId}>
                <option value="">All bills</option>
                {(data?.bills || []).map(bill => (
                  <option key={bill.id} value={bill.id}>{bill.name}{bill.active ? '' : ' (inactive)'}</option>
                ))}
              </ControlSelect>
            </Field>
            <Field label="Trend style">
              <ControlSelect value={trendMode} onChange={setTrendMode}>
                <option value="line">Line</option>
                <option value="area">Area</option>
              </ControlSelect>
            </Field>
            <Field label="Forecast">
              <ControlSelect value={String(forecastHorizon)} onChange={v => setForecastHorizon(Number(v))}>
                <option value="3">3 months</option>
                <option value="6">6 months</option>
                <option value="12">12 months</option>
              </ControlSelect>
            </Field>
          </div>
          <Button type="button" variant="outline" onClick={reset}>
            <RotateCcw className="h-4 w-4" />
            Reset filters
          </Button>
        </div>

        <div className="mt-4 grid gap-3 border-t border-border/60 pt-4 md:grid-cols-2 xl:grid-cols-4">
          {CHART_OPTIONS.map(([key, label]) => (
            <label key={key} className="flex items-center gap-2 text-sm">
              <input
                type="checkbox"
                checked={visible[key]}
                onChange={e => setVisible(prev => ({ ...prev, [key]: e.target.checked }))}
                className="h-4 w-4 rounded border-input bg-background accent-primary"
              />
              {label}
            </label>
          ))}
          <label className="flex items-center gap-2 text-sm">
            <input
              type="checkbox"
              checked={includeInactive}
              onChange={e => setIncludeInactive(e.target.checked)}
              className="h-4 w-4 rounded border-input bg-background accent-primary"
            />
            Include inactive bills
          </label>
          <label className="flex items-center gap-2 text-sm">
            <input
              type="checkbox"
              checked={includeSkipped}
              onChange={e => setIncludeSkipped(e.target.checked)}
              className="h-4 w-4 rounded border-input bg-background accent-primary"
            />
            Show skipped months
          </label>
        </div>
      </section>

      <div className="analytics-range text-sm text-muted-foreground">
        {data ? (
          <>
            Reporting on <span className="font-medium text-foreground">{formatRange(data.range)}</span>.
            <span className="ml-2">{filterSummary}</span>
          </>
        ) : 'Preparing analytics...'}
      </div>

      {loading && (
        <div className="grid gap-5 lg:grid-cols-2">
          {[1, 2, 3, 4].map(item => <div key={item} className="h-80 animate-pulse rounded-2xl bg-muted/50" />)}
        </div>
      )}

      {!loading && error && (
        <div className="rounded-lg border border-destructive/25 bg-destructive/10 px-4 py-3 text-sm text-destructive">
          {error}
        </div>
      )}

      {!loading && !error && data && (
        <div className="analytics-chart-grid grid gap-5 xl:grid-cols-2">
          {visible.monthlyTrend && (
            <ChartCard title="Monthly spending trend" subtitle="Actual payments grouped by paid month.">
              <LineChart rows={data.monthly_spending || []} area={trendMode === 'area'} />
            </ChartCard>
          )}
          {visible.expectedActual && (
            <ChartCard title="Expected vs actual spend" subtitle="Expected uses monthly override amount when present, otherwise the bill estimate.">
              <GroupedBarChart rows={data.expected_vs_actual || []} />
            </ChartCard>
          )}
          {visible.categorySpend && (
            <ChartCard title="Spending by category" subtitle="Payments grouped by bill category." summary={fullMoney(totalCategorySpend)}>
              <DonutChart rows={data.category_spend || []} />
            </ChartCard>
          )}
          {visible.heatmap && (
            <div className="xl:col-span-2">
              <ChartCard title="Pay-on-time heatmap" subtitle="Bill status by month. Future/current unpaid months show as no data.">
                <Heatmap heatmap={data.heatmap} />
              </ChartCard>
            </div>
          )}
          {visible.forecast && (
            <div className="xl:col-span-2">
              <ChartCard
                title={`Spending forecast · next ${forecastHorizon} months`}
                subtitle={
                  (data.monthly_spending || []).length < 3
                    ? 'Need at least 3 months of history to project.'
                    : 'Linear extrapolation from historical payments. Shaded band shows ± 1 standard deviation of past residuals.'
                }
              >
                {(data.monthly_spending || []).length < 3 ? (
                  <EmptyState label="Not enough history to project — select a wider range." />
                ) : (
                  <>
                    <ForecastChart historical={data.monthly_spending || []} forecast={forecastRows} />
                    <div className="mt-4 overflow-x-auto">
                      <table className="w-full text-sm">
                        <thead>
                          <tr className="border-b border-border/60 text-[10px] font-semibold uppercase tracking-widest text-muted-foreground">
                            <th className="py-2 pr-4 text-left">Month</th>
                            <th className="py-2 pr-4 text-right">Projected</th>
                            <th className="py-2 pr-4 text-right">Low estimate</th>
                            <th className="py-2 text-right">High estimate</th>
                          </tr>
                        </thead>
                        <tbody className="divide-y divide-border/40">
                          {forecastRows.map(row => (
                            <tr key={row.month} className="text-muted-foreground hover:text-foreground transition-colors">
                              <td className="py-2 pr-4 font-medium text-foreground">{row.label}</td>
                              <td className="tracker-number py-2 pr-4 text-right font-semibold">{fullMoney(row.total)}</td>
                              <td className="tracker-number py-2 pr-4 text-right font-medium text-muted-foreground">{fullMoney(row.low)}</td>
                              <td className="tracker-number py-2 text-right font-medium text-muted-foreground">{fullMoney(row.high)}</td>
                            </tr>
                          ))}
                        </tbody>
                      </table>
                    </div>
                  </>
                )}
              </ChartCard>
            </div>
          )}
          {!Object.values(visible).some(Boolean) && (
            <div className="xl:col-span-2">
              <EmptyState label="Select at least one chart to show analytics." />
            </div>
          )}
        </div>
      )}

      <div className="analytics-print-footer hidden text-xs text-muted-foreground">
        Generated from BillTracker Analytics on {new Date(data?.generated_at || Date.now()).toLocaleString()}.
      </div>
    </div>
  );
}
