'use client'

import { useEffect, useRef, useState } from 'react'

import { useI18n } from '@/components/providers/i18n-provider'
import { Button } from '@/components/ui/controls'
import { cn } from '@/lib/cn'

/**
 * The twin's timeline (§21, §27).
 *
 * One control drives both the time machine and the cascade replay, because to an
 * operator they are the same gesture: move through time and watch the scene change. Play
 * advances through the offsets the model was actually evaluated at rather than
 * interpolating between them — an interpolated frame would be a picture of a moment
 * nobody computed.
 */
export interface TimelineFrame {
  offsetMin: number
  peakRisk: number
  stabilityIndex: number
  breached: boolean
}

const SPEEDS = [1, 2, 4] as const

export function TwinTimeline({
  frames,
  index,
  onIndexChange,
  className,
  label,
}: {
  frames: TimelineFrame[]
  index: number
  onIndexChange: (next: number) => void
  className?: string
  label?: string
}) {
  const { t, n } = useI18n()
  const [playing, setPlaying] = useState(false)
  const [speed, setSpeed] = useState<(typeof SPEEDS)[number]>(1)
  const timer = useRef<ReturnType<typeof setInterval> | null>(null)

  useEffect(() => {
    if (timer.current) clearInterval(timer.current)
    if (!playing || frames.length === 0) return

    timer.current = setInterval(() => {
      onIndexChange(index + 1 >= frames.length ? 0 : index + 1)
    }, 1600 / speed)

    return () => {
      if (timer.current) clearInterval(timer.current)
    }
  }, [playing, speed, index, frames.length, onIndexChange])

  // Stopping at the end rather than looping silently: an audience needs to see that the
  // sequence finished.
  useEffect(() => {
    if (playing && frames.length > 0 && index === frames.length - 1) {
      const stop = setTimeout(() => setPlaying(false), 1600 / speed)
      return () => clearTimeout(stop)
    }
  }, [playing, index, frames.length, speed])

  if (frames.length === 0) return null
  const current = frames[Math.min(index, frames.length - 1)]

  return (
    <div className={cn('rounded-[--radius-panel] border border-border bg-surface/70 p-3', className)}>
      <div className="flex flex-wrap items-center justify-between gap-3">
        <div className="flex items-center gap-1.5">
          <Button
            size="sm"
            variant="ghost"
            onClick={() => onIndexChange(0)}
            aria-label={t('twin.rewind')}
            title={t('twin.rewind')}
          >
            ⏮
          </Button>
          <Button
            size="sm"
            variant={playing ? 'secondary' : 'primary'}
            onClick={() => setPlaying((current) => !current)}
            aria-label={playing ? t('twin.pause') : t('twin.play')}
          >
            {playing ? '❚❚' : '▶'}
          </Button>
          <Button
            size="sm"
            variant="ghost"
            onClick={() => onIndexChange(Math.min(index + 1, frames.length - 1))}
            aria-label={t('twin.step')}
            title={t('twin.step')}
          >
            ⏭
          </Button>
          <div className="ms-2 flex items-center gap-1">
            {SPEEDS.map((option) => (
              <button
                key={option}
                type="button"
                onClick={() => setSpeed(option)}
                aria-pressed={speed === option}
                className={cn(
                  'rounded px-1.5 py-0.5 text-[10px] font-medium transition-colors',
                  speed === option ? 'bg-brand/18 text-brand' : 'text-text-faint hover:text-text',
                )}
              >
                {option}×
              </button>
            ))}
          </div>
        </div>

        <div className="flex items-baseline gap-3 text-[11px]">
          <span className="text-text-faint">{label ?? t('twin.timeline')}</span>
          <span className="tnum font-semibold text-text">
            {current.offsetMin === 0 ? t('timeMachine.offsets.now') : `+${n(current.offsetMin)} ${t('common.minutes')}`}
          </span>
          <span className={cn('tnum font-medium', current.breached ? 'text-critical' : 'text-text-muted')}>
            {n(current.peakRisk, { maximumFractionDigits: 0 })}%
          </span>
        </div>
      </div>

      {/* The track doubles as a risk profile: the shape of the run is visible without
          playing it. */}
      <div className="mt-3 flex items-end gap-1" role="group" aria-label={t('twin.timeline')}>
        {frames.map((frame, position) => {
          const active = position === index
          const height = 6 + Math.min(frame.peakRisk, 100) * 0.28
          return (
            <button
              key={frame.offsetMin}
              type="button"
              onClick={() => onIndexChange(position)}
              aria-label={`+${frame.offsetMin} min`}
              aria-current={active}
              className="group relative flex-1"
              style={{ height: 40 }}
            >
              <span
                className={cn(
                  // `inset-x-0` rather than `w-full`: an absolutely positioned child with
                  // only a width resolves its start edge from the static position, which
                  // lands outside the row under RTL.
                  'absolute inset-x-0 bottom-0 rounded-t transition-all',
                  frame.breached
                    ? 'bg-critical'
                    : frame.peakRisk >= 60
                      ? 'bg-warning'
                      : frame.peakRisk >= 40
                        ? 'bg-watch'
                        : 'bg-normal',
                  active ? 'opacity-100' : 'opacity-45 group-hover:opacity-75',
                )}
                style={{ height }}
              />
            </button>
          )
        })}
      </div>
      <div className="mt-1 flex justify-between text-[9px] text-text-faint">
        <span>{t('timeMachine.offsets.now')}</span>
        <span>+{n(frames[frames.length - 1].offsetMin)} {t('common.minutes')}</span>
      </div>
    </div>
  )
}
