"use client";

import { useState } from "react";

import type { MetricReturnDto } from "@enode/core/api/dtos/metrics";
import type { WorkoutRep, WorkoutSet } from "@enode/core/training/models";
import { compareRepsByCreated } from "@enode/core/training/rep-order";
import { countedRepCount } from "@enode/core/training/rep-scope";
import { useTranslation } from "@enode/core/translations/hooks";
import { RepPairList } from "@enode/ui/reps/rep-pair-list";
import { RepSelectActions } from "@enode/ui/reps/rep-select-actions";

import { ChevronDownIcon, ChevronUpIcon } from "./icons";

// Collapsible per-rep card. Summary line shows the tracked count and
// (when any rep is flagged invalid) the invalid count. Expanded body
// lists every rep — tap-to-select toggles the rep into a multi-select;
// the floating action pill below the card surfaces Toggle valid /
// Delete / Done.
export function RepsCard({
  set,
  focusMetrics,
  onRepsChange,
}: {
  set: WorkoutSet;
  focusMetrics: MetricReturnDto[];
  onRepsChange: (reps: WorkoutRep[]) => void;
}) {
  const { t } = useTranslation();
  const [expanded, setExpanded] = useState(false);
  const [selectedIDs, setSelectedIDs] = useState<Set<string>>(new Set());

  // Snap selection back to the empty set whenever the underlying set
  // changes (the parent reseats the editor on a new set), so a stale
  // id list doesn't leak across edits.
  const [prevSetId, setPrevSetId] = useState(set.id);
  if (prevSetId !== set.id) {
    setPrevSetId(set.id);
    setSelectedIDs(new Set());
    setExpanded(false);
  }

  // Only for the summary counts below — the list itself sorts and pairs the
  // reps in `RepPairList`.
  const reps = [...set.reps].sort(compareRepsByCreated);
  // Tracked-rep count follows the shared `isCounted` rule (valid concentrics)
  // so it always equals the set's repCount measurement — toggling a rep
  // invalid drops the headline immediately. The invalid count next to it stays
  // across BOTH phases, so the coach still sees everything flagged.
  const summary = t(
    "{count, plural, one {# Tracked rep} other {# Tracked reps}}",
    { count: countedRepCount(reps) },
  );
  const invalidCount = reps.filter((r) => !r.valid).length;

  function toggleSelected(id: string): void {
    setSelectedIDs((prev) => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id);
      else next.add(id);
      return next;
    });
  }

  function clearSelection(): void {
    setSelectedIDs(new Set());
  }

  // Flip `valid` on every selected rep — each rep toggles independently,
  // so a mixed selection ends up still mixed but inverted. Selection
  // stays so the user can chain further actions.
  function handleToggleValid(): void {
    onRepsChange(
      set.reps.map((r) =>
        selectedIDs.has(r.id) ? { ...r, valid: !r.valid } : r,
      ),
    );
  }

  function handleDeleteSelected(): void {
    onRepsChange(set.reps.filter((r) => !selectedIDs.has(r.id)));
    clearSelection();
  }

  return (
    <>
      <div className="overflow-hidden rounded-2xl border border-surface-stroke bg-surface-base">
        <button
          type="button"
          onClick={() => setExpanded((v) => !v)}
          aria-expanded={expanded}
          className="flex w-full items-center gap-3 px-4 py-3 text-left hover:bg-surface-base-muted"
        >
          <span className="body-prominent">{summary}</span>
          <span className="flex-1" />
          {invalidCount > 0 && (
            <span className="stat-small uppercase text-feedback-error">
              {t("{count} invalid", { count: invalidCount })}
            </span>
          )}
          {expanded ? (
            <ChevronUpIcon className="size-4 text-foreground-muted" />
          ) : (
            <ChevronDownIcon className="size-4 text-foreground-muted" />
          )}
        </button>
        {expanded && (
          <>
            <div className="border-t border-surface-stroke" />
            {/* One shared scroll container for every row — horizontal AND
                vertical — so the fixed-width columns stay aligned across
                rows while they scroll together. The inner list is
                max-content wide (never narrower than the card) so row
                backgrounds and separators span the full scroll width. */}
            <div className="max-h-72 overflow-auto">
              <RepPairList
                set={set}
                metrics={focusMetrics}
                selectedIDs={selectedIDs}
                onToggleRep={toggleSelected}
              />
            </div>
          </>
        )}
      </div>
      {selectedIDs.size > 0 && (
        <div className="flex justify-center pt-1">
          <RepSelectActions
            onToggleValid={handleToggleValid}
            onDelete={handleDeleteSelected}
            onDone={clearSelection}
          />
        </div>
      )}
    </>
  );
}
