"use client";

import { useEffect, useRef, useState } from "react";

/**
 * Daily line chart for up to three series (validated categorical slots: blue, aqua, orange, in that order; all pairs
 * pass colour-vision checks). Lines are 2px, the grid is recessive, there is one y-axis, a legend, direct labels at
 * the end of each line, a crosshair with a tooltip on hover/focus, and a table view (the aqua slot is below 3:1 on
 * white, so values are always available as text).
 */
export const SERIES_COLORS = ["#2a78d6", "#1baf7a", "#eb6834"] as const;

export interface Series {
  key: string;
  label: string;
}

const dayLabel = (d: string) => new Date(`${d}T00:00:00Z`).toLocaleDateString("en-IN", { day: "numeric", month: "short", timeZone: "UTC" });
const fmt = (n: number) => n.toLocaleString("en-IN");

function niceMax(v: number) {
  if (v <= 4) return 4;
  const pow = 10 ** Math.floor(Math.log10(v));
  const step = [1, 2, 2.5, 5, 10].find((s) => s * pow * 4 >= v)! * pow;
  return step * 4;
}

export function LineChart({ days, series, label, height = 220 }: { days: Record<string, string | number>[]; series: Series[]; label: string; height?: number }) {
  const ref = useRef<HTMLDivElement>(null);
  const [width, setWidth] = useState(640);
  const [hover, setHover] = useState<number | null>(null);
  const [table, setTable] = useState(false);

  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const ro = new ResizeObserver(([e]) => setWidth(Math.max(280, Math.floor(e.contentRect.width))));
    ro.observe(el);
    return () => ro.disconnect();
  }, [table]);

  const pad = { top: 12, right: 84, bottom: 26, left: 40 };
  const w = width - pad.left - pad.right;
  const h = height - pad.top - pad.bottom;
  const max = niceMax(Math.max(1, ...days.flatMap((d) => series.map((s) => Number(d[s.key] ?? 0)))));
  const x = (i: number) => pad.left + (days.length <= 1 ? w / 2 : (i / (days.length - 1)) * w);
  const y = (v: number) => pad.top + h - (v / max) * h;
  const ticks = [0, 0.25, 0.5, 0.75, 1].map((f) => Math.round(max * f));
  const lastIdx = days.length - 1;
  // Direct labels at the right end, nudged apart so they never overlap.
  const ends = series
    .map((s, i) => ({ s, i, v: Number(days[lastIdx]?.[s.key] ?? 0), yy: y(Number(days[lastIdx]?.[s.key] ?? 0)) }))
    .sort((a, b) => a.yy - b.yy);
  for (let k = 1; k < ends.length; k++) if (ends[k].yy - ends[k - 1].yy < 14) ends[k].yy = ends[k - 1].yy + 14;
  // Keep the stack inside the plot: if nudging pushed the last label below the axis, move them all up.
  const overflow = ends.length ? ends[ends.length - 1].yy - (pad.top + h) : 0;
  if (overflow > 0) for (const e of ends) e.yy -= overflow;
  const labelEvery = Math.ceil(days.length / Math.max(2, Math.floor(w / 80)));
  const d = hover !== null ? days[hover] : null;

  return (
    <div>
      <div className="mb-2 flex flex-wrap items-center gap-x-4 gap-y-1 text-[13px] text-muted">
        {series.map((s, i) => (
          <span key={s.key} className="inline-flex items-center gap-1.5">
            <span className="inline-block h-0.5 w-4 rounded" style={{ background: SERIES_COLORS[i] }} />
            {s.label}
          </span>
        ))}
        <button type="button" onClick={() => setTable((t) => !t)} className="ml-auto text-[13px] font-medium text-teal hover:underline">
          {table ? "Show chart" : "Show table"}
        </button>
      </div>
      {table ? (
        <div className="relative max-h-72 overflow-auto rounded-md border border-line">
          <table className="w-full text-sm">
            <caption className="sr-only">{label}</caption>
            <thead className="text-left text-[13px] text-muted">
              <tr>
                <th scope="col" className="sticky top-0 bg-paper px-3 py-2 font-medium">Day</th>
                {series.map((s) => (
                  <th key={s.key} scope="col" className="sticky top-0 bg-paper px-3 py-2 text-right font-medium">
                    {s.label}
                  </th>
                ))}
              </tr>
            </thead>
            <tbody className="divide-y divide-line">
              {days.map((row) => (
                <tr key={String(row.day)}>
                  <td className="px-3 py-1.5">{dayLabel(String(row.day))}</td>
                  {series.map((s) => (
                    <td key={s.key} className="tabular px-3 py-1.5 text-right">
                      {fmt(Number(row[s.key] ?? 0))}
                    </td>
                  ))}
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      ) : (
        <div ref={ref} className="relative" onMouseLeave={() => setHover(null)}>
          <svg width={width} height={height} role="img" aria-label={label} className="block max-w-full">
            {ticks.map((t) => (
              <g key={t}>
                <line x1={pad.left} x2={pad.left + w} y1={y(t)} y2={y(t)} stroke="#e3e9e7" strokeWidth={1} />
                <text x={pad.left - 8} y={y(t)} dy="0.32em" textAnchor="end" className="fill-muted text-[11px] tabular">
                  {fmt(t)}
                </text>
              </g>
            ))}
            {days.map((row, i) =>
              i % labelEvery === 0 || i === lastIdx ? (
                <text key={String(row.day)} x={x(i)} y={height - 6} textAnchor={i === 0 ? "start" : i === lastIdx ? "end" : "middle"} className="fill-muted text-[11px]">
                  {dayLabel(String(row.day))}
                </text>
              ) : null,
            )}
            {hover !== null && <line x1={x(hover)} x2={x(hover)} y1={pad.top} y2={pad.top + h} stroke="#9aa8a4" strokeWidth={1} strokeDasharray="3 3" />}
            {series.map((s, si) => (
              <polyline
                key={s.key}
                fill="none"
                stroke={SERIES_COLORS[si]}
                strokeWidth={2}
                strokeLinejoin="round"
                strokeLinecap="round"
                points={days.map((row, i) => `${x(i)},${y(Number(row[s.key] ?? 0))}`).join(' ')}
              />
            ))}
            {hover !== null &&
              series.map((s, si) => <circle key={s.key} cx={x(hover)} cy={y(Number(days[hover][s.key] ?? 0))} r={4.5} fill={SERIES_COLORS[si]} stroke="#fff" strokeWidth={2} />)}
            {ends.map((e) => (
              <text key={e.s.key} x={pad.left + w + 8} y={e.yy} dy="0.32em" className="fill-text text-[12px] tabular">
                {fmt(e.v)} {e.s.label.toLowerCase()}
              </text>
            ))}
            {days.map((row, i) => (
              <rect
                key={String(row.day)}
                x={x(i) - w / Math.max(1, days.length - 1) / 2}
                y={pad.top}
                width={Math.max(8, w / Math.max(1, days.length - 1))}
                height={h}
                fill="transparent"
                tabIndex={0}
                aria-label={`${dayLabel(String(row.day))}: ${series.map((s) => `${fmt(Number(row[s.key] ?? 0))} ${s.label.toLowerCase()}`).join(", ")}`}
                onMouseEnter={() => setHover(i)}
                onFocus={() => setHover(i)}
                onBlur={() => setHover(null)}
              />
            ))}
          </svg>
          {d && hover !== null && (
            <div className="pointer-events-none absolute top-1 z-10 rounded-md border border-line bg-white px-3 py-2 text-[13px] shadow-md" style={{ left: Math.min(Math.max(0, x(hover) - 70), width - 170) }}>
              <p className="mb-1 font-medium">{dayLabel(String(d.day))}</p>
              {series.map((s, si) => (
                <p key={s.key} className="flex items-center gap-2">
                  <span className="inline-block size-2 rounded-full" style={{ background: SERIES_COLORS[si] }} />
                  <span className="text-muted">{s.label}</span>
                  <span className="tabular ml-auto pl-3 font-medium">{fmt(Number(d[s.key] ?? 0))}</span>
                </p>
              ))}
            </div>
          )}
        </div>
      )}
    </div>
  );
}
