"use client";

import { useState } from "react";
import { useTranslation } from "@enode/core/translations/hooks";
import { Sparkline } from "./chart-legend";
import { changeStrength, matrixRowOrder } from "./trend-model";
import type { TrendCell, TrendGridRow } from "./trend-small-multiples";

// The Trend's team matrix, for many athletes × many lifts: one cell per
// athlete × lift holding the same change the panels and the legend show, tinted
// by its direction and size. It answers "where is something off?" at a glance —
// a tinted row points at an athlete, a tinted column at a lift, a single cell at
// that athlete on that lift. Rows with the biggest changes come first; a column
// header sorts by that lift. Tapping a cell opens that series' trend.

type CellValue = TrendCell | "hidden" | null;

/** Tint per direction and strength (see `changeStrength`). Literal classes so Tailwind keeps them. */
const UP_TINT = ["", "bg-foreground-platinum-green/15", "bg-foreground-platinum-green/30", "bg-foreground-platinum-green/50"];
const DOWN_TINT = ["", "bg-feedback-error/15", "bg-feedback-error/30", "bg-feedback-error/50"];

function tintClass(change: number | null): string {
  const strength = changeStrength(change);
  if (strength === 0 || change == null) return "bg-surface-base-muted/60";
  return change > 0 ? UP_TINT[strength] : DOWN_TINT[strength];
}

function changeOf(cell: CellValue): number | null {
  return cell && cell !== "hidden" ? cell.change : null;
}

export function TrendMatrix({
  exercises,
  rows,
  focusedId,
  onOpen,
  maxRows,
  maxColumns,
  compact = false,
  sortColumn: controlledSort,
  onSortColumn,
}: {
  /** Column names, most-trained exercise first (see `exerciseColumns`). */
  exercises: string[];
  rows: TrendGridRow[];
  focusedId: string | null;
  /** Opens one series' trend (a cell's series id). */
  onOpen: (seriesId: string) => void;
  /** A board tile: only the rows with the biggest changes, the rest counted. */
  maxRows?: number;
  /** A board tile: only the most-trained lifts, the rest counted. */
  maxColumns?: number;
  /** A thumbnail: tinted cells only, no names, numbers or notes. */
  compact?: boolean;
  /** The sorted lift column (null: biggest change), when the parent keeps it (it survives leaving the matrix). */
  sortColumn?: number | null;
  onSortColumn?: (column: number | null) => void;
}) {
  const { t } = useTranslation();
  // null = biggest change in the row; a number = that column's change.
  const [ownSort, setOwnSort] = useState<number | null>(null);
  // The cell under the pointer (or keyboard focus): its trend in a popover.
  const [peek, setPeek] = useState<CellPeek | null>(null);
  const sortColumn = onSortColumn ? (controlledSort ?? null) : ownSort;
  const setSortColumn = onSortColumn ?? setOwnSort;
  const shownExercises = maxColumns != null ? exercises.slice(0, maxColumns) : exercises;
  const column = sortColumn != null && sortColumn < shownExercises.length ? sortColumn : null;
  const ordered = matrixRowOrder(rows, (row) => row.cells.map(changeOf), column ?? undefined);
  const sorted = (maxRows != null ? ordered.slice(0, maxRows) : ordered).map((row) => ({
    ...row,
    cells: row.cells.slice(0, shownExercises.length),
  }));
  const moreRows = ordered.length - sorted.length;
  const moreColumns = exercises.length - shownExercises.length;

  if (compact) {
    return (
      <div
        aria-hidden
        className="grid h-full gap-0.5"
        style={{ gridTemplateColumns: `repeat(${shownExercises.length}, minmax(0, 1fr))` }}
      >
        {sorted.flatMap((row) =>
          row.cells.map((cell, index) => (
            <span key={`${row.id}-${index}`} className={`rounded-sm ${tintClass(changeOf(cell))}`} />
          )),
        )}
      </div>
    );
  }

  return (
    <div>
      <div className="overflow-x-auto">
        <div
          role="table"
          aria-label={t("Team matrix")}
          className="grid min-w-max gap-1"
          // One name column, then one column per exercise (a runtime count).
          style={{
            gridTemplateColumns: `minmax(6rem, 10rem) repeat(${shownExercises.length}, minmax(5.5rem, 1fr))`,
          }}
        >
          <button
            type="button"
            onClick={() => setSortColumn(null)}
            aria-pressed={column == null}
            className={`truncate pb-1 text-left body-small font-medium transition-colors ${
              column == null ? "text-foreground-default" : "text-foreground-muted hover:text-foreground-default"
            }`}
          >
            {t("Biggest change")}
            {column == null ? " ↓" : ""}
          </button>
          {shownExercises.map((exercise, index) => (
            <button
              key={exercise}
              type="button"
              onClick={() => setSortColumn(index)}
              aria-pressed={index === column}
              title={t("Sort rows by {exercise}", { exercise })}
              className={`truncate px-1 pb-1 text-left body-small font-medium transition-colors ${
                index === column
                  ? "text-foreground-default"
                  : "text-foreground-muted hover:text-foreground-default"
              }`}
            >
              {exercise}
              {index === column ? " ↓" : ""}
            </button>
          ))}
          {sorted.map((row) => (
            <MatrixRow
              key={row.id}
              row={row}
              exercises={shownExercises}
              focusedId={focusedId}
              onOpen={onOpen}
              // A peek at a cell's trend without leaving the matrix (not on a tile, which only reads).
              onPeek={maxRows == null ? setPeek : undefined}
            />
          ))}
        </div>
      </div>
      {moreRows > 0 || moreColumns > 0 ? (
        <p className="body-small mt-1 text-foreground-muted">
          {moreRows > 0 && moreColumns > 0
            ? t("⋯ {rows, plural, one {# more athlete} other {# more athletes}} and {columns, plural, one {# more lift} other {# more lifts}} in the full chart", { rows: moreRows, columns: moreColumns })
            : moreRows > 0
              ? t("⋯ {count, plural, one {# more athlete} other {# more athletes}} with smaller changes", { count: moreRows })
              : t("⋯ {count, plural, one {# more lift} other {# more lifts}} in the full chart", { count: moreColumns })}
        </p>
      ) : null}
      {peek ? <CellPopover peek={peek} /> : null}
      {maxRows == null ? (
        <p className="body-small mt-2 text-foreground-muted">
          {t("Each cell is the change of that athlete's trend line on that lift · green up, red down · tap a cell for its trend")}
        </p>
      ) : null}
    </div>
  );
}

/** A matrix cell the coach is looking at, with where it sits on screen. */
type CellPeek = { cell: TrendCell; athlete: string; exercise: string; rect: DOMRect };

// The cell's trend as a small line, its sessions, change and load, under the
// cell: enough to check a tint without opening the series.
function CellPopover({ peek }: { peek: CellPeek }) {
  const { t } = useTranslation();
  const { cell, rect } = peek;
  const left = Math.max(8, Math.min(rect.left + rect.width / 2 - 110, window.innerWidth - 228));
  const below = rect.bottom + 6 + 96 < window.innerHeight;
  return (
    <div
      role="tooltip"
      className="pointer-events-none fixed z-[60] flex w-[220px] flex-col gap-1 rounded-xl border border-surface-stroke bg-surface-base p-2.5 shadow-floating"
      style={{ left, top: below ? rect.bottom + 6 : rect.top - 102 }}
    >
      <p className="body-small truncate font-semibold text-foreground-default">
        {peek.exercise ? `${peek.athlete} · ${peek.exercise}` : peek.athlete}
      </p>
      <Sparkline points={cell.points} color="currentColor" />
      <p className="body-small text-foreground-muted">
        {t("{count, plural, one {# session} other {# sessions}}", { count: cell.points.length })}
        {" · "}
        {cell.changeText ?? t("Too few sessions for a trend")}
        {cell.loadText ? ` · ${cell.loadText}` : ""}
      </p>
    </div>
  );
}

function MatrixRow({
  row,
  exercises,
  focusedId,
  onOpen,
  onPeek,
}: {
  row: TrendGridRow;
  exercises: readonly string[];
  focusedId: string | null;
  onOpen: (seriesId: string) => void;
  onPeek?: (peek: CellPeek | null) => void;
}) {
  const peekAt = (cell: TrendCell, index: number) => (event: { currentTarget: Element }) =>
    onPeek?.({ cell, athlete: row.name, exercise: exercises[index] ?? "", rect: event.currentTarget.getBoundingClientRect() });
  const { t } = useTranslation();
  return (
    <>
      <span
        role="rowheader"
        className={`truncate pr-2 body-small font-medium leading-8 ${
          row.id === focusedId ? "text-foreground-red-orange" : "text-foreground-default"
        }`}
      >
        {row.name}
      </span>
      {row.cells.map((cell, index) =>
        cell && cell !== "hidden" ? (
          <button
            key={cell.id}
            type="button"
            role="cell"
            onClick={() => onOpen(cell.id)}
            onMouseEnter={onPeek ? peekAt(cell, index) : undefined}
            onFocus={onPeek ? peekAt(cell, index) : undefined}
            onMouseLeave={onPeek ? () => onPeek(null) : undefined}
            onBlur={onPeek ? () => onPeek(null) : undefined}
            title={
              onPeek
                ? undefined
                : cell.changeText == null
                  ? t("Too few sessions for a trend")
                  : t("{count, plural, one {# session} other {# sessions}}", { count: cell.points.length })
            }
            className={`flex h-8 items-center justify-center rounded-lg px-2 stat-small tabular-nums text-foreground-default transition-shadow hover:ring-2 hover:ring-foreground-default/30 ${tintClass(
              cell.change,
            )} ${cell.id === focusedId ? "ring-2 ring-foreground-red-orange" : ""}`}
          >
            {cell.changeText ?? "—"}
          </button>
        ) : (
          <span
            key={`empty-${index}`}
            role="cell"
            className="flex h-8 items-center justify-center rounded-lg border border-dashed border-surface-stroke text-[11px] text-foreground-more-muted"
          >
            {cell === "hidden" ? t("Hidden") : t("No sessions")}
          </span>
        ),
      )}
    </>
  );
}
