/**
 * SegmentedControl — a single-select button group with correct ARIA semantics.
 *
 * A "pick one of N" toolbar control (fit modes, draft/published view) is a
 * **radio group**, not a row of independent toggle buttons. Modeled per
 * WAI-ARIA APG → Radio Group (see docs/architecture/10 §5): `role="radiogroup"`
 * wrapping `role="radio"` options with `aria-checked`, a **roving tabindex**
 * (only the selected option is in the tab order), and Arrow/Home/End keys that
 * move focus *and* select. Native `<button>` gives Enter/Space-to-select for
 * free. One widget so every segmented control gets the same, documented model.
 *
 * The control owns its base look (SegmentedControl.module.css) so every instance
 * stays visually consistent; call sites may pass class props for layout-specific
 * additions, which compose on top of the base classes.
 */

import { useRef } from 'react';
import styles from './SegmentedControl.module.css';

export interface SegmentedOption<T extends string> {
  value: T;
  label: string;
  /**
   * When true the option is shown but not selectable: `aria-disabled`, never the
   * roving tab stop, skipped by Arrow/Home/End, and ignores clicks (APG radio —
   * disabled options remain perceivable but inert). The selected value must never
   * be a disabled option (callers keep the active value valid).
   */
  disabled?: boolean;
  /** Full accessible name + hover tooltip when `label` is a terse abbreviation. */
  title?: string;
}

interface SegmentedControlProps<T extends string> {
  /** Accessible name for the group. */
  label: string;
  options: ReadonlyArray<SegmentedOption<T>>;
  value: T;
  onChange: (value: T) => void;
  /** Extra class for the radiogroup container (composed onto the base look). */
  className?: string;
  /** Extra class for each option button (composed onto the base look). */
  optionClassName?: string;
  /** Extra class applied to the selected option (composed onto the base active look). */
  activeClassName?: string;
}

export function SegmentedControl<T extends string>({
  label,
  options,
  value,
  onChange,
  className,
  optionClassName,
  activeClassName,
}: SegmentedControlProps<T>) {
  const refs = useRef<Array<HTMLButtonElement | null>>([]);

  /**
   * Select the first **enabled** option reached from `from` stepping by `dir`
   * (wrapping), and move focus to it (APG radio). Skips disabled options so arrow
   * keys never land on an inert segment; a group with no enabled option is a no-op.
   */
  const move = (from: number, dir: 1 | -1) => {
    const n = options.length;
    for (let step = 1; step <= n; step++) {
      const idx = (((from + dir * step) % n) + n) % n;
      if (!options[idx].disabled) {
        onChange(options[idx].value);
        refs.current[idx]?.focus();
        return;
      }
    }
  };

  const onKeyDown = (e: React.KeyboardEvent, index: number) => {
    switch (e.key) {
      case 'ArrowRight':
      case 'ArrowDown':
        move(index, 1);
        break;
      case 'ArrowLeft':
      case 'ArrowUp':
        move(index, -1);
        break;
      case 'Home':
        move(-1, 1); // first enabled from the start
        break;
      case 'End':
        move(0, -1); // last enabled from the end
        break;
      default:
        return; // not ours — let it bubble
    }
    e.preventDefault();
  };

  return (
    <div
      className={[styles.group, className].filter(Boolean).join(' ')}
      role="radiogroup"
      aria-label={label}
    >
      {options.map((opt, i) => {
        const selected = opt.value === value;
        return (
          <button
            key={opt.value}
            ref={(el) => {
              refs.current[i] = el;
            }}
            type="button"
            role="radio"
            aria-checked={selected}
            aria-disabled={opt.disabled || undefined}
            aria-label={opt.title}
            title={opt.title}
            // Roving tabindex: only the selected (always enabled) option is a tab
            // stop; arrows move within the group, skipping disabled options. A
            // disabled option is never a tab stop.
            tabIndex={selected && !opt.disabled ? 0 : -1}
            className={[
              styles.option,
              optionClassName,
              selected && styles.active,
              selected && activeClassName,
            ]
              .filter(Boolean)
              .join(' ')}
            onClick={() => {
              if (!opt.disabled) onChange(opt.value);
            }}
            onKeyDown={(e) => onKeyDown(e, i)}
          >
            {opt.label}
          </button>
        );
      })}
    </div>
  );
}
