'use client';

/**
 * AdmissionsTrendChart
 * Compact line chart of the first-choice offer rate across admissions years.
 * Renders nothing unless at least two years carry an offer-rate value.
 */

import { Line } from 'react-chartjs-2';
import { ChartOptions } from 'chart.js';
import '@/lib/chartSetup';
import { useThemeTokens, alpha } from '@/lib/theme';
import { formatAcademicYear } from '@/lib/utils';
import { fillAcademicYears } from '@/lib/compareChartData';
import type { SchoolAdmissions } from '@/lib/types';
import styles from './AdmissionsTrendChart.module.css';

export default function AdmissionsTrendChart({ history }: { history: SchoolAdmissions[] }) {
  const pts = history.filter((h) => h.first_preference_offer_pct != null);
  if (pts.length < 2) return null;

  // Gap-honest axis: every academic year between the first and last data point
  // appears, so a missing admissions year renders as a real gap (spanGaps:false)
  // rather than compressing time between distant years.
  const axisYears = fillAcademicYears(pts.map((p) => p.year));
  const byYear = new Map(pts.map((p) => [p.year, p.first_preference_offer_pct as number]));
  const labels = axisYears.map(formatAcademicYear);
  const values: (number | null)[] = axisYears.map((y) => byYear.get(y) ?? null);
  const present = values
    .map((v, i) => (v != null ? i : -1))
    .filter((i) => i >= 0);
  const lastIdx = present[present.length - 1];

  // Auto-scale with headroom so variation is visible, clamped to 0–100.
  const numeric = values.filter((v): v is number => v != null);
  const lo = Math.min(...numeric);
  const hi = Math.max(...numeric);
  const padded = Math.max(5, Math.round((hi - lo) * 0.25));
  const yMin = Math.max(0, Math.floor((lo - padded) / 5) * 5);
  const yMax = Math.min(100, Math.ceil((hi + padded) / 5) * 5);

  // Canvas can't read var(), so pull the resolved tokens through the bridge.
  const [cBrand, cGrid, cInverse, cInverseText, cCard] = useThemeTokens(
    '--brand', '--chart-grid', '--surface-inverse', '--text-inverse', '--bg-card'
  );

  const options: ChartOptions<'line'> = {
    responsive: true,
    maintainAspectRatio: false,
    interaction: { mode: 'index', intersect: false },
    // Headroom so a point sitting on the y-max ceiling (e.g. 100%) isn't
    // clipped by the top of the plot area.
    layout: { padding: { top: 8 } },
    plugins: {
      legend: { display: false },
      title: { display: false },
      tooltip: {
        backgroundColor: cInverse,
        titleColor: cInverseText,
        bodyColor: cInverseText,
        padding: 10,
        titleFont: { size: 12 },
        bodyFont: { size: 12 },
        callbacks: {
          label: (ctx) => (ctx.parsed.y == null ? '' : `First-choice offers: ${Math.round(ctx.parsed.y)}%`),
        },
      },
    },
    scales: {
      y: {
        min: yMin,
        max: yMax,
        grid: { color: cGrid },
        ticks: { font: { size: 11 }, maxTicksLimit: 5, callback: (v) => `${v}%` },
      },
      x: {
        grid: { display: false },
        ticks: { font: { size: 11 }, autoSkip: true, maxRotation: 0, autoSkipPadding: 16 },
      },
    },
  };

  const data = {
    labels,
    datasets: [
      {
        label: 'First-choice offer rate',
        data: values,
        clip: false as const,
        spanGaps: false,
        borderColor: cBrand,
        backgroundColor: alpha('--brand', 0.10),
        borderWidth: 2.5,
        tension: 0.3,
        fill: true,
        pointRadius: values.map((_, i) => (i === lastIdx ? 5 : 3)),
        pointBackgroundColor: cBrand,
        pointBorderColor: cCard,
        pointBorderWidth: values.map((_, i) => (i === lastIdx ? 2 : 0)),
        pointHoverRadius: 6,
      },
    ],
  };

  return (
    <div className={styles.wrapper}>
      <Line data={data} options={options} />
    </div>
  );
}
