import { useCallback, useEffect, useRef, useState } from 'react'
import { Navigate, useNavigate } from 'react-router-dom'
import { ROUTE_PATHS } from '../app/route-paths'
import { playSound, SFX_BUTTON_DEFAULT } from '../lib/sounds'
import { supportsFullscreen } from '../lib/platform'
import { useGameStatsStore } from '../stores/game-stats/store'
import { ArrowGame } from '../game/ArrowGame'
import type { ArrowGameHandle } from '../game/ArrowGame'
import { loadSSC, simplifyNotes } from '../game/sscParser'
import type { ChartNote } from '../game/sscParser'
import type { AudioPlayer } from '../game/audioPlayer'
import { createLocalAudioPlayer, createYouTubeAudioPlayer } from '../game/audioPlayer'
import { usePoseLoop } from '../pose/usePoseLoop'
import { GestureOverlay } from '../components/GestureOverlay'
import { Tutorial, useTutorialSeen } from '../components/Tutorial'
import type { StepDirection } from '../gesture/types'
import type { PoseSettings } from '../types'
import { loadSettings } from '../types'
import { gameLog, getGameLog, clearGameLog } from '../game/gameLog'
import { YouTubeAttribution } from '../components/YouTubeAttribution'
import styles from './GamePage.module.css'

const KEY_TO_STEP: Record<string, StepDirection> = {
  ArrowLeft: 'left',
  ArrowRight: 'right',
  ArrowUp: 'forward',
  ArrowDown: 'back',
}

export function GamePage() {
  const navigate = useNavigate()
  const song = useGameStatsStore((state) => state.currentSong)
  const addPerfectHit = useGameStatsStore((state) => state.addPerfectHit)
  const addGoodHit = useGameStatsStore((state) => state.addGoodHit)
  const addMiss = useGameStatsStore((state) => state.addMiss)
  const addScore = useGameStatsStore((state) => state.addScore)
  const currentScore = useGameStatsStore((state) => state.score)

  if (!song) return <Navigate to={ROUTE_PATHS.intro} replace />

  const resolvedSong = song
  const [paused, setPaused] = useState(false)
  const { seen: tutorialSeen, markSeen: markTutorialSeen } = useTutorialSeen()
  const [showTutorial, setShowTutorial] = useState(!tutorialSeen)

  // Fullscreen (for pause menu)
  const canFullscreen = supportsFullscreen()
  const [isFullscreen, setIsFullscreen] = useState(!!document.fullscreenElement)
  useEffect(() => {
    if (!canFullscreen) return
    const onChange = () => setIsFullscreen(!!document.fullscreenElement)
    document.addEventListener('fullscreenchange', onChange)
    return () => document.removeEventListener('fullscreenchange', onChange)
  }, [canFullscreen])

  // Clear log on mount
  useEffect(() => {
    clearGameLog()
    gameLog(`GamePage mount, song=${resolvedSong.id} chartUrl=${resolvedSong.chartUrl}`)
  }, [])
  const [songProgress, setSongProgress] = useState(0)

  const arrowGameRef = useRef<ArrowGameHandle>(null)
  const videoRef = useRef<HTMLVideoElement>(null)
  const canvasRef = useRef<HTMLCanvasElement>(null)
  const arCanvasRef = useRef<HTMLCanvasElement>(null)

  const [settings] = useState<PoseSettings>(loadSettings)
  const [chartNotes, setChartNotes] = useState<ChartNote[] | undefined>(undefined)
  const [autoPaused, setAutoPaused] = useState(false)
  const [audioPlayer, setAudioPlayer] = useState<AudioPlayer | null>(null)
  const ytHostRef = useRef<HTMLDivElement>(null)
  const [keyboardMode, setKeyboardMode] = useState(false)
  const [useYoutube] = useState(!!resolvedSong.youtubeVideoId && !resolvedSong.isCustom)

  // Load chart
  useEffect(() => {
    const shouldSimplify = resolvedSong.simplify !== false
    const preferredDiff = resolvedSong.selectedDifficulty

    loadSSC(resolvedSong.chartUrl).then((parsed) => {
      const diff =
        (preferredDiff && parsed.difficulties.find((d) => d.difficulty === preferredDiff)) ||
        parsed.difficulties.find((d) => d.difficulty === 'Beginner') ||
        (parsed.difficulties.length > 0
          ? parsed.difficulties.reduce((a, b) => (a.meter < b.meter ? a : b))
          : null)
      if (diff) {
        const notes = shouldSimplify ? simplifyNotes(diff.notes) : diff.notes
        gameLog(`chart loaded: ${notes.length} notes, difficulty=${diff.difficulty}, simplified=${shouldSimplify}`)
        setChartNotes(notes)
      } else {
        gameLog(`chart loaded: NO playable difficulty found (parsed=${parsed.difficulties.length})`)
      }
    }).catch((e) => gameLog(`chart load FAILED: ${e}`))
  }, [resolvedSong.chartUrl, resolvedSong.selectedDifficulty, resolvedSong.simplify])

  // Create audio player (YouTube or local)
  useEffect(() => {
    let destroyed = false
    let player: AudioPlayer | null = null

    if (useYoutube && resolvedSong.youtubeVideoId && ytHostRef.current) {
      gameLog(`creating YouTube audio player: ${resolvedSong.youtubeVideoId} offset=${resolvedSong.youtubeOffset}`)
      createYouTubeAudioPlayer({
        videoId: resolvedSong.youtubeVideoId,
        offset: resolvedSong.youtubeOffset ?? 0,
        host: ytHostRef.current,
      }).then((p) => {
        if (destroyed) { p.destroy(); return }
        player = p
        setAudioPlayer(p)
        gameLog('YouTube audio player ready')
      }).catch((e) => gameLog(`YouTube player FAILED: ${e}`))
    } else {
      gameLog(`creating local audio player: ${resolvedSong.audioUrl}`)
      player = createLocalAudioPlayer(resolvedSong.audioUrl)
      setAudioPlayer(player)
    }

    return () => {
      destroyed = true
      player?.destroy()
      setAudioPlayer(null)
    }
  }, [resolvedSong.id, useYoutube])

  // Pose detection (disabled in keyboard mode)
  const gameStartedRef = useRef(false)
  const { loading, error, calibrationStatus, framingHint, gesture } = usePoseLoop(
    videoRef,
    canvasRef,
    arCanvasRef,
    settings,
    null,
    { onStep: keyboardMode ? undefined : (direction) => arrowGameRef.current?.handleStep(direction) },
    paused || autoPaused ? 'full' : gameStartedRef.current ? 'toes-only' : 'nose',
  )

  const gameReady = calibrationStatus === 'ready' || keyboardMode
  const prevCalibRef = useRef(calibrationStatus)
  if (gameReady && !gameStartedRef.current) {
    gameStartedRef.current = true
    gameLog('gameStarted=true')
  }
  const gameStarted = gameStartedRef.current
  if (prevCalibRef.current !== calibrationStatus) {
    gameLog(`calibration: ${prevCalibRef.current} → ${calibrationStatus}, gameStarted=${gameStarted}`)
    prevCalibRef.current = calibrationStatus
  }

  // Auto-pause when user leaves frame; resume handled by crossed-arms release
  const autoPausedAtRef = useRef(0)
  useEffect(() => {
    if (!gameStarted || keyboardMode) return
    if (calibrationStatus === 'paused' || calibrationStatus === 'framing') {
      if (!autoPaused) {
        gameLog(`autoPause ON (calibration=${calibrationStatus})`)
        autoPausedAtRef.current = performance.now()
      }
      setAutoPaused(true)
    }
  }, [calibrationStatus, gameStarted, autoPaused])

  // Unpause when calibration returns to ready (crossed-arms released)
  useEffect(() => {
    if (!autoPaused || calibrationStatus !== 'ready') return
    if (performance.now() - autoPausedAtRef.current < 1000) return // ignore stale transition
    gameLog('autoPause OFF (resumed)')
    setAutoPaused(false)
  }, [autoPaused, calibrationStatus])

  // Score delta floater + combo
  const [scoreDelta, setScoreDelta] = useState<{ value: number; key: number; dx: number; dy: number } | null>(null)
  const [combo, setCombo] = useState(0)

  // Scoring: base points + combo bonus (capped at 4x)
  const handleJudgment = useCallback(
    (j: 'perfect' | 'good' | 'miss', combo: number) => {
      setCombo(combo)
      let delta = 0
      if (j === 'perfect') {
        addPerfectHit()
        const multiplier = 1 + Math.min(combo, 30) / 10
        delta = Math.round(1000 * multiplier)
      } else if (j === 'good') {
        addGoodHit()
        const multiplier = 1 + Math.min(combo, 30) / 10
        delta = Math.round(500 * multiplier)
      } else {
        addMiss()
      }
      if (delta > 0) {
        addScore(delta)
        setScoreDelta({
          value: delta,
          key: Date.now(),
          dx: 20 + Math.random() * 80,
          dy: -10 + Math.random() * 40,
        })
      }
    },
    [addPerfectHit, addGoodHit, addMiss, addScore],
  )

  const handleSongEnd = useCallback(() => {
    navigate(ROUTE_PATHS.endOfGame)
  }, [navigate])

  // Pause menu via Esc
  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if (e.key === 'Escape') setPaused((p) => !p)
    }
    window.addEventListener('keydown', onKey)
    return () => window.removeEventListener('keydown', onKey)
  }, [])

  // Keyboard mode: arrow keys feed steps into the game
  useEffect(() => {
    if (!keyboardMode) return
    const onKey = (e: KeyboardEvent) => {
      const step = KEY_TO_STEP[e.key]
      if (step) {
        e.preventDefault()
        arrowGameRef.current?.handleStep(step)
      }
    }
    window.addEventListener('keydown', onKey)
    return () => window.removeEventListener('keydown', onKey)
  }, [keyboardMode])

  // Glow effect
  const [glow, setGlow] = useState<{ color: string; key: number } | null>(null)
  const handleJudgmentWithGlow = useCallback(
    (j: 'perfect' | 'good' | 'miss', combo: number) => {
      const colors = { perfect: '#00ff6a', good: '#ffe600', miss: '#ff2222' }
      setGlow({ color: colors[j], key: Date.now() })
      handleJudgment(j, combo)
    },
    [handleJudgment],
  )

  return (
    <div className={styles.root}>
      {loading && (
        <div className={styles.loadingOverlay}>
          <div className={styles.spinner} />
          <p className={`${styles.loadingText} font-title`}>Loading pose detection...</p>
        </div>
      )}
      {error && <div className="status error">{error}</div>}

      <video ref={videoRef} className="webcam-video" playsInline muted />
      <canvas ref={canvasRef} className="pose-overlay" />
      <canvas ref={arCanvasRef} className="ar-overlay" />

      {/* YouTube video player (visible to comply with YT ToS) */}
      {!resolvedSong.isCustom && (
        <YouTubeAttribution>
          <div ref={ytHostRef} className={styles.ytPlayer} />
        </YouTubeAttribution>
      )}

      {(calibrationStatus === 'framing') && (
        <div className="framing-line" />
      )}

      {!gameStarted && (
        <GestureOverlay
          gesture={gesture}
          calibrationStatus={calibrationStatus}
          framingHint={framingHint}
        />
      )}
      {autoPaused && (
        <div className="debug-status">
          {calibrationStatus === 'ready'
            ? 'PAUSED — Place hand on opposite shoulder to continue!'
            : framingHint === 'head not visible'
            ? 'PAUSED — I can\'t see your head! Step back.'
            : framingHint === 'feet are outside the camera view'
            ? 'PAUSED — I can\'t see your feet! Step back.'
            : 'PAUSED — Step back into frame!'}
        </div>
      )}

      {glow && (
        <div
          key={glow.key}
          className="judgment-glow"
          style={{ '--glow-color': glow.color } as React.CSSProperties}
          onAnimationEnd={() => setGlow(null)}
        />
      )}

      <ArrowGame
        ref={arrowGameRef}
        active={gameStarted && !paused && !autoPaused && !showTutorial}
        chartNotes={chartNotes}
        audioPlayer={audioPlayer}
        onJudgment={handleJudgmentWithGlow}
        onSongEnd={handleSongEnd}
        onProgress={setSongProgress}
      />

      <div className={styles.bottomHud}>
        <div className={styles.scoreHud}>
          {combo > 1 && (
            <span className={`${styles.comboValue} font-title`}>{combo}x</span>
          )}
          <span className={`${styles.scoreValue} font-title`}>
            {currentScore.toLocaleString()}
          </span>
          {scoreDelta && (
            <span
              key={scoreDelta.key}
              className={styles.scoreDelta}
              onAnimationEnd={() => setScoreDelta(null)}
            >
              +{scoreDelta.value.toLocaleString()}
            </span>
          )}
        </div>
        <div className={styles.progressBar}>
          <div
            className={styles.progressFill}
            style={{ width: `${songProgress * 100}%` }}
          />
        </div>
      </div>

      <button
        className={styles.menuBtn}
        onClick={() => { playSound(SFX_BUTTON_DEFAULT); setPaused(true) }}
        aria-label="Menu"
      >
        &#x22EE;
      </button>

      {paused && (
        <div className={styles.overlay}>
          <button
            className={`${styles.cornerBtn} ${styles.cornerBtnLeft}`}
            onClick={() => {
              navigator.clipboard.writeText(getGameLog())
                .then(() => alert('Log copied!'))
            }}
          >
            LOG
          </button>
          <button
            className={`${styles.cornerBtn} ${styles.cornerBtnLeft2}`}
            onClick={() => {
              playSound(SFX_BUTTON_DEFAULT)
              setKeyboardMode((k) => !k)
              setAutoPaused(false)
            }}
          >
            {keyboardMode ? 'KB: ON' : 'KB: OFF'}
          </button>
          {canFullscreen && !isFullscreen && (
            <button
              className={`${styles.cornerBtn} ${styles.cornerBtnRight}`}
              onClick={() => document.documentElement.requestFullscreen().catch(() => {})}
            >
              &#x26F6;
            </button>
          )}
          <div className={styles.pauseMenu}>
            <h2 className={`${styles.pauseTitle} font-title`}>PAUSED</h2>
            <button
              className={`${styles.pauseBtn} font-title`}
              onClick={() => { playSound(SFX_BUTTON_DEFAULT); setPaused(false) }}
            >
              CONTINUE
            </button>
            <button
              className={`${styles.pauseBtn} ${styles.pauseBtnSecondary} font-title`}
              onClick={() => { playSound(SFX_BUTTON_DEFAULT); setShowTutorial(true) }}
            >
              TUTORIAL
            </button>
            <button
              className={`${styles.pauseBtn} ${styles.pauseBtnDanger} font-title`}
              onClick={() => { playSound(SFX_BUTTON_DEFAULT); handleSongEnd() }}
            >
              FINISH EARLY
            </button>
            <button
              className={`${styles.pauseBtn} ${styles.pauseBtnSecondary} font-title`}
              onClick={() => { playSound(SFX_BUTTON_DEFAULT); navigate(ROUTE_PATHS.home) }}
            >
              MAIN MENU
            </button>
          </div>
        </div>
      )}

      {showTutorial && (
        <Tutorial
          onDismiss={() => {
            markTutorialSeen()
            setShowTutorial(false)
          }}
        />
      )}
    </div>
  )
}
