import { useEffect, useRef } from 'preact/hooks';
import uPlot from 'uplot';
import 'uplot/dist/uPlot.min.css';

// A handful of distinguishable line colors, theme-independent (uPlot draws
// on canvas, so it can't pick up CSS custom properties the way the rest of
// the app's chrome does) — enough for the "top 5 drinks" case, cycles if a
// caller ever passes more series.
const COLORS = ['#7aa2f7', '#f7768e', '#9ece6a', '#e0af68', '#bb9af7'];

export interface ChartSeries { name: string; values: number[] }

// Thin uPlot wrapper: x-axis is just sequential day labels (business days
// with zero sales are already dropped server-side, so evenly-spaced ticks
// read better than gapped real dates would) rather than real timestamps —
// avoids re-deriving a Date from the server's businessDay() string, which
// would need the same DST-aware timezone handling admin.ts already
// centralizes rather than a second copy in the browser.
export function DayChart({ labels, series }: { labels: string[]; series: ChartSeries[] }) {
  const holderRef = useRef<HTMLDivElement>(null);
  const plotRef = useRef<uPlot | null>(null);

  useEffect(() => {
    const holder = holderRef.current;
    if (!holder) return;

    const xs = labels.map((_, i) => i);
    const data: uPlot.AlignedData = [xs, ...series.map(s => s.values)];

    const opts: uPlot.Options = {
      width: holder.clientWidth || 600,
      height: 220,
      legend: { show: series.length > 1 },
      cursor: { drag: { x: false, y: false } },
      // distr: 2 (ordinal) — the default (1, linear) lets uPlot's tick
      // generator pick fractional increments on a short/sparse series,
      // and the label lookup below (`labels[v]`) misses on a non-integer
      // v, rendering a blank tick. Ordinal forces whole-number splits,
      // which is what an index-based x-axis always wants anyway (#55).
      scales: { x: { time: false, distr: 2 } },
      axes: [
        { stroke: '#888', grid: { stroke: '#333' }, values: (_u, vals) => vals.map(v => labels[v] ?? '') },
        { stroke: '#888', grid: { stroke: '#333' } },
      ],
      series: [
        {},
        ...series.map((s, i) => ({
          label: s.name,
          stroke: COLORS[i % COLORS.length],
          width: 2,
          points: { show: labels.length <= 14 },
        })),
      ],
    };

    plotRef.current = new uPlot(opts, data, holder);
    // Re-create on every data change rather than plot.setData: these charts
    // redraw once per page load / manual refresh, not on a hot path, so
    // the simplicity of "just rebuild it" outweighs the incremental-update
    // complexity setData would otherwise save.
    return () => { plotRef.current?.destroy(); plotRef.current = null; };
  }, [labels, series]);

  // Width was only ever read once at plot-creation time — no resize/
  // orientation-change handling, minor on desktop but more visible on the
  // phone/tablet path into /stats. setSize() is uPlot's cheap resize path
  // (no destroy/rebuild), separate from the effect above since it only
  // needs to run once for the holder's lifetime, not on every data change.
  useEffect(() => {
    const holder = holderRef.current;
    if (!holder || typeof ResizeObserver === 'undefined') return;
    const ro = new ResizeObserver(entries => {
      const width = entries[0]?.contentRect.width;
      if (width && plotRef.current) plotRef.current.setSize({ width, height: 220 });
    });
    ro.observe(holder);
    return () => ro.disconnect();
  }, []);

  return <div ref={holderRef} class="chart" />;
}
