/**
 * The demand motion, across the top.
 *
 * The eight open stages are the spine of the whole feature — Motion binds
 * artefacts to them rather than inventing a second pipeline — so the rail is
 * the one control that appears on the OS home, the library filter and the
 * engagement workspace, and it has to mean the same thing in all three.
 *
 * The closed stages are absent because `DEMAND_OPEN_STAGES` is the default:
 * a won or lost deal has left the motion, and a rail segment nobody can file
 * work against is a segment people ask about once a quarter.
 *
 * It scrolls horizontally rather than wrapping. Wrapping puts `procurement`
 * under `qualification` at 393px, which reads as a second row of the sequence
 * starting over; a scroller keeps the order legible and keeps the overflow
 * inside this box instead of dragging the page sideways.
 */
import { DEMAND_OPEN_STAGES, DEMAND_STAGE_LABELS, type DemandStage } from '@pig/core';
import { cn } from '@/components/ui';

/** Zero takes the plural, as it does in English: "no templates", "0 templates". */
function noun(count: number, [one, many]: readonly [string, string]): string {
  return count === 1 ? one : many;
}

export function StageRail({
  counts,
  coverage,
  stages = DEMAND_OPEN_STAGES,
  countNoun = ['engagement', 'engagements'],
  coverageNoun = ['template', 'templates'],
  activeStage,
  onSelect,
  className,
}: {
  /** The headline figure per stage. A stage absent from the record reads as 0. */
  counts: Partial<Record<DemandStage, number>>;
  /** The second figure, if the caller has one — library cover, typically. */
  coverage?: Partial<Record<DemandStage, number>>;
  stages?: readonly DemandStage[];
  /** Singular and plural, because "1 engagements" is read aloud by a screen reader. */
  countNoun?: readonly [one: string, many: string];
  coverageNoun?: readonly [one: string, many: string];
  activeStage?: DemandStage | null;
  /** Omit to render a read-only rail: a non-interactive button is a trap. */
  onSelect?: (stage: DemandStage) => void;
  className?: string;
}) {
  return (
    <div className={cn('scroll-x min-w-0 pb-1', className)}>
      <ol className="flex min-w-0 items-stretch gap-2">
        {stages.map((stage) => {
          const count = counts[stage] ?? 0;
          const covered = coverage?.[stage];
          const active = activeStage === stage;

          const body = (
            <>
              {/*
                * Wraps rather than truncates. The stage names are a fixed
                * vocabulary of eight words we control, and truncation was
                * silently rendering "QUALIFICATIO" and "PROCUREMEN" — losing a
                * letter is worse than taking a second line. Two attempts to fix
                * this by widening the card (8.5rem, then 10, then 11) each
                * looked right in a DOM measurement and still clipped on the
                * rendered frame, which is the argument for not depending on a
                * magic width at all.
                */}
              <span className="text-xs font-medium uppercase leading-tight tracking-wide text-muted">
                {DEMAND_STAGE_LABELS[stage]}
              </span>
              <span className={cn('nums text-2xl font-semibold leading-none', count === 0 && 'text-muted')}>
                {count}
              </span>
              {covered === undefined ? null : (
                // A stage with no template is the finding this rail exists to
                // surface — it is where the motion stops repeating — so it is
                // called out rather than shown as another grey zero.
                <span className={cn('nums truncate text-xs', covered === 0 ? 'text-warning' : 'text-muted')}>
                  {covered === 0 ? `No ${coverageNoun[1]}` : `${covered} ${noun(covered, coverageNoun)}`}
                </span>
              )}
            </>
          );

          const shape = cn(
            // 10rem, not 8.5, so the common labels sit on one line; the two
            // longest wrap to two rather than losing a letter.
            'tap flex w-40 shrink-0 flex-col justify-between gap-2 rounded-xl border p-3 text-left',
            active ? 'border-brand bg-surface-2' : 'border-border bg-surface',
          );

          return (
            /*
             * `shrink-0` belongs on the LI, not only on the card inside it.
             * Without it the list item shrank to its flex share — 88px against
             * the card's 160 — and every card was overpainted by the next one,
             * so the labels lost their last letters with no ellipsis to show
             * for it. Three attempts to fix that by widening the card did
             * nothing, because the card was never the thing being measured.
             */
            <li key={stage} className="flex shrink-0">
              {onSelect ? (
                <button
                  type="button"
                  onClick={() => onSelect(stage)}
                  aria-pressed={active}
                  aria-label={`${DEMAND_STAGE_LABELS[stage]}: ${count} ${noun(count, countNoun)}`}
                  className={cn(shape, 'transition-colors hover:bg-surface-2')}
                >
                  {body}
                </button>
              ) : (
                <div className={shape} aria-label={`${DEMAND_STAGE_LABELS[stage]}: ${count} ${noun(count, countNoun)}`}>
                  {body}
                </div>
              )}
            </li>
          );
        })}
      </ol>
    </div>
  );
}
