import { useRef, useEffect, useCallback, useState } from 'react'
import './DinoGame.css'
import {
  CANVAS_W, CANVAS_H, GROUND_Y,
  GRAVITY, JUMP_VEL, INITIAL_SPEED, SPEED_GROWTH, MAX_SPEED,
  PLAYER_X, PLAYER_W, PLAYER_H,
  MIN_GAP, MAX_GAP,
  OBSTACLE_TEMPLATES,
} from './gameConstants.js'

// ─── Helpers ──────────────────────────────────────────────────────────────────

function randObstacle() {
  const t = OBSTACLE_TEMPLATES[Math.floor(Math.random() * OBSTACLE_TEMPLATES.length)]
  return { ...t, x: CANVAS_W + 60, y: GROUND_Y - t.h }
}

/** AABB collision with per-axis inward padding for forgiveness */
function rectsOverlap(ax, ay, aw, ah, bx, by, bw, bh, pad = 7) {
  return (
    ax + pad < bx + bw - pad &&
    ax + aw - pad > bx + pad &&
    ay + pad < by + bh - pad &&
    ay + ah - pad > by + pad
  )
}

// ─── Canvas drawing ───────────────────────────────────────────────────────────

function roundRect(ctx, x, y, w, h, r = 5) {
  ctx.beginPath()
  ctx.moveTo(x + r, y)
  ctx.lineTo(x + w - r, y)
  ctx.quadraticCurveTo(x + w, y, x + w, y + r)
  ctx.lineTo(x + w, y + h - r)
  ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h)
  ctx.lineTo(x + r, y + h)
  ctx.quadraticCurveTo(x, y + h, x, y + h - r)
  ctx.lineTo(x, y + r)
  ctx.quadraticCurveTo(x, y, x + r, y)
  ctx.closePath()
}

function drawBackground(ctx, bgOffset) {
  // Fill
  ctx.fillStyle = '#080c14'
  ctx.fillRect(0, 0, CANVAS_W, CANVAS_H)

  // Scrolling dot grid
  ctx.fillStyle = 'rgba(0, 180, 255, 0.055)'
  const spacing = 28
  const startX  = (-bgOffset % spacing) - spacing
  for (let x = startX; x < CANVAS_W + spacing; x += spacing) {
    for (let y = 10; y < GROUND_Y - 4; y += spacing) {
      ctx.fillRect(x, y, 1.5, 1.5)
    }
  }
}

function drawGround(ctx, bgOffset) {
  // Glowing ground line
  ctx.save()
  ctx.shadowBlur  = 8
  ctx.shadowColor = '#00ccff'
  ctx.strokeStyle = '#00ccff'
  ctx.lineWidth   = 1.5
  ctx.beginPath()
  ctx.moveTo(0, GROUND_Y)
  ctx.lineTo(CANVAS_W, GROUND_Y)
  ctx.stroke()
  ctx.restore()

  // Scrolling dashes below ground line
  ctx.save()
  ctx.strokeStyle   = 'rgba(0, 140, 200, 0.35)'
  ctx.lineWidth     = 1
  ctx.setLineDash([18, 14])
  ctx.lineDashOffset = -bgOffset
  ctx.beginPath()
  ctx.moveTo(0, GROUND_Y + 7)
  ctx.lineTo(CANVAS_W, GROUND_Y + 7)
  ctx.stroke()
  ctx.setLineDash([])
  ctx.restore()
}

/** Pod player — kubernetes steering-wheel symbol inside a neon green box */
function drawPlayer(ctx, playerY, frameCount, isJumping) {
  const x  = PLAYER_X
  const y  = playerY
  const w  = PLAYER_W
  const h  = PLAYER_H
  const cx = x + w / 2
  const cy = y + h / 2 - 1

  ctx.save()

  // Outer glow
  ctx.shadowBlur  = 14
  ctx.shadowColor = '#00ff88'

  // Body fill
  roundRect(ctx, x, y, w, h, 5)
  ctx.fillStyle = '#081410'
  ctx.fill()

  // Body border
  ctx.strokeStyle = '#00ff88'
  ctx.lineWidth   = 2
  ctx.stroke()

  ctx.shadowBlur = 0

  // Kubernetes helm wheel (6 spokes + outer ring)
  ctx.strokeStyle = '#00ff88'
  ctx.lineWidth   = 1
  for (let i = 0; i < 6; i++) {
    const a = (Math.PI / 3) * i - Math.PI / 6
    ctx.beginPath()
    ctx.moveTo(cx + Math.cos(a) * 3, cy + Math.sin(a) * 3)
    ctx.lineTo(cx + Math.cos(a) * 9, cy + Math.sin(a) * 9)
    ctx.stroke()
  }
  ctx.beginPath()
  ctx.arc(cx, cy, 10, 0, Math.PI * 2)
  ctx.stroke()

  // Centre dot
  ctx.beginPath()
  ctx.arc(cx, cy, 2.5, 0, Math.PI * 2)
  ctx.fillStyle = '#00ff88'
  ctx.fill()

  // Running legs (2-frame animation, hidden while airborne)
  if (!isJumping) {
    const phase = Math.floor(frameCount / 6) % 2
    ctx.fillStyle = '#00ff88'
    // Left leg
    const lh = phase === 0 ? 8 : 4
    ctx.fillRect(x + 7,       y + h, 6, lh)
    ctx.fillRect(x + 3,       y + h + lh, 10, 3)
    // Right leg (opposite phase)
    const rh = phase === 0 ? 4 : 8
    ctx.fillRect(x + w - 13,  y + h, 6, rh)
    ctx.fillRect(x + w - 17,  y + h + rh, 10, 3)
  }

  ctx.restore()
}

function drawObstacle(ctx, obs) {
  const { x, y, w, h, lines, color } = obs
  const cx = x + w / 2
  const cy = y + h / 2

  ctx.save()

  // Diagonal warning stripes clipped to obstacle rect
  roundRect(ctx, x, y, w, h, 4)
  ctx.fillStyle = 'rgba(0,0,0,0.82)'
  ctx.fill()

  ctx.save()
  roundRect(ctx, x, y, w, h, 4)
  ctx.clip()
  // Translucent diagonal stripes using 8-digit hex alpha
  ctx.strokeStyle = `${color}22`
  ctx.lineWidth   = 5
  for (let s = -h; s < w + h; s += 10) {
    ctx.beginPath()
    ctx.moveTo(x + s,       y)
    ctx.lineTo(x + s - h,  y + h)
    ctx.stroke()
  }
  ctx.restore()

  // Glowing border
  ctx.shadowBlur  = 14
  ctx.shadowColor = color
  roundRect(ctx, x, y, w, h, 4)
  ctx.strokeStyle = color
  ctx.lineWidth   = 2
  ctx.stroke()
  ctx.shadowBlur  = 0

  // Label text
  ctx.fillStyle   = color
  ctx.textAlign   = 'center'
  ctx.textBaseline = 'middle'
  if (lines.length === 1) {
    ctx.font = 'bold 10px monospace'
    ctx.fillText(lines[0], cx, cy)
  } else {
    ctx.font = 'bold 9px monospace'
    ctx.fillText(lines[0], cx, cy - 6)
    ctx.fillText(lines[1], cx, cy + 6)
  }

  ctx.restore()
}

function drawHUD(ctx, score, highScore) {
  // Score
  ctx.save()
  ctx.fillStyle = '#00ccff'
  ctx.font      = 'bold 13px monospace'
  ctx.textAlign = 'right'
  ctx.fillText(`SCORE  ${String(score).padStart(5, '0')}`, CANVAS_W - 14, 20)

  // High score
  ctx.fillStyle = 'rgba(0,200,255,0.42)'
  ctx.font      = '11px monospace'
  ctx.fillText(`HI  ${String(highScore).padStart(5, '0')}`, CANVAS_W - 14, 36)
  ctx.restore()
}

function drawIdleOverlay(ctx) {
  ctx.save()
  ctx.fillStyle = 'rgba(8, 12, 20, 0.72)'
  ctx.fillRect(0, 0, CANVAS_W, CANVAS_H)
  ctx.fillStyle   = '#00ffcc'
  ctx.font        = 'bold 15px monospace'
  ctx.textAlign   = 'center'
  ctx.fillText('[ SPACE  /  ↑  /  TAP  TO  LAUNCH ]', CANVAS_W / 2, CANVAS_H / 2 - 8)
  ctx.fillStyle   = 'rgba(0,220,255,0.5)'
  ctx.font        = '11px monospace'
  ctx.fillText('dodge the cluster faults', CANVAS_W / 2, CANVAS_H / 2 + 12)
  ctx.restore()
}

function drawGameOverOverlay(ctx, score, highScore) {
  ctx.save()
  ctx.fillStyle = 'rgba(8, 12, 20, 0.72)'
  ctx.fillRect(0, 0, CANVAS_W, CANVAS_H)

  ctx.fillStyle   = '#ff4455'
  ctx.font        = 'bold 17px monospace'
  ctx.textAlign   = 'center'
  ctx.fillText('──  POD  TERMINATED  ──', CANVAS_W / 2, CANVAS_H / 2 - 26)

  ctx.fillStyle = '#e0e8f0'
  ctx.font      = '13px monospace'
  ctx.fillText(`score:  ${score}`, CANVAS_W / 2, CANVAS_H / 2 - 2)

  if (score > 0 && score >= highScore) {
    ctx.fillStyle = '#ffee22'
    ctx.font      = 'bold 11px monospace'
    ctx.fillText('✦  new  high  score  ✦', CANVAS_W / 2, CANVAS_H / 2 + 16)
  }

  ctx.fillStyle = '#00ffcc'
  ctx.font      = '12px monospace'
  ctx.fillText('[ SPACE  /  ↑  /  TAP  to  respawn ]', CANVAS_W / 2, CANVAS_H / 2 + 38)
  ctx.restore()
}

// ─── Component ────────────────────────────────────────────────────────────────

/**
 * DinoGame — canvas-based infinite runner with a Kubernetes pod player
 * and DevOps-themed obstacles.
 *
 * Controls: Space / ArrowUp / click / tap to jump (or start / restart).
 */
export default function DinoGame() {
  const canvasRef = useRef(null)

  // All mutable game state lives in a ref so the rAF loop never re-renders
  const s = useRef({
    status: 'idle',        // 'idle' | 'playing' | 'gameover'
    playerY:    GROUND_Y - PLAYER_H,
    velocityY:  0,
    isJumping:  false,
    obstacles:  [],
    bgOffset:   0,
    frameCount: 0,         // frames while playing (used for animation & score)
    distTravelled: 0,      // pixels travelled (used for obstacle spawning)
    nextSpawnAt:   400,    // distTravelled value at which to spawn next obstacle
    speed:      INITIAL_SPEED,
    score:      0,
    highScore:  parseInt(localStorage.getItem('lab404_hi') ?? '0', 10),
  })

  // Minimal React state — only for values shown in JSX buttons outside canvas
  const [gameStatus, setGameStatus] = useState('idle')

  // ── Input handler ─────────────────────────────────────────────────────────
  const handleAction = useCallback(() => {
    const g = s.current
    if (g.status === 'idle') {
      g.status = 'playing'
      setGameStatus('playing')
    } else if (g.status === 'playing' && !g.isJumping) {
      g.velocityY = JUMP_VEL
      g.isJumping = true
    } else if (g.status === 'gameover') {
      // Full reset
      g.status       = 'playing'
      g.playerY      = GROUND_Y - PLAYER_H
      g.velocityY    = 0
      g.isJumping    = false
      g.obstacles    = []
      g.bgOffset     = 0
      g.frameCount   = 0
      g.distTravelled = 0
      g.nextSpawnAt  = 400
      g.speed        = INITIAL_SPEED
      g.score        = 0
      setGameStatus('playing')
    }
  }, [])

  // ── Keyboard listeners ────────────────────────────────────────────────────
  useEffect(() => {
    const onKey = (e) => {
      if (e.code === 'Space' || e.code === 'ArrowUp') {
        e.preventDefault()
        handleAction()
      }
    }
    window.addEventListener('keydown', onKey)
    return () => window.removeEventListener('keydown', onKey)
  }, [handleAction])

  // ── Game loop ─────────────────────────────────────────────────────────────
  useEffect(() => {
    const canvas = canvasRef.current
    const ctx    = canvas.getContext('2d')
    let rafId

    const loop = () => {
      const g = s.current

      // ── Update ──────────────────────────────────────────────────────────
      if (g.status === 'playing') {
        g.frameCount++

        // Speed ramp
        g.score = Math.floor(g.frameCount / 7)
        g.speed = Math.min(INITIAL_SPEED + g.score * SPEED_GROWTH, MAX_SPEED)

        // Background scroll
        g.bgOffset     = (g.bgOffset + g.speed * 0.35) % CANVAS_W
        g.distTravelled += g.speed

        // Player physics
        g.velocityY += GRAVITY
        g.playerY   += g.velocityY
        if (g.playerY >= GROUND_Y - PLAYER_H) {
          g.playerY   = GROUND_Y - PLAYER_H
          g.velocityY = 0
          g.isJumping = false
        }

        // Spawn obstacle when distance threshold is reached
        if (g.distTravelled >= g.nextSpawnAt) {
          g.obstacles.push(randObstacle())
          const gap     = MIN_GAP + Math.random() * (MAX_GAP - MIN_GAP)
          g.nextSpawnAt = g.distTravelled + gap
        }

        // Move & cull obstacles
        for (const o of g.obstacles) o.x -= g.speed
        g.obstacles = g.obstacles.filter(o => o.x + o.w > -10)

        // Collision detection
        const hit = g.obstacles.some(o =>
          rectsOverlap(PLAYER_X, g.playerY, PLAYER_W, PLAYER_H, o.x, o.y, o.w, o.h)
        )
        if (hit) {
          if (g.score > g.highScore) {
            g.highScore = g.score
            localStorage.setItem('lab404_hi', String(g.score))
          }
          g.status = 'gameover'
          setGameStatus('gameover')
        }
      } else {
        // Even while idle/gameover keep background subtly drifting
        g.bgOffset = (g.bgOffset + 0.4) % CANVAS_W
      }

      // ── Draw ────────────────────────────────────────────────────────────
      drawBackground(ctx, g.bgOffset)
      drawGround(ctx, g.bgOffset)
      g.obstacles.forEach(o => drawObstacle(ctx, o))
      drawPlayer(ctx, g.playerY, g.frameCount, g.isJumping)
      drawHUD(ctx, g.score, g.highScore)

      if (g.status === 'idle')     drawIdleOverlay(ctx)
      if (g.status === 'gameover') drawGameOverOverlay(ctx, g.score, g.highScore)

      rafId = requestAnimationFrame(loop)
    }

    rafId = requestAnimationFrame(loop)
    return () => cancelAnimationFrame(rafId)
  }, []) // runs once — game loop owns its own state via ref

  return (
    <div
      className="dino-game-wrapper"
      onClick={handleAction}
      onTouchStart={(e) => { e.preventDefault(); handleAction() }}
    >
      <canvas
        ref={canvasRef}
        width={CANVAS_W}
        height={CANVAS_H}
        className="dino-game-canvas"
        role="img"
        aria-label="Kubernetes pod runner mini-game. Press Space or tap to jump."
      />
    </div>
  )
}
