"use client";

import { useRef, type KeyboardEvent as ReactKeyboardEvent, type PointerEvent as ReactPointerEvent } from "react";
import { useTranslation } from "@enode/core/translations/hooks";
import { formatDate } from "./chart-setup";
import {
  WEEK_MS,
  matchingPreset,
  moveEdge,
  panWindow,
  presetRange,
  presetsFor,
  type TimeRange,
} from "./navigator-model";

// The Trend's time navigator (study proposal T17): the chart's whole period in
// miniature under the plot, with the zoom as a window on it. It shows where the
// zoom sits, and sets it without dragging across the plot: drag an edge or the
// window (mouse, pen and touch), use the arrow keys on an edge (Shift: four
// weeks), or pick a preset. It's the accessible twin of drag-to-zoom (WCAG
// 2.5.7), which stays as a shortcut.

/** One period's value across the plotted series, for the outline. */
export type NavigatorPoint = { x: number; y: number };

export function TrendNavigator({
  domain,
  zoom,
  onZoom,
  outline,
}: {
  /** The whole period (first to last session). */
  domain: TimeRange;
  /** The zoomed range, or null for everything. */
  zoom: TimeRange | null;
  onZoom: (range: TimeRange | null) => void;
  outline: readonly NavigatorPoint[];
}) {
  const { t } = useTranslation();
  const trackRef = useRef<HTMLDivElement>(null);
  const drag = useRef<{ mode: "start" | "end" | "pan"; origin: number; window: TimeRange } | null>(null);
  const span = domain[1] - domain[0];
  if (span <= 0) return null;
  const window: TimeRange = zoom ?? domain;
  const pct = (x: number) => ((x - domain[0]) / span) * 100;
  const timeAt = (clientX: number) => {
    const rect = trackRef.current?.getBoundingClientRect();
    if (!rect || rect.width === 0) return domain[0];
    return domain[0] + ((clientX - rect.left) / rect.width) * span;
  };

  function startDrag(mode: "start" | "end" | "pan", event: ReactPointerEvent) {
    event.preventDefault();
    event.stopPropagation();
    (event.currentTarget as Element).setPointerCapture?.(event.pointerId);
    drag.current = { mode, origin: timeAt(event.clientX), window };
  }
  const onPointerMove = (event: ReactPointerEvent) => {
    const d = drag.current;
    if (!d) return;
    const at = timeAt(event.clientX);
    onZoom(d.mode === "pan" ? panWindow(d.window, at - d.origin, domain) : moveEdge(d.window, d.mode, at, domain));
  };
  const endDrag = () => {
    drag.current = null;
  };
  const onEdgeKey = (edge: "start" | "end") => (event: ReactKeyboardEvent) => {
    const step = (event.shiftKey ? 4 : 1) * WEEK_MS;
    const delta = event.key === "ArrowLeft" ? -step : event.key === "ArrowRight" ? step : 0;
    if (delta === 0) return;
    event.preventDefault();
    event.stopPropagation();
    onZoom(moveEdge(window, edge, window[edge === "start" ? 0 : 1] + delta, domain));
  };

  // The outline: the period's values as one faint line, scaled to the strip.
  const ys = outline.map((p) => p.y);
  const lo = Math.min(...ys);
  const hi = Math.max(...ys);
  const path = outline
    .map((p, i) => `${i ? "L" : "M"}${pct(p.x).toFixed(2)},${(hi > lo ? 22 - ((p.y - lo) / (hi - lo)) * 18 : 13).toFixed(2)}`)
    .join(" ");
  const presets = presetsFor(domain);
  const activePreset = matchingPreset(domain, zoom);
  const handle = (edge: "start" | "end") => (
    <span
      role="slider"
      tabIndex={0}
      aria-label={edge === "start" ? t("Start of the shown period") : t("End of the shown period")}
      aria-valuemin={domain[0]}
      aria-valuemax={domain[1]}
      aria-valuenow={window[edge === "start" ? 0 : 1]}
      aria-valuetext={formatDate(window[edge === "start" ? 0 : 1])}
      onPointerDown={(event) => startDrag(edge, event)}
      onKeyDown={onEdgeKey(edge)}
      className={`absolute inset-y-0 flex w-4 cursor-ew-resize touch-none items-center justify-center outline-none focus-visible:ring-2 focus-visible:ring-foreground-red-orange/40 ${
        edge === "start" ? "-left-2" : "-right-2"
      }`}
    >
      <span className="h-4 w-1 rounded-full bg-foreground-default" />
    </span>
  );

  return (
    <div className="mt-2 flex flex-col gap-1.5">
      <div
        ref={trackRef}
        onPointerMove={onPointerMove}
        onPointerUp={endDrag}
        onPointerCancel={endDrag}
        className="relative h-7 select-none rounded-md border border-surface-stroke bg-surface-base-muted/50"
      >
        <svg viewBox="0 0 100 26" preserveAspectRatio="none" aria-hidden className="absolute inset-0 h-full w-full">
          <path d={path} fill="none" stroke="currentColor" strokeWidth={1} vectorEffect="non-scaling-stroke" className="text-foreground-more-muted" />
        </svg>
        <div
          onPointerDown={(event) => startDrag("pan", event)}
          className="absolute inset-y-0 cursor-grab touch-none rounded-md border-2 border-foreground-default/70 bg-foreground-default/5 active:cursor-grabbing"
          style={{ left: `${pct(window[0])}%`, width: `${pct(window[1]) - pct(window[0])}%` }}
        >
          {handle("start")}
          {handle("end")}
        </div>
      </div>
      <div className="flex flex-wrap items-center gap-1.5">
        {presets.map((weeks) => (
          <button
            key={weeks}
            type="button"
            aria-pressed={activePreset === weeks}
            onClick={() => onZoom(presetRange(domain, weeks))}
            className={`body-small rounded-full border px-2.5 py-0.5 transition-colors ${
              activePreset === weeks
                ? "border-foreground-default bg-foreground-default text-surface-base"
                : "border-surface-stroke text-foreground-muted hover:text-foreground-default"
            }`}
          >
            {t("{count} wk", { count: weeks })}
          </button>
        ))}
        <button
          type="button"
          aria-pressed={zoom == null}
          onClick={() => onZoom(null)}
          className={`body-small rounded-full border px-2.5 py-0.5 transition-colors ${
            zoom == null
              ? "border-foreground-default bg-foreground-default text-surface-base"
              : "border-surface-stroke text-foreground-muted hover:text-foreground-default"
          }`}
        >
          {t("All")}
        </button>
        <span className="body-small ml-auto tabular-nums text-foreground-muted">
          {formatDate(window[0])} – {formatDate(window[1])}
        </span>
      </div>
    </div>
  );
}
