import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useNavigate } from 'react-router-dom'
import { playSound, SFX_BUTTON_DEFAULT, SFX_BUTTON_START } from '../lib/sounds'
import { useTopHighscore } from '../features/leaderboard/useTopHighscore'
import { MAIN_MENU_ENTRIES, type MainMenuId } from '../features/home-main-menu/main-menu-config'
import styles from './HomePage.module.css'

type JointKey =
  | 'nose'
  | 'lSh'
  | 'rSh'
  | 'lEl'
  | 'rEl'
  | 'lWr'
  | 'rWr'
  | 'lHp'
  | 'rHp'
  | 'lKn'
  | 'rKn'
  | 'lAn'
  | 'rAn'

type JointPoint = [number, number]
type Skeleton = Record<JointKey, JointPoint>

type MenuMeta = {
  label: string
  subLabel: string
  badge: string
  badgeClass: string
}

type PoseResults = {
  poseLandmarks?: Array<{ x: number; y: number }>
}

const BASE_SKELETON: Skeleton = {
  nose: [0.5, 0.16],
  lSh: [0.42, 0.26],
  rSh: [0.58, 0.26],
  lEl: [0.36, 0.36],
  rEl: [0.64, 0.36],
  lWr: [0.33, 0.5],
  rWr: [0.67, 0.5],
  lHp: [0.45, 0.48],
  rHp: [0.55, 0.48],
  lKn: [0.45, 0.66],
  rKn: [0.56, 0.66],
  lAn: [0.44, 0.86],
  rAn: [0.57, 0.86],
}

const MENU_META: Record<MainMenuId, MenuMeta> = {
  start: { label: 'START', subLabel: 'Begin Session', badge: '▶', badgeClass: styles.badgeStart },
  leaderboards: {
    label: 'LEADERBOARD',
    subLabel: 'Top Movers',
    badge: '★',
    badgeClass: styles.badgeLeaderboard,
  },
  credits: { label: 'CREDITS', subLabel: 'Team + Tech', badge: '◇', badgeClass: styles.badgeCredits },
}

function cloneSkeleton(input: Skeleton): Skeleton {
  return {
    nose: [...input.nose],
    lSh: [...input.lSh],
    rSh: [...input.rSh],
    lEl: [...input.lEl],
    rEl: [...input.rEl],
    lWr: [...input.lWr],
    rWr: [...input.rWr],
    lHp: [...input.lHp],
    rHp: [...input.rHp],
    lKn: [...input.lKn],
    rKn: [...input.rKn],
    lAn: [...input.lAn],
    rAn: [...input.rAn],
  }
}

/** Constrain point B so it's exactly `len` distance from point A */
function constrainBone(a: JointPoint, b: JointPoint, len: number): JointPoint {
  const dx = b[0] - a[0]
  const dy = b[1] - a[1]
  const dist = Math.sqrt(dx * dx + dy * dy)
  if (dist < 0.001) return [a[0] + len, a[1]]
  const scale = len / dist
  return [a[0] + dx * scale, a[1] + dy * scale]
}

// Precompute bone lengths from base skeleton
const ARM_UPPER = Math.sqrt(
  (BASE_SKELETON.lSh[0] - BASE_SKELETON.lEl[0]) ** 2 +
  (BASE_SKELETON.lSh[1] - BASE_SKELETON.lEl[1]) ** 2
)
const ARM_LOWER = Math.sqrt(
  (BASE_SKELETON.lEl[0] - BASE_SKELETON.lWr[0]) ** 2 +
  (BASE_SKELETON.lEl[1] - BASE_SKELETON.lWr[1]) ** 2
)
const LEG_UPPER = Math.sqrt(
  (BASE_SKELETON.lHp[0] - BASE_SKELETON.lKn[0]) ** 2 +
  (BASE_SKELETON.lHp[1] - BASE_SKELETON.lKn[1]) ** 2
)
const LEG_LOWER = Math.sqrt(
  (BASE_SKELETON.lKn[0] - BASE_SKELETON.lAn[0]) ** 2 +
  (BASE_SKELETON.lKn[1] - BASE_SKELETON.lAn[1]) ** 2
)

function animateIdleSkeleton(target: Skeleton, t: number) {
  // Energetic dance: big body bounce, pumping arms, stepping legs, head bob
  const beat = t * 2.2 // faster tempo
  const bounce = Math.abs(Math.sin(beat)) * 0.04
  const sway = Math.sin(beat * 0.5) * 0.04
  const headTilt = Math.sin(beat * 0.5) * 0.02

  // Body bounces up and sways
  target.nose = [BASE_SKELETON.nose[0] + sway + headTilt, BASE_SKELETON.nose[1] - bounce * 1.2]
  target.lSh = [BASE_SKELETON.lSh[0] + sway, BASE_SKELETON.lSh[1] - bounce]
  target.rSh = [BASE_SKELETON.rSh[0] + sway, BASE_SKELETON.rSh[1] - bounce]
  target.lHp = [BASE_SKELETON.lHp[0] + sway * 0.7, BASE_SKELETON.lHp[1] - bounce * 0.5]
  target.rHp = [BASE_SKELETON.rHp[0] + sway * 0.7, BASE_SKELETON.rHp[1] - bounce * 0.5]

  // Arms sway side to side, elbows stay low
  const lSwing = Math.sin(beat * 0.5) * 0.1
  const rSwing = Math.sin(beat * 0.5 + Math.PI) * 0.1
  const lBob = Math.sin(beat) * 0.03
  const rBob = Math.sin(beat + Math.PI) * 0.03

  target.lEl = [
    BASE_SKELETON.lEl[0] + lSwing + sway,
    BASE_SKELETON.lEl[1] + lBob - bounce,
  ]
  target.rEl = [
    BASE_SKELETON.rEl[0] + rSwing + sway,
    BASE_SKELETON.rEl[1] + rBob - bounce,
  ]
  target.lWr = [
    BASE_SKELETON.lWr[0] + lSwing * 1.6 + sway,
    BASE_SKELETON.lWr[1] + lBob * 1.3 - bounce,
  ]
  target.rWr = [
    BASE_SKELETON.rWr[0] + rSwing * 1.6 + sway,
    BASE_SKELETON.rWr[1] + rBob * 1.3 - bounce,
  ]

  // Legs step side to side with knee bend
  const legPhase = Math.sin(beat * 0.5)
  const kneeBend = Math.abs(Math.sin(beat)) * 0.03

  target.lKn = [
    BASE_SKELETON.lKn[0] + legPhase * 0.03 + sway * 0.5,
    BASE_SKELETON.lKn[1] - kneeBend - bounce * 0.3,
  ]
  target.rKn = [
    BASE_SKELETON.rKn[0] - legPhase * 0.03 + sway * 0.5,
    BASE_SKELETON.rKn[1] - kneeBend * 0.5 - bounce * 0.3,
  ]
  target.lAn = [
    BASE_SKELETON.lAn[0] + legPhase * 0.04 + sway * 0.3,
    BASE_SKELETON.lAn[1] + (legPhase > 0 ? -0.02 : 0),
  ]
  target.rAn = [
    BASE_SKELETON.rAn[0] - legPhase * 0.04 + sway * 0.3,
    BASE_SKELETON.rAn[1] + (legPhase < 0 ? -0.02 : 0),
  ]

  // Enforce bone lengths so limbs don't stretch
  target.lEl = constrainBone(target.lSh, target.lEl, ARM_UPPER)
  target.lWr = constrainBone(target.lEl, target.lWr, ARM_LOWER)
  target.rEl = constrainBone(target.rSh, target.rEl, ARM_UPPER)
  target.rWr = constrainBone(target.rEl, target.rWr, ARM_LOWER)
  target.lKn = constrainBone(target.lHp, target.lKn, LEG_UPPER)
  target.lAn = constrainBone(target.lKn, target.lAn, LEG_LOWER)
  target.rKn = constrainBone(target.rHp, target.rKn, LEG_UPPER)
  target.rAn = constrainBone(target.rKn, target.rAn, LEG_LOWER)
}

function drawSkeleton(ctx: CanvasRenderingContext2D, skeleton: Skeleton, width: number, height: number) {
  const p = (key: JointKey): JointPoint => [skeleton[key][0] * width, skeleton[key][1] * height]
  const [lShX, lShY] = p('lSh')
  const [rShX, rShY] = p('rSh')
  const [lHpX, lHpY] = p('lHp')
  const [rHpX, rHpY] = p('rHp')
  const midSh: JointPoint = [(lShX + rShX) / 2, (lShY + rShY) / 2]
  const midHp: JointPoint = [(lHpX + rHpX) / 2, (lHpY + rHpY) / 2]
  const [noseX, noseY] = p('nose')

  const line = (a: JointPoint, b: JointPoint, color: string, widthPx: number, alpha = 1) => {
    ctx.save()
    ctx.strokeStyle = color
    ctx.globalAlpha = alpha
    ctx.lineWidth = widthPx
    ctx.lineCap = 'round'
    ctx.beginPath()
    ctx.moveTo(a[0], a[1])
    ctx.lineTo(b[0], b[1])
    ctx.stroke()
    ctx.restore()
  }

  const dot = (point: JointPoint, radius: number, fill: string, alpha = 1) => {
    ctx.save()
    ctx.globalAlpha = alpha
    ctx.fillStyle = fill
    ctx.beginPath()
    ctx.arc(point[0], point[1], radius, 0, Math.PI * 2)
    ctx.fill()
    ctx.restore()
  }

  ctx.clearRect(0, 0, width, height)

  line(p('lSh'), p('lEl'), 'rgba(255,78,203,0.12)', 24)
  line(p('lEl'), p('lWr'), 'rgba(255,78,203,0.12)', 24)
  line(p('rSh'), p('rEl'), 'rgba(0,200,255,0.12)', 24)
  line(p('rEl'), p('rWr'), 'rgba(0,200,255,0.12)', 24)
  line(p('lSh'), p('lEl'), 'rgba(255,78,203,0.08)', 36)
  line(p('lEl'), p('lWr'), 'rgba(255,78,203,0.08)', 36)
  line(p('rSh'), p('rEl'), 'rgba(0,200,255,0.08)', 36)
  line(p('rEl'), p('rWr'), 'rgba(0,200,255,0.08)', 36)

  line(p('lSh'), p('lEl'), '#ff4ecb', 4.5)
  line(p('lEl'), p('lWr'), '#ff4ecb', 3.6)
  line(p('rSh'), p('rEl'), '#00c8ff', 4.5)
  line(p('rEl'), p('rWr'), '#00c8ff', 3.6)

  line(midSh, midHp, '#ffffff', 3.6, 0.65)
  line(p('lSh'), p('rSh'), '#ffffff', 4.5, 0.88)
  line(p('lHp'), p('rHp'), '#ffffff', 3.6, 0.74)
  line(p('lSh'), p('lHp'), '#ffffff', 2.7, 0.18)
  line(p('rSh'), p('rHp'), '#ffffff', 2.7, 0.18)

  line(p('lHp'), p('lKn'), 'rgba(255,255,255,0.22)', 4)
  line(p('lKn'), p('lAn'), 'rgba(255,255,255,0.22)', 3.6)
  line(p('rHp'), p('rKn'), 'rgba(255,255,255,0.22)', 4)
  line(p('rKn'), p('rAn'), 'rgba(255,255,255,0.22)', 3.6)

  ctx.save()
  ctx.strokeStyle = '#ffffff'
  ctx.lineWidth = 3.6
  ctx.fillStyle = 'rgba(255,255,255,0.07)'
  ctx.beginPath()
  ctx.arc(noseX, noseY + 18, 23, 0, Math.PI * 2)
  ctx.fill()
  ctx.stroke()
  ctx.beginPath()
  ctx.arc(noseX, noseY + 18, 20, 0, Math.PI * 2)
  ctx.stroke()
  ctx.restore()

  dot(p('lSh'), 7.2, '#ffffff')
  dot(p('rSh'), 7.2, '#ffffff')
  dot(p('lHp'), 7.2, '#ffffff')
  dot(p('rHp'), 7.2, '#ffffff')
  dot(p('lKn'), 5.4, '#ffffff', 0.3)
  dot(p('rKn'), 5.4, '#ffffff', 0.3)
  dot(p('lAn'), 5.4, '#ffffff', 0.3)
  dot(p('rAn'), 5.4, '#ffffff', 0.3)
  dot(p('lEl'), 9, '#ff4ecb')
  dot(p('rEl'), 9, '#00c8ff')
  dot(p('lWr'), 10.8, '#ff4ecb')
  dot(p('rWr'), 10.8, '#00c8ff')
  dot(p('lWr'), 18, 'rgba(255,78,203,0.2)')
  dot(p('rWr'), 18, 'rgba(0,200,255,0.2)')
}

function useDanceFloorGrid() {
  return useMemo(() => {
    if (typeof window === 'undefined') return { cols: 18, rows: 8 }
    if (window.matchMedia('(max-width: 600px)').matches) return { cols: 12, rows: 6 }
    return { cols: 20, rows: 8 }
  }, [])
}

/**
 * Main hub: arcade layout (title, menu, hi-score, avatar slot, dance floor).
 * Song-select screen palette (purple/black, magenta accent, grid + scanlines).
 * MediaPipe avatar + D-pad feedback can replace placeholders later.
 */
export function HomePage() {
  const navigate = useNavigate()
  const [selectedIndex, setSelectedIndex] = useState(0)
  const [flashIndex, setFlashIndex] = useState<number | null>(null)
  const [isCoarsePointer, setIsCoarsePointer] = useState(false)
  const [indicatorTop, setIndicatorTop] = useState(0)
  const menuRef = useRef<HTMLDivElement | null>(null)
  const menuItemRefs = useRef<Array<HTMLButtonElement | null>>([])
  const canvasRef = useRef<HTMLCanvasElement | null>(null)
  const skeletonRef = useRef<Skeleton>(cloneSkeleton(BASE_SKELETON))
  const hasLivePoseRef = useRef(false)
  const rafRef = useRef<number | null>(null)
  const timeRef = useRef(0)
  const { topScore, topName: topNameFromDB, loading: hiScoreLoading } = useTopHighscore()
  const { cols: floorCols, rows: floorRows } = useDanceFloorGrid()
  const floorTileCount = floorCols * floorRows

  // Rainbow EQ bars removed

  const updateIndicator = useCallback(() => {
    const wrapper = menuRef.current
    const selected = menuItemRefs.current[selectedIndex]
    if (!wrapper || !selected) return
    const wrapperRect = wrapper.getBoundingClientRect()
    const itemRect = selected.getBoundingClientRect()
    setIndicatorTop(itemRect.top - wrapperRect.top + itemRect.height / 2)
  }, [selectedIndex])

  const activateSelection = useCallback(
    (index: number) => {
      const item = MAIN_MENU_ENTRIES[index]
      if (!item) return
      playSound(index === 0 ? SFX_BUTTON_START : SFX_BUTTON_DEFAULT)
      setFlashIndex(index)
      window.setTimeout(() => {
        setFlashIndex(null)
        navigate(item.to)
      }, 110)
    },
    [navigate],
  )

  useEffect(() => {
    updateIndicator()
  }, [updateIndicator])

  useEffect(() => {
    const onResize = () => updateIndicator()
    window.addEventListener('resize', onResize)
    return () => window.removeEventListener('resize', onResize)
  }, [updateIndicator])

  useEffect(() => {
    const mq = window.matchMedia('(hover: none), (pointer: coarse)')
    const syncPointerMode = () => setIsCoarsePointer(mq.matches)
    syncPointerMode()

    if (typeof mq.addEventListener === 'function') {
      mq.addEventListener('change', syncPointerMode)
      return () => mq.removeEventListener('change', syncPointerMode)
    }

    mq.addListener(syncPointerMode)
    return () => mq.removeListener(syncPointerMode)
  }, [])

  useEffect(() => {
    const onKeyDown = (event: KeyboardEvent) => {
      if (event.key === 'ArrowDown') {
        event.preventDefault()
        setSelectedIndex((prev) => (prev + 1) % MAIN_MENU_ENTRIES.length)
        return
      }
      if (event.key === 'ArrowUp') {
        event.preventDefault()
        setSelectedIndex((prev) => (prev - 1 + MAIN_MENU_ENTRIES.length) % MAIN_MENU_ENTRIES.length)
        return
      }
      if (event.key === 'Enter') {
        event.preventDefault()
        activateSelection(selectedIndex)
      }
    }

    document.addEventListener('keydown', onKeyDown)
    return () => document.removeEventListener('keydown', onKeyDown)
  }, [activateSelection, selectedIndex])

  useEffect(() => {
    const onPoseResults = (results: PoseResults) => {
      const lm = results.poseLandmarks
      if (!lm || lm.length < 29) return
      hasLivePoseRef.current = true
      skeletonRef.current.nose = [lm[0].x, lm[0].y]
      skeletonRef.current.lSh = [lm[11].x, lm[11].y]
      skeletonRef.current.rSh = [lm[12].x, lm[12].y]
      skeletonRef.current.lEl = [lm[13].x, lm[13].y]
      skeletonRef.current.rEl = [lm[14].x, lm[14].y]
      skeletonRef.current.lWr = [lm[15].x, lm[15].y]
      skeletonRef.current.rWr = [lm[16].x, lm[16].y]
      skeletonRef.current.lHp = [lm[23].x, lm[23].y]
      skeletonRef.current.rHp = [lm[24].x, lm[24].y]
      skeletonRef.current.lKn = [lm[25].x, lm[25].y]
      skeletonRef.current.rKn = [lm[26].x, lm[26].y]
      skeletonRef.current.lAn = [lm[27].x, lm[27].y]
      skeletonRef.current.rAn = [lm[28].x, lm[28].y]
    }

    const host = window as Window & { onGrooveQuestPoseResults?: (results: PoseResults) => void }
    host.onGrooveQuestPoseResults = onPoseResults

    return () => {
      if (host.onGrooveQuestPoseResults === onPoseResults) {
        delete host.onGrooveQuestPoseResults
      }
    }
  }, [])

  useEffect(() => {
    const canvas = canvasRef.current
    if (!canvas) return
    const ctx = canvas.getContext('2d')
    if (!ctx) return

    const tick = () => {
      if (!hasLivePoseRef.current) {
        timeRef.current += 0.028
        animateIdleSkeleton(skeletonRef.current, timeRef.current)
      }
      drawSkeleton(ctx, skeletonRef.current, canvas.width, canvas.height)
      rafRef.current = window.requestAnimationFrame(tick)
    }

    rafRef.current = window.requestAnimationFrame(tick)
    return () => {
      if (rafRef.current != null) {
        window.cancelAnimationFrame(rafRef.current)
      }
    }
  }, [])

  return (
    <div className={styles.page}>
      <div className={styles.backdrop} aria-hidden />
      <div className={styles.floor} aria-hidden>
        <div className={styles.floorInner}>
          <div className={styles.floorGlow} />
          <div
            className={styles.floorTiles}
            style={{
              gridTemplateColumns: `repeat(${floorCols}, 1fr)`,
              gridTemplateRows: `repeat(${floorRows}, 1fr)`,
            }}
          >
            {Array.from({ length: floorTileCount }, (_, i) => (
              <div
                key={i}
                className={styles.floorTile}
                style={{ animationDelay: `${(i % 7) * 0.12}s` }}
              />
            ))}
          </div>
        </div>
      </div>
      <div className={styles.scanlines} aria-hidden />
      <div className={`${styles.corner} ${styles.cornerTopLeft}`} aria-hidden />
      <div className={`${styles.corner} ${styles.cornerTopRight}`} aria-hidden />
      <div className={`${styles.corner} ${styles.cornerBottomLeft}`} aria-hidden />
      <div className={`${styles.corner} ${styles.cornerBottomRight}`} aria-hidden />

      <div className={styles.layout}>
        <main className={styles.mainRow}>
          <aside className={styles.leftPanel}>
            <header className={styles.titleBlock}>
              <h1 className={styles.gameTitle}>HACK HACK REVOLUTION</h1>
            </header>

            <div className={styles.menuWrap} ref={menuRef}>
              <span className={styles.menuTriangle} style={{ top: `${indicatorTop}px` }} aria-hidden />
              <nav className={styles.menu} aria-label="Main menu">
                {MAIN_MENU_ENTRIES.map((entry, index) => {
                  const meta = MENU_META[entry.id]
                  const selected = selectedIndex === index
                  const flashed = flashIndex === index
                  return (
                    <button
                      key={entry.id}
                      type="button"
                      ref={(node) => {
                        menuItemRefs.current[index] = node
                      }}
                      className={`${styles.menuItem}${selected ? ` ${styles.menuItemSelected}` : ''}${flashed ? ` ${styles.menuItemFlash}` : ''}`}
                      onClick={() => {
                        activateSelection(index)
                      }}
                      onMouseEnter={() => {
                        if (!isCoarsePointer) {
                          setSelectedIndex(index)
                        }
                      }}
                    >
                      <span className={`${styles.menuBadge} ${meta.badgeClass}`} aria-hidden>
                        {meta.badge}
                      </span>
                      <span className={styles.menuText}>
                        <span className={styles.menuLabel}>{meta.label}</span>
                        <span className={styles.menuSubLabel}>{meta.subLabel}</span>
                      </span>
                    </button>
                  )
                })}
              </nav>
            </div>

            {/* Rainbow EQ bars removed */}
          </aside>

          <section className={styles.centerPanel} aria-label="Avatar preview">
            {/* Mediapipe ready badge removed */}
            <canvas ref={canvasRef} className={styles.avatarCanvas} width={360} height={612} />
          </section>

          <aside className={styles.rightPanel}>
            <article className={styles.scoreCard}>
              <p className={styles.scoreEyebrow}>🌍 Global</p>
              <p className={styles.scoreTitle}>HI SCORE</p>
              <p className={styles.scoreValue}>
                {hiScoreLoading ? '···' : topScore != null ? topScore.toLocaleString() : '—'}
              </p>
              <p className={styles.scorePlayer}>★ {topNameFromDB ?? 'ACE_DANCER'}</p>
            </article>
          </aside>
        </main>
      </div>
    </div>
  )
}
