import { ChevronLeft, ChevronRight, Circle, Pause, Play, RotateCcw } from 'lucide-react';
import { PLAYBACK_SPEEDS } from '@/lib/demo-kit/player';
import type { PlaybackSpeed } from '@/lib/demo-kit/player';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import { formatDate } from './format';
import { SegmentedControl } from './SegmentedControl';

/** `PlaybackSpeed` is `1 | 2 | 4 | 'instant'`; the control speaks strings. */
const SPEED_OPTIONS = PLAYBACK_SPEEDS.map((speed) => ({
  value: String(speed),
  label: speed === 'instant' ? 'Instant' : `${speed}x`,
  title: speed === 'instant' ? 'Jump straight to the end of the run' : `${speed} times real pace`,
}));

function toSpeed(value: string): PlaybackSpeed {
  return value === 'instant' ? 'instant' : (Number(value) as 1 | 2 | 4);
}

export interface TracePlayerProps {
  playing: boolean;
  onPlayingChange: (playing: boolean) => void;
  speed: PlaybackSpeed;
  onSpeedChange: (speed: PlaybackSpeed) => void;
  onRestart: () => void;
  step: number;
  stepCount: number;
  onStepChange: (next: number) => void;
  /** 0..1 through the current step, from the player. Drives the hairline. */
  progress?: number;
  /**
   * False when any step's dwell was invented because the trace carried no
   * duration. Surfaced, not hidden: the player's whole claim is that the
   * pacing is the model's, and where it is not, it says so.
   */
  timingIsReal?: boolean;
  /** Straight off the run. Never a marketing name for the model. */
  model: string;
  capturedAt: string;
  /** Present only on an `intervened` run; the contract requires it there. */
  intervention?: string;
  className?: string;
}

/**
 * Transport controls for a recorded rollout.
 *
 * There is no spinner in this component and there never should be. A spinner
 * implies a request is in flight; nothing here is live, and an exec who
 * believes they are watching a model think in real time has been misled by the
 * UI rather than the copy. Hence the permanent badge — not a disclosure tucked
 * into a footnote, but a fixture of the transport bar for the whole session.
 */
export function TracePlayer({
  playing,
  onPlayingChange,
  speed,
  onSpeedChange,
  onRestart,
  step,
  stepCount,
  onStepChange,
  progress = 0,
  timingIsReal = true,
  model,
  capturedAt,
  intervention,
  className,
}: TracePlayerProps) {
  const canPlay = stepCount > 1;

  return (
    <div className={cn('card overflow-hidden', className)}>
      <div className="flex flex-wrap items-center gap-x-3 gap-y-2 p-2 sm:gap-x-4">
        <div className="flex items-center gap-1">
          <Button
            variant="ghost"
            size="icon-touch"
            onClick={() => onStepChange(Math.max(step - 1, 0))}
            disabled={step <= 0}
            aria-label="Previous step"
          >
            <ChevronLeft aria-hidden="true" />
          </Button>
          <Button
            size="touch"
            onClick={() => onPlayingChange(!playing)}
            disabled={!canPlay}
            aria-label={playing ? 'Pause the recorded run' : 'Play the recorded run'}
            aria-keyshortcuts="Space"
          >
            {playing ? <Pause aria-hidden="true" /> : <Play aria-hidden="true" />}
          </Button>
          <Button
            variant="ghost"
            size="icon-touch"
            onClick={() => onStepChange(Math.min(step + 1, Math.max(stepCount - 1, 0)))}
            disabled={step >= stepCount - 1}
            aria-label="Next step"
          >
            <ChevronRight aria-hidden="true" />
          </Button>
          <Button
            variant="ghost"
            size="icon-touch"
            onClick={onRestart}
            aria-label="Restart from the first step"
          >
            <RotateCcw aria-hidden="true" />
          </Button>
        </div>

        <p className="nums text-sm text-muted">
          Step <span className="font-semibold text-fg">{Math.min(step + 1, stepCount)}</span> of{' '}
          {stepCount}
        </p>

        <SegmentedControl
          label="Playback speed"
          options={SPEED_OPTIONS}
          value={String(speed)}
          onChange={(next) => onSpeedChange(toSpeed(next))}
        />

        <RecordedBadge
          model={model}
          capturedAt={capturedAt}
          {...(intervention ? { intervention } : {})}
          {...(timingIsReal ? {} : { timingNote: 'pacing approximate' })}
        />
      </div>

      {/*
        A hairline, not a scrubber: the step chips below are the scrubber. It
        exists so the pause between steps reads as time passing rather than as
        the page having stopped. `transition-[width]` covers the ~15 Hz at which
        the player pushes progress — without it the bar visibly ratchets.
      */}
      <div className="h-0.5 w-full bg-surface-2" aria-hidden="true">
        <div
          className="h-full bg-brand transition-[width] duration-1 ease-enter"
          style={{
            width: `${
              stepCount <= 1 ? 0 : ((step + Math.min(Math.max(progress, 0), 1)) / (stepCount - 1)) * 100
            }%`,
          }}
        />
      </div>
    </div>
  );
}

export interface RecordedBadgeProps {
  model: string;
  capturedAt: string;
  intervention?: string;
  /** Rendered when the playback pacing is not the model's own. */
  timingNote?: string;
  className?: string;
}

export function RecordedBadge({
  model,
  capturedAt,
  intervention,
  timingNote,
  className,
}: RecordedBadgeProps) {
  return (
    <p
      className={cn(
        'ml-auto flex flex-wrap items-center gap-x-1.5 gap-y-1 rounded-lg bg-surface-2 px-2.5 py-1.5 text-xs text-muted',
        className,
      )}
    >
      <Circle className="size-2 shrink-0 fill-muted text-muted" aria-hidden="true" />
      <span className="font-medium text-fg">Recorded run</span>
      <span aria-hidden="true">·</span>
      <span className="nums font-mono">{model}</span>
      <span aria-hidden="true">·</span>
      <span className="nums">{formatDate(capturedAt)}</span>
      {intervention ? (
        <span className="rounded-md bg-accent-subtle px-1.5 py-0.5 font-medium text-accent-fg">
          {intervention}
        </span>
      ) : null}
      {timingNote ? (
        <span className="rounded-md border border-border px-1.5 py-0.5">{timingNote}</span>
      ) : null}
    </p>
  );
}
