import { useRef, useState, useCallback, useEffect, forwardRef, useImperativeHandle } from 'react'
import { endpoints, authenticatedApiUrl } from '../../api'
import type { Media } from '../../types'

interface Props {
  media: Media
  onTimeUpdate: (time: number) => void
  /** Fires when the <video> emits 'play'/'pause' (no polling needed). */
  onPlayingChange?: (playing: boolean) => void
  /** Fires when the <video> reports a valid duration. */
  onDurationChange?: (duration: number) => void
  /** Fires when the <video> mute state changes (incl. the M keyboard shortcut). */
  onMutedChange?: (muted: boolean) => void
  children?: React.ReactNode
}

export interface VideoPlayerHandle {
  seek: (seconds: number) => void
  getVideoElement: () => HTMLVideoElement | null
  play: () => void
  pause: () => void
  toggle: () => void
  isPlaying: () => boolean
  frameStep: (deltaFrames: number) => void
  getDuration: () => number
  getCurrentTime: () => number
  getFrameRate: () => number
  getCurrentFrame: () => number
  getTotalFrames: () => number
  getVolume: () => number
  setVolume: (v: number) => void
  toggleMute: () => void
  isMuted: () => boolean
}

const FPS = 30

const VideoPlayer = forwardRef<VideoPlayerHandle, Props>(function VideoPlayer({
  media, onTimeUpdate, onPlayingChange, onDurationChange, onMutedChange, children,
}, ref) {
  const videoRef = useRef<HTMLVideoElement>(null)
  const [error, setError] = useState<string | null>(null)
  const [muted, setMuted] = useState(false)

  const notifyMuted = useCallback((m: boolean) => {
    setMuted(m)
    onMutedChange?.(m)
  }, [onMutedChange])

  const videoUrl = media.path.startsWith('blob:')
    ? media.path
    : authenticatedApiUrl(endpoints.annotations.mediaFile(media.id))

  const stepFrames = useCallback((count: number) => {
    const video = videoRef.current
    if (!video) return
    video.currentTime = Math.max(0, Math.min(video.duration || 0, video.currentTime + count / FPS))
  }, [])

  const togglePlay = useCallback(() => {
    const v = videoRef.current
    if (!v) return
    if (v.paused) v.play().catch(() => {})
    else v.pause()
  }, [])

  useImperativeHandle(ref, () => ({
    seek(seconds: number) {
      const v = videoRef.current
      if (v) v.currentTime = seconds
    },
    getVideoElement() { return videoRef.current },
    play() { videoRef.current?.play().catch(() => {}) },
    pause() { videoRef.current?.pause() },
    toggle() { togglePlay() },
    isPlaying() { return !!videoRef.current && !videoRef.current.paused },
    frameStep(delta) { stepFrames(delta) },
    getDuration() { return videoRef.current?.duration ?? 0 },
    getCurrentTime() { return videoRef.current?.currentTime ?? 0 },
    getFrameRate() { return FPS },
    getCurrentFrame() { return Math.floor((videoRef.current?.currentTime ?? 0) * FPS) },
    getTotalFrames() { return Math.floor((videoRef.current?.duration ?? 0) * FPS) },
    getVolume() { return videoRef.current?.volume ?? 1 },
    setVolume(v) {
      const el = videoRef.current
      if (!el) return
      el.volume = Math.max(0, Math.min(1, v))
      if (el.volume > 0 && el.muted) { el.muted = false; notifyMuted(false) }
    },
    toggleMute() {
      const el = videoRef.current
      if (!el) return
      el.muted = !el.muted
      notifyMuted(el.muted)
    },
    isMuted() { return !!videoRef.current?.muted },
  }))

  useEffect(() => {
    const handler = (e: KeyboardEvent) => {
      if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement) return
      switch (e.key) {
        case ' ': e.preventDefault(); togglePlay(); break
        case 'ArrowLeft': e.preventDefault(); stepFrames(e.ctrlKey ? -150 : -1); break
        case 'ArrowRight': e.preventDefault(); stepFrames(e.ctrlKey ? 150 : 1); break
        case 'm': case 'M': {
          const v = videoRef.current
          if (v) { v.muted = !v.muted; notifyMuted(v.muted) }
          break
        }
      }
    }
    window.addEventListener('keydown', handler)
    return () => window.removeEventListener('keydown', handler)
  }, [togglePlay, stepFrames])

  return (
    <div className="flex flex-col flex-1 min-h-0 bg-surface-0">
      {error && (
        <div className="bg-surface-1 border-b border-border-hair text-accent-red text-xs px-3 py-1">{error}</div>
      )}
      <div className="relative flex-1 min-h-0 flex items-center justify-center">
        <video
          ref={videoRef}
          src={videoUrl}
          muted={muted}
          controls={false}
          playsInline
          className="max-w-full max-h-full object-contain"
          onTimeUpdate={e => onTimeUpdate((e.target as HTMLVideoElement).currentTime)}
          onPlay={() => onPlayingChange?.(true)}
          onPause={() => onPlayingChange?.(false)}
          onDurationChange={e => {
            const d = (e.target as HTMLVideoElement).duration
            if (Number.isFinite(d)) onDurationChange?.(d)
          }}
          onLoadedMetadata={() => setError(null)}
          onError={() => setError(`Failed to load video (${media.name})`)}
        />
        {children && <div className="absolute inset-0">{children}</div>}
      </div>
    </div>
  )
})

export default VideoPlayer
