import { useId, useRef, useState, type KeyboardEvent } from "react";
import { formatDate } from "../../flow";
import { labChange, plotLab, rangeLabel, shortDate, trendWords, type LabSeries, type PlotPoint } from "../../logic/charts";
import { cn, Icon, Stat, StatusPill, Tag, Tip, TipProvider } from "../kit";
import { FlagPill } from "./LabBits";

const flagged = (p: PlotPoint) => p.lab.flag === "high" || p.lab.flag === "low";
const flagWord = (p: PlotPoint) => (p.lab.flag === "high" ? "High" : "Low");
const keys: Record<string, number | "first" | "last"> = { ArrowRight: 1, ArrowUp: 1, ArrowLeft: -1, ArrowDown: -1, Home: "first", End: "last" };

/** One test over time: the latest value first, then the line with the record's range as a faint band. */
export function LabTrend({ series, id }: { series: LabSeries; id?: string }) {
  const plot = plotLab(series);
  const pts = plot.points;
  const [active, setActive] = useState(pts.length - 1);
  const refs = useRef<(HTMLButtonElement | null)[]>([]);
  const gradient = `lab-fill-${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
  if (pts.length < 2) return null;
  const current = Math.min(active, pts.length - 1);
  const { latest, unit } = series;
  const [first, last] = [pts[0], pts[pts.length - 1]];
  const change = labChange(series);
  const low = pts.reduce((a, b) => (b.value < a.value ? b : a));
  const high = pts.reduce((a, b) => (b.value > a.value ? b : a));
  const words = (p: PlotPoint) => `${formatDate(p.lab.date)} · ${p.lab.value.trim()}${unit ? ` ${unit}` : ""}${flagged(p) ? ` · ${flagWord(p)} on the record` : ""}`;
  const move = (e: KeyboardEvent, i: number) => {
    const k = keys[e.key];
    if (k === undefined) return;
    e.preventDefault();
    const to = k === "first" ? 0 : k === "last" ? pts.length - 1 : Math.max(0, Math.min(pts.length - 1, i + k));
    setActive(to);
    refs.current[to]?.focus();
  };
  const offChart = series.results.length - pts.length;
  return (
    <figure id={id} className="m-0 flex flex-col gap-4">
      <figcaption className="flex items-center gap-2.5">
        <span className="flex size-7 items-center justify-center rounded-lg bg-page text-teal-icon shadow-card">
          <Icon name="test-tube-line" />
        </span>
        <span className="min-w-0 truncate text-[14px] font-medium text-body">{series.name}</span>
        <span className="ml-auto shrink-0 font-mono text-[12px] text-muted">{formatDate(latest.date)}</span>
      </figcaption>

      <div className="flex flex-wrap items-end gap-x-3 gap-y-2">
        <span className="text-[40px] leading-[44px] font-semibold tracking-[-0.03em] text-ink tabular-nums">{latest.value.trim()}</span>
        {unit && <span className="pb-1 text-[15px] leading-6 font-medium text-subtle">{unit}</span>}
        <span className="flex flex-wrap items-center gap-2 pb-1.5">
          <FlagPill flag={latest.flag} />
          {change && (
            <StatusPill tone="grey" size="sm" icon={change.direction === "up" ? "arrow-right-up-line" : change.direction === "down" ? "arrow-right-down-line" : "subtract-line"}>
              {change.words}
            </StatusPill>
          )}
          {latest.range && <Tag>Range {rangeLabel(latest.range)}</Tag>}
        </span>
      </div>

      <div className="flex flex-col gap-1.5">
        <div className="relative ml-8 h-[168px] sm:h-[200px]">
          <svg role="img" aria-label={trendWords(series)} viewBox="0 0 100 100" preserveAspectRatio="none" className="absolute inset-0 block size-full overflow-visible">
            <defs>
              <linearGradient id={gradient} x1="0" y1="0" x2="0" y2="1">
                <stop offset="0" stopColor="var(--color-teal-icon)" stopOpacity={0.2} />
                <stop offset="1" stopColor="var(--color-teal-icon)" stopOpacity={0} />
              </linearGradient>
            </defs>
            {plot.band && <rect x="0" width="100" y={plot.band.top} height={Math.max(0, plot.band.bottom - plot.band.top)} rx="1" className="fill-green-fill" />}
            {plot.ticks.map((t) => (
              <line key={t.value} x1="0" x2="100" y1={t.y} y2={t.y} className="stroke-hairline" strokeWidth={1} vectorEffect="non-scaling-stroke" />
            ))}
            <path d={plot.area} fill={`url(#${gradient})`} />
            <path d={plot.line} fill="none" className="stroke-teal-icon" strokeWidth={2} strokeLinecap="round" vectorEffect="non-scaling-stroke" />
            <line x1={last.x} x2={last.x} y1="0" y2="100" className="stroke-ink" strokeOpacity={0.2} strokeWidth={1} vectorEffect="non-scaling-stroke" />
          </svg>
          {plot.ticks.map((t) => (
            <span key={t.value} aria-hidden="true" className="absolute right-full mr-2 -translate-y-1/2 font-mono text-[11px] leading-none text-subtle tabular-nums" style={{ top: `${t.y}%` }}>
              {t.label}
            </span>
          ))}
          <TipProvider>
            <div role="group" aria-label={`${series.name}, each result`} className="absolute inset-0">
              {pts.map((p, i) => {
                const isLast = i === pts.length - 1;
                return (
                  <div key={p.lab.id} className="absolute" style={{ left: `${p.x}%`, top: `${p.y}%` }}>
                    {flagged(p) && pts.length <= 8 && (
                      <span aria-hidden="true" className="absolute bottom-2.5 left-1/2 -translate-x-1/2 text-[10.5px] leading-none font-medium whitespace-nowrap text-amber-text">
                        {flagWord(p)}
                      </span>
                    )}
                    <Tip label={words(p)}>
                      <button
                        ref={(el) => {
                          refs.current[i] = el;
                        }}
                        type="button"
                        aria-label={words(p)}
                        tabIndex={i === current ? 0 : -1}
                        onFocus={() => setActive(i)}
                        onKeyDown={(e) => move(e, i)}
                        className="absolute top-0 left-0 flex size-6 -translate-x-1/2 -translate-y-1/2 cursor-default items-center justify-center rounded-full border-0 bg-transparent p-0 outline-none focus-visible:shadow-[0_0_0_2px_var(--color-brand)]"
                      >
                        <span
                          className={cn(
                            "block rounded-full ring-2",
                            isLast ? "size-3 ring-page" : "size-[9px]",
                            isLast ? (flagged(p) ? "bg-amber-icon" : "bg-ink") : flagged(p) ? "bg-amber-icon ring-page" : "bg-page ring-teal-icon",
                          )}
                        />
                      </button>
                    </Tip>
                  </div>
                );
              })}
            </div>
          </TipProvider>
        </div>
        <div aria-hidden="true" className="ml-8 flex justify-between font-mono text-[11px] text-subtle">
          <span>{shortDate(first.lab.date, last.lab.date)}</span>
          <span className="text-ink">{shortDate(last.lab.date, first.lab.date)}</span>
        </div>
      </div>

      <ul className="m-0 flex list-none flex-wrap gap-x-4 gap-y-1.5 p-0 text-[12px] text-muted">
        {plot.band && (
          <li className="flex items-center gap-1.5">
            <span aria-hidden="true" className="h-2 w-3 rounded-[2px] bg-green-fill shadow-[inset_0_0_0_1px_var(--color-green-icon)]" />
            Range on the record
          </li>
        )}
        {pts.some(flagged) && (
          <li className="flex items-center gap-1.5">
            <span aria-hidden="true" className="size-2 rounded-full bg-amber-icon" />
            Flagged high or low on the record
          </li>
        )}
        <li className="flex items-center gap-1.5">
          <span aria-hidden="true" className="size-2 rounded-full bg-ink" />
          Latest result
        </li>
      </ul>

      <div className="grid grid-cols-3 gap-4">
        <Stat label="Lowest" value={low.lab.value.trim()} tail={shortDate(low.lab.date, last.lab.date)} />
        <Stat label="Highest" value={high.lab.value.trim()} tail={shortDate(high.lab.date, last.lab.date)} />
        <Stat label="Results" value={pts.length} tail={`since ${shortDate(first.lab.date, last.lab.date)}`} />
      </div>
      {offChart > 0 && (
        <p className="m-0 text-[12.5px] text-muted">
          {offChart === 1 ? "1 more result is" : `${offChart} more results are`} in the table, not on the chart: not a number, in another unit or from the same day.
        </p>
      )}
    </figure>
  );
}
