import { type ComponentPropsWithoutRef, type CSSProperties } from 'react';
import { Slider as RSlider } from 'radix-ui';

type Mark = { value: number; label?: string };
type MarksProp = boolean | Array<number | Mark>;
type NotchPlacement = 'top' | 'bottom' | 'both' | 'none';

type KnobStyle = 'square' | 'round';

type SliderProps = Omit<ComponentPropsWithoutRef<typeof RSlider.Root>, 'className'> & {
  /**
   * Step marks.
   * - `true` — auto-generate one mark per `step` between `min` and `max`.
   * - array — explicit marks; numbers or `{ value, label }` for tick labels.
   */
  marks?: MarksProp;
  /**
   * Where to draw the notch ticks relative to the track.
   * `'bottom'` (default), `'top'`, `'both'`, or `'none'` to hide ticks
   * (labels still render when provided). No effect without `marks`.
   */
  notches?: NotchPlacement;
  /** Thumb shape. `'square'` (default) has a small border-radius; `'round'` is a full circle. */
  knobStyle?: KnobStyle;
  /**
   * Enable step-glide animation. Defaults to `true` when `marks` is set, `false` otherwise.
   * Explicitly setting this always overrides the default.
   */
  animated?: boolean;
  className?: string;
};

function resolveMarks(
  marks: MarksProp,
  min: number,
  max: number,
  step: number,
): Mark[] {
  if (Array.isArray(marks)) {
    return marks
      .map((m) => (typeof m === 'number' ? { value: m } : m))
      .filter((m) => m.value >= min && m.value <= max);
  }
  if (marks !== true) return [];
  if (!(step > 0) || max <= min) return [];
  const count = Math.floor((max - min) / step);
  // Guard against absurd notch counts (e.g. step=1 over a 0–1000 range).
  if (count < 1 || count > 100) return [];
  return Array.from({ length: count + 1 }, (_, i) => ({ value: min + i * step }));
}

const percent = (value: number, min: number, max: number) =>
  max === min ? 0 : (value - min) / (max - min);

const NotchLayer = ({
  marks,
  min,
  max,
  side,
}: {
  marks: Mark[];
  min: number;
  max: number;
  side: 'top' | 'bottom';
}) => (
  <div className={`mta-slider__notches mta-slider__notches--${side}`} aria-hidden>
    {marks.map((mark) => (
      <span
        key={mark.value}
        className="mta-slider__notch"
        style={{ '--p': percent(mark.value, min, max) } as CSSProperties}
      />
    ))}
  </div>
);

export const Slider = ({
  marks,
  notches = 'bottom',
  knobStyle = 'square',
  animated,
  min = 0,
  max = 100,
  step = 1,
  className,
  ...props
}: SliderProps) => {
  const resolved = marks != null ? resolveMarks(marks, min, max, step) : [];
  const hasMarks = resolved.length > 0;
  const hasLabels = resolved.some((m) => m.label != null);
  const showTop = hasMarks && (notches === 'top' || notches === 'both');
  const showBottom = hasMarks && (notches === 'bottom' || notches === 'both');
  const isAnimated = animated !== undefined ? animated : hasMarks;

  const cls = [
    'mta-slider',
    `mta-slider--knob-${knobStyle}`,
    isAnimated && 'mta-slider--animated',
    hasMarks && 'mta-slider--has-marks',
    hasLabels && 'mta-slider--has-labels',
    showTop && 'mta-slider--notch-top',
    showBottom && 'mta-slider--notch-bottom',
    className,
  ]
    .filter(Boolean)
    .join(' ');

  return (
    <RSlider.Root className={cls} min={min} max={max} step={step} {...props}>
      <RSlider.Track className="mta-slider__track">
        <RSlider.Range className="mta-slider__range" />
        {showTop && <NotchLayer marks={resolved} min={min} max={max} side="top" />}
        {showBottom && <NotchLayer marks={resolved} min={min} max={max} side="bottom" />}
        {hasLabels && (
          <div className="mta-slider__labels" aria-hidden>
            {resolved.map((mark) =>
              mark.label != null ? (
                <span
                  key={mark.value}
                  className="mta-slider__label"
                  style={{ '--p': percent(mark.value, min, max) } as CSSProperties}
                >
                  {mark.label}
                </span>
              ) : null,
            )}
          </div>
        )}
      </RSlider.Track>
      <RSlider.Thumb className="mta-slider__thumb" aria-label="Value" />
    </RSlider.Root>
  );
};

export const Stepper = ({
  onDecrement,
  onIncrement,
}: {
  onDecrement: () => void;
  onIncrement: () => void;
}) => (
  <div className="mta-stepper">
    <button type="button" onClick={onDecrement} aria-label="Decrease">
      −
    </button>
    <button type="button" onClick={onIncrement} aria-label="Increase">
      +
    </button>
  </div>
);
