// Game engine core logic / 游戏引擎核心逻辑
import { TICK_RATE, TICK_INTERVAL, MSG_TYPE, PLAYER_CONFIG } from '../utils/constants.js'
import { Grid } from '../utils/grid.js'
import { InputManager } from '../utils/input.js'
import { GameScene } from './scene.js'
import { NetworkClient } from '../network/client.js'

/**
 * Main game engine that handles game loop, player prediction, and state reconciliation / 主游戏引擎，处理游戏循环、玩家预测和状态同步
 */
export class GameEngine {
  constructor(canvas) {
    this.canvas = canvas
    this.scene = new GameScene(canvas)
    this.input = new InputManager()
    this.network = new NetworkClient()
    this.grid = null
    this.mapData = null

    // Local player identifier / 本地玩家标识
    this.localPlayerId = null
    // Map of all players by ID / 所有玩家的 ID 映射
    this.players = new Map()
    // Map of all zombies by ID / 所有僵尸的 ID 映射
    this.zombies = new Map()

    // Game loop state / 游戏循环状态
    this.running = false
    this.lastTick = 0
    this.accumulator = 0

    // Pending input buffer for reconciliation / 待处理的输入缓冲，用于状态同步
    this.pendingInputs = []

    // Current game time from server / 来自服务器的当前游戏时间
    this.gameTime = 0

    // External state update callback / 外部状态更新回调
    this.onStateUpdate = null
  }

  // Connect to game server / 连接游戏服务器
  async connect(url) {
    await this.network.connect(url)
    this._setupNetworkHandlers()
  }

  // Register network event handlers / 注册网络事件处理器
  _setupNetworkHandlers() {
    // Handle game start from server / 处理服务器发送的游戏开始信号
    this.network.on(MSG_TYPE.GAME_STARTED, (data) => {
      if (!data.mapData) {
        console.error('Server did not provide map data / 服务器未提供地图数据')
        alert('Failed to load map data from server. / 无法从服务器加载地图数据。')
        return
      }
      this.localPlayerId = data.playerId
      this.mapData = data.mapData
      this.grid = new Grid(this.mapData)
      this.scene.buildMap(this.mapData)
      this._initPlayers(data.players)
      this.start()
      if (this.onStateUpdate) this.onStateUpdate('game_started', data)
    })

    // Handle periodic game state updates / 处理定期游戏状态更新
    this.network.on(MSG_TYPE.GAME_STATE, (data) => {
      this._processServerState(data)
    })

    // Handle new player joining / 处理新玩家加入
    this.network.on(MSG_TYPE.PLAYER_JOIN, (data) => {
      this._addPlayer(data)
    })

    // Handle player leaving / 处理玩家离开
    this.network.on(MSG_TYPE.PLAYER_LEAVE, (data) => {
      this._removePlayer(data)
    })

    // Handle server errors / 处理服务器错误
    this.network.on(MSG_TYPE.ERROR, (data) => {
      console.error('Server error:', data.message)
      alert(data.message)
    })
  }

  // Initialize all players from server data / 根据服务器数据初始化所有玩家
  _initPlayers(playersData) {
    const colors = [0x4488ff, 0xff4444, 0x44ff44, 0xffff44]
    for (const p of playersData) {
      const isLocal = p.id === this.localPlayerId
      const color = colors[p.index % colors.length]
      this.players.set(p.id, {
        id: p.id,
        name: p.name,
        x: p.x,
        y: p.y,
        angle: 0,
        health: PLAYER_CONFIG.MAX_HEALTH,
        color,
        isLocal
      })
      this.scene.addPlayer(p.id, p.x, p.y, color, isLocal)
    }
  }

  // Add a new player to the game / 向游戏中添加新玩家
  _addPlayer(data) {
    const colors = [0x4488ff, 0xff4444, 0x44ff44, 0xffff44]
    const isLocal = data.id === this.localPlayerId
    const color = colors[data.index % colors.length]
    this.players.set(data.id, {
      id: data.id,
      name: data.name,
      x: data.x,
      y: data.y,
      angle: 0,
      health: PLAYER_CONFIG.MAX_HEALTH,
      color,
      isLocal
    })
    this.scene.addPlayer(data.id, data.x, data.y, color, isLocal)
  }

  // Remove a player from the game / 从游戏中移除玩家
  _removePlayer(data) {
    this.players.delete(data.id)
    this.scene.removePlayer(data.id)
  }

  // Start the game loop / 启动游戏循环
  start() {
    if (this.running) return
    this.running = true
    this.input.attach()
    this.lastTick = performance.now()
    this._loop()
  }

  // Stop the game loop / 停止游戏循环
  stop() {
    this.running = false
    this.input.detach()
  }

  // Main game loop with fixed timestep / 固定时间步长的主游戏循环
  _loop() {
    if (!this.running) return
    requestAnimationFrame(() => this._loop())

    const now = performance.now()
    const delta = now - this.lastTick
    this.lastTick = now

    this.accumulator += delta
    // Run fixed ticks / 执行固定时间步长更新
    while (this.accumulator >= TICK_INTERVAL) {
      this._tick()
      this.accumulator -= TICK_INTERVAL
    }

    // Update camera to follow local player / 更新相机跟随本地玩家
    const localPlayer = this.players.get(this.localPlayerId)
    if (localPlayer) {
      this.scene.updateCamera(localPlayer.x, localPlayer.y)
    }
    this.scene.render()
  }

  // Single tick update: input, prediction, and network send / 单次tick更新：输入处理、预测和网络发送
  _tick() {
    if (!this.localPlayerId) return

    const localPlayer = this.players.get(this.localPlayerId)
    if (!localPlayer || localPlayer.health <= 0) return

    // Get mouse position on ground plane / 获取鼠标在地面上的位置
    const mouseGroundPos = this.scene.getMouseGroundPos(this.input.mouse.x, this.input.mouse.y)
    this.input.mouse.groundX = mouseGroundPos.x
    this.input.mouse.groundY = mouseGroundPos.y

    const inputState = this.input.buildInputState(mouseGroundPos)

    // Apply client-side prediction / 应用客户端预测
    this._applyLocalPrediction(inputState)

    // Buffer input for reconciliation / 缓存输入用于状态同步
    this.pendingInputs.push(inputState)
    if (this.pendingInputs.length > 60) {
      this.pendingInputs.splice(0, this.pendingInputs.length - 60)
    }

    this.network.sendInput(inputState)
  }

  // Apply local movement prediction / 应用本地移动预测
  _applyLocalPrediction(inputState) {
    const player = this.players.get(this.localPlayerId)
    if (!player) return

    const speed = PLAYER_CONFIG.SPEED
    const dt = TICK_INTERVAL / 1000
    let newX = player.x + inputState.dx * speed * dt
    let newY = player.y + inputState.dy * speed * dt

    // Check wall collisions / 检查墙壁碰撞
    if (this.grid) {
      if (!this.grid.isWalkable(newX, player.y, 0.8)) newX = player.x
      if (!this.grid.isWalkable(player.x, newY, 0.8)) newY = player.y
    }

    // Clamp to map bounds / 限制在地图范围内
    const maxX = this.grid.width - 0.5
    const maxY = this.grid.height - 0.5
    newX = Math.max(0.5, Math.min(maxX, newX))
    newY = Math.max(0.5, Math.min(maxY, newY))

    player.x = newX
    player.y = newY

    // Calculate facing angle toward aim point / 计算朝向瞄准点的角度
    const dx = inputState.aimX - player.x
    const dy = inputState.aimY - player.y
    player.angle = Math.atan2(dx, dy)

    this.scene.updatePlayer(player.id, player.x, player.y, player.angle, player.health)
  }

  // Process server state update / 处理服务器状态更新
  _processServerState(state) {
    if (state.players) {
      for (const ps of state.players) {
        const player = this.players.get(ps.id)
        if (player) {
          if (ps.id === this.localPlayerId) {
            this._reconcileLocalPlayer(ps)
          } else {
            player.x = ps.x
            player.y = ps.y
            player.angle = ps.angle
            player.health = ps.health
            this.scene.updatePlayer(ps.id, ps.x, ps.y, ps.angle, ps.health)
          }
        }
      }
    }

    // Sync zombies
    if (state.zombies) {
      const serverZombieIds = new Set()
      for (const zs of state.zombies) {
        const id = zs.id
        serverZombieIds.add(id)
        if (!this.zombies.has(id)) {
          this.zombies.set(id, { id, x: zs.x, y: zs.y })
          this.scene.addZombie(id, zs.x, zs.y)
        } else {
          const zombie = this.zombies.get(id)
          zombie.x = zs.x
          zombie.y = zs.y
          this.scene.updateZombie(id, zs.x, zs.y, zs.angle)
        }
      }
      for (const [id] of this.zombies) {
        if (!serverZombieIds.has(id)) {
          this.zombies.delete(id)
          this.scene.removeZombie(id)
        }
      }
    }

    // Sync nut walls
    if (state.nutWalls) {
      for (const nw of state.nutWalls) {
        this.scene.updateNutWall(nw.x, nw.y, nw.health, nw.maxHealth)
      }
    }

    if (state.gameTime !== undefined) this.gameTime = state.gameTime

    if (this.onStateUpdate) this.onStateUpdate('state', state)
  }

  // Reconcile local player state with server authority / 用服务器权威数据同步本地玩家状态
  _reconcileLocalPlayer(serverState) {
    const player = this.players.get(this.localPlayerId)
    if (!player) return

    let lastProcessedSeq = serverState.lastProcessedSeq || 0

    // Apply server state / 应用服务器状态
    player.x = serverState.x
    player.y = serverState.y
    player.angle = serverState.angle
    player.health = serverState.health

    // Remove acknowledged inputs / 移除已确认的输入
    this.pendingInputs = this.pendingInputs.filter(input => input.seq > lastProcessedSeq)

    // Re-apply unacknowledged inputs / 重新应用未确认的输入
    if (player.health > 0) {
      const speed = PLAYER_CONFIG.SPEED
      const dt = TICK_INTERVAL / 1000
      for (const input of this.pendingInputs) {
        let newX = player.x + input.dx * speed * dt
        let newY = player.y + input.dy * speed * dt

        if (this.grid) {
          if (!this.grid.isWalkable(newX, player.y, 0.8)) newX = player.x
          if (!this.grid.isWalkable(player.x, newY, 0.8)) newY = player.y
        }

        const maxX = this.grid.width - 0.5
        const maxY = this.grid.height - 0.5
        newX = Math.max(0.5, Math.min(maxX, newX))
        newY = Math.max(0.5, Math.min(maxY, newY))

        player.x = newX
        player.y = newY

        const dx = input.aimX - player.x
        const dy = input.aimY - player.y
        player.angle = Math.atan2(dx, dy)
      }
    }

    this.scene.updatePlayer(player.id, player.x, player.y, player.angle, player.health)
  }

  // Clean up resources / 清理资源
  destroy() {
    this.stop()
    this.network.disconnect()
    this.scene.destroy()
  }
}
