import { useEffect, useRef, useState, useCallback } from 'react'
import type { Landmark2D, Landmark3D } from '../gesture/types'
import styles from './RecordingPlayer.module.css'

interface RecordedFrame {
  t: number
  landmarks: Landmark2D[]
  worldLandmarks: Landmark3D[]
}

// MediaPipe indices to keep in simplified recordings
// 0=nose, 11-16=arms, 23-28=legs
export const KEEP_INDICES = [0, 11, 12, 13, 14, 15, 16, 23, 24, 25, 26, 27, 28]

// Landmark order in simplified recordings:
// 0=nose, 1=lShoulder, 2=rShoulder, 3=lElbow, 4=rElbow,
// 5=lWrist, 6=rWrist, 7=lHip, 8=rHip, 9=lKnee, 10=rKnee, 11=lAnkle, 12=rAnkle
const HEAD_IDX = 0
const L_SHOULDER = 1
const R_SHOULDER = 2

const STICK_CONNECTIONS: [number, number][] = [
  [1, 2],             // shoulders
  [1, 3], [3, 5],     // left arm
  [2, 4], [4, 6],     // right arm
  [1, 7], [2, 8],     // torso sides
  [7, 8],             // hips
  [7, 9], [9, 11],    // left leg
  [8, 10], [10, 12],  // right leg
]

const JOINT_INDICES = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]

interface RecordingPlayerProps {
  frames: RecordedFrame[]
  width?: number
  height?: number
  autoPlay?: boolean
  loop?: boolean
  showControls?: boolean
}

export function RecordingPlayer({ frames, width = 640, height = 480, autoPlay = false, loop = false, showControls = true }: RecordingPlayerProps) {
  const canvasRef = useRef<HTMLCanvasElement>(null)
  const [playing, setPlaying] = useState(autoPlay)
  const [frameIndex, setFrameIndex] = useState(0)
  const playingRef = useRef(false)
  const frameIndexRef = useRef(0)
  const startTimeRef = useRef(0)

  const totalDuration = frames.length > 0 ? frames[frames.length - 1].t : 0

  const drawFrame = useCallback((frame: RecordedFrame) => {
    const canvas = canvasRef.current
    if (!canvas) return
    const ctx = canvas.getContext('2d')!
    ctx.clearRect(0, 0, width, height)

    // Background
    ctx.fillStyle = '#111'
    ctx.fillRect(0, 0, width, height)

    const lm = frame.landmarks
    if (!lm.length) return

    // Head circle (use nose landmark 0, size from shoulder width)
    const nose = lm[HEAD_IDX]
    const lShoulder = lm[L_SHOULDER]
    const rShoulder = lm[R_SHOULDER]
    if (nose && lShoulder && rShoulder &&
        (nose.visibility ?? 1) >= 0.3 &&
        (lShoulder.visibility ?? 1) >= 0.3 &&
        (rShoulder.visibility ?? 1) >= 0.3) {
      const shoulderDx = (rShoulder.x - lShoulder.x) * width
      const shoulderDy = (rShoulder.y - lShoulder.y) * height
      const shoulderDist = Math.sqrt(shoulderDx * shoulderDx + shoulderDy * shoulderDy)
      const headRadius = shoulderDist * 0.45
      ctx.beginPath()
      ctx.arc(nose.x * width, nose.y * height, headRadius, 0, Math.PI * 2)
      ctx.strokeStyle = '#d946ef'
      ctx.lineWidth = 2
      ctx.stroke()
    }

    // Draw connections
    ctx.strokeStyle = '#d946ef'
    ctx.lineWidth = 2
    for (const [a, b] of STICK_CONNECTIONS) {
      if (a >= lm.length || b >= lm.length) continue
      const la = lm[a], lb = lm[b]
      if ((la.visibility ?? 1) < 0.3 || (lb.visibility ?? 1) < 0.3) continue
      ctx.beginPath()
      ctx.moveTo(la.x * width, la.y * height)
      ctx.lineTo(lb.x * width, lb.y * height)
      ctx.stroke()
    }

    // Draw joints
    for (const i of JOINT_INDICES) {
      if (i >= lm.length) continue
      const l = lm[i]
      if ((l.visibility ?? 1) < 0.3) continue
      ctx.beginPath()
      ctx.arc(l.x * width, l.y * height, 4, 0, Math.PI * 2)
      ctx.fillStyle = '#00ffcc'
      ctx.fill()
    }
  }, [width, height])

  // Draw current frame when index changes
  useEffect(() => {
    if (frames.length === 0) return
    drawFrame(frames[frameIndex])
  }, [frameIndex, frames, drawFrame])

  // Playback loop
  useEffect(() => {
    if (!playing || frames.length === 0) return
    playingRef.current = true
    frameIndexRef.current = frameIndex

    startTimeRef.current = performance.now()
    const startT = frames[frameIndexRef.current].t

    let rafId: number
    function tick() {
      if (!playingRef.current) return
      const elapsed = performance.now() - startTimeRef.current
      const targetT = startT + elapsed

      // Advance frame index
      let idx = frameIndexRef.current
      while (idx < frames.length - 1 && frames[idx + 1].t <= targetT) {
        idx++
      }
      frameIndexRef.current = idx
      setFrameIndex(idx)

      if (idx >= frames.length - 1) {
        if (loop) {
          frameIndexRef.current = 0
          setFrameIndex(0)
          startTimeRef.current = performance.now()
          rafId = requestAnimationFrame(tick)
          return
        }
        setPlaying(false)
        return
      }
      rafId = requestAnimationFrame(tick)
    }

    rafId = requestAnimationFrame(tick)
    return () => {
      playingRef.current = false
      cancelAnimationFrame(rafId)
    }
  }, [playing, frames]) // eslint-disable-line react-hooks/exhaustive-deps

  function togglePlay() {
    if (playing) {
      setPlaying(false)
    } else {
      if (frameIndex >= frames.length - 1) setFrameIndex(0)
      setPlaying(true)
    }
  }

  function handleScrub(e: React.ChangeEvent<HTMLInputElement>) {
    const idx = Number(e.target.value)
    setFrameIndex(idx)
    setPlaying(false)
  }

  if (frames.length === 0) {
    return <div className={styles.empty}>No frames loaded</div>
  }

  return (
    <div className={styles.player}>
      <canvas
        ref={canvasRef}
        width={width}
        height={height}
        className={styles.canvas}
      />
      {showControls && (
        <div className={styles.controls}>
          <button className={styles.playBtn} onClick={togglePlay}>
            {playing ? 'Pause' : 'Play'}
          </button>
          <input
            type="range"
            className={styles.scrubber}
            min={0}
            max={frames.length - 1}
            value={frameIndex}
            onChange={handleScrub}
          />
          <span className={styles.time}>
            {(frames[frameIndex].t / 1000).toFixed(1)}s / {(totalDuration / 1000).toFixed(1)}s
          </span>
          <span className={styles.frameInfo}>
            {frameIndex + 1}/{frames.length}
          </span>
        </div>
      )}
    </div>
  )
}
