// useGameEngine - Core game state and logic hook
import { useState, useEffect, useRef, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import api from '../../../services/api'
import { useAudio } from '../../../contexts/AudioContext'
import { getTranslatedText } from '../../../utils/i18nUtils'
import type {
  PlayerState,
  Location,
  Profile,
  CombatState,
  CombatLogEntry,
  LocationMessage,
  Equipment,
  WorkbenchTab,
  MobileMenuState
} from '../types'

export interface GameEngineState {
  // Core state
  playerState: PlayerState | null
  location: Location | null
  profile: Profile | null
  loading: boolean
  message: string
  quests: { active: any[], available: any[] }

  // Combat state
  combatState: CombatState | null
  combatLog: CombatLogEntry[]
  enemyName: string
  enemyImage: string
  enemyTurnMessage: string

  // UI state
  selectedItem: any
  collapsedCategories: Set<string>
  expandedCorpse: string | null
  corpseDetails: any
  movementCooldown: number
  equipment: Equipment
  failedActionItemId: string | number | null // For shake animation

  // Workbench state
  showCraftingMenu: boolean
  showRepairMenu: boolean
  craftableItems: any[]
  repairableItems: any[]
  uncraftableItems: any[]
  workbenchTab: WorkbenchTab
  craftFilter: string
  craftCategoryFilter: string
  repairFilter: string
  uncraftFilter: string
  inventoryFilter: string
  inventoryCategoryFilter: string

  // PvP state
  lastSeenPvPAction: string | null
  pvpTimeRemaining: number | null

  // Mobile UI state
  mobileMenuOpen: MobileMenuState
  mobileHeaderOpen: boolean

  // Location messages
  locationMessages: LocationMessage[]

  // Interactable cooldowns
  interactableCooldowns: Record<string, number>
  forceUpdate: number
}

export interface GameEngineActions {
  // Data fetching
  fetchGameData: (skipCombatLogInit?: boolean) => Promise<void>
  fetchLocationData: () => Promise<void>
  fetchPlayerState: () => Promise<void>

  // Movement
  handleMove: (direction: string) => Promise<void>

  // Items
  handlePickup: (itemId: number, quantity?: number) => Promise<void>
  handleUseItem: (itemId: string) => Promise<void>
  handleEquipItem: (inventoryId: number) => Promise<void>
  handleUnequipItem: (slot: string) => Promise<void>
  handleDropItem: (itemId: string, quantity?: number, inventoryId?: number) => Promise<void>

  // Crafting/Workbench
  handleOpenCrafting: () => Promise<void>
  handleCloseCrafting: () => void
  handleCraft: (itemId: string) => Promise<void>
  handleOpenRepair: () => Promise<void>
  handleRepairFromMenu: (uniqueItemId: number, inventoryId?: number) => Promise<void>
  handleUncraft: (uniqueItemId: number, inventoryId: number, quantity?: number) => Promise<void>
  handleSwitchWorkbenchTab: (tab: WorkbenchTab) => Promise<void>

  // Combat
  handleInitiateCombat: (enemyId: number) => Promise<void>
  handleCombatAction: (action: string) => Promise<any>
  handleExitCombat: () => void
  handleExitPvPCombat: () => Promise<void>
  handleInitiatePvP: (targetPlayerId: number) => Promise<void>
  handlePvPAction: (action: string, targetId: number) => Promise<void>
  handlePvPAcknowledge: () => Promise<void>
  handleFlee: () => Promise<void>
  addCombatLogEntry: (entry: CombatLogEntry) => void

  // Interactions
  handleInteract: (interactableId: string, actionId: string) => Promise<void>
  handleViewCorpseDetails: (corpseId: string) => Promise<void>
  handleCloseCorpseDetails: () => void
  handleLootCorpse: (corpseId: string) => Promise<void>
  handleLootCorpseItem: (corpseId: string, itemIndex: number | null) => Promise<void>

  // Stats
  handleSpendPoint: (stat: string) => Promise<void>

  // UI helpers
  addLocationMessage: (msg: string) => void
  setMessage: (msg: string) => void
  setSelectedItem: (item: any) => void
  setMobileMenuOpen: (state: MobileMenuState) => void
  setMobileHeaderOpen: (open: boolean) => void
  setCraftFilter: (filter: string) => void
  setCraftCategoryFilter: (filter: string) => void
  setRepairFilter: (filter: string) => void
  setUncraftFilter: (filter: string) => void
  setInventoryFilter: (filter: string) => void
  setInventoryCategoryFilter: (filter: string) => void
  toggleCategoryCollapse: (category: string) => void

  // WebSocket helpers
  refreshLocation: () => Promise<void>
  refreshCombat: () => Promise<void>
  updatePlayerState: (playerData: any) => void
  updateCombatState: (combatData: any) => void
  updateCooldowns: (cooldowns: Record<string, number>) => void
  addPlayerToLocation: (player: any) => void
  removePlayerFromLocation: (playerId: number) => void
  addNPCToLocation: (npc: any) => void
  removeNPCFromLocation: (enemyId: string) => void
  updateStatusEffect: (effectName: string | any, remainingTicks: number) => void

  // Quests
  updateQuests: (active: any[], available: any[]) => void
  handleQuestUpdate: (quest: any) => void
}

export function useGameEngine(
  token: string | null,
  _handleWebSocketMessage: (message: any) => Promise<void>
): [GameEngineState, GameEngineActions] {
  const { t } = useTranslation()
  const { playSfx } = useAudio()
  // All state declarations
  const [playerState, setPlayerState] = useState<PlayerState | null>(null)
  const [location, setLocation] = useState<Location | null>(null)
  const [profile, setProfile] = useState<Profile | null>(null)
  const [loading, setLoading] = useState(true)
  const [message, setMessage] = useState('')
  const [selectedItem, setSelectedItem] = useState<any>(null)
  const [combatState, setCombatState] = useState<CombatState | null>(null)
  const [combatLog, setCombatLog] = useState<CombatLogEntry[]>([])
  const [enemyName, setEnemyName] = useState<string>('')
  const [enemyImage, setEnemyImage] = useState<string>('')
  const [collapsedCategories, setCollapsedCategories] = useState<Set<string>>(new Set())
  const [expandedCorpse, setExpandedCorpse] = useState<string | null>(null)
  const [corpseDetails, setCorpseDetails] = useState<any>(null)
  const [movementCooldown, setMovementCooldown] = useState<number>(0)
  const [failedActionItemId, setFailedActionItemId] = useState<string | number | null>(null)
  const [quests, setQuests] = useState<{ active: any[], available: any[] }>({ active: [], available: [] })
  // const [enemyTurnMessage, setEnemyTurnMessage] = useState<string>('') // Moved to Combat.tsx

  const [equipment, setEquipment] = useState<Equipment>({})
  const [showCraftingMenu, setShowCraftingMenu] = useState<boolean>(false)
  const [showRepairMenu, setShowRepairMenu] = useState<boolean>(false)
  const [craftableItems, setCraftableItems] = useState<any[]>([])
  const [repairableItems, setRepairableItems] = useState<any[]>([])
  const [workbenchTab, setWorkbenchTab] = useState<WorkbenchTab>('craft')
  const [craftFilter, setCraftFilter] = useState<string>('')
  const [craftCategoryFilter, setCraftCategoryFilter] = useState<string>('all')
  const [repairFilter, setRepairFilter] = useState<string>('')
  const [uncraftFilter, setUncraftFilter] = useState<string>('')
  const [uncraftableItems, setUncraftableItems] = useState<any[]>([])
  const [inventoryFilter, setInventoryFilter] = useState<string>('')
  const [inventoryCategoryFilter, setInventoryCategoryFilter] = useState<string>('all')
  const [lastSeenPvPAction, setLastSeenPvPAction] = useState<string | null>(null)
  const [_pvpTimeRemaining, _setPvpTimeRemaining] = useState<number | null>(null)
  const [mobileMenuOpen, setMobileMenuOpen] = useState<MobileMenuState>('none')
  const [mobileHeaderOpen, setMobileHeaderOpen] = useState<boolean>(false)
  const [locationMessages, setLocationMessages] = useState<LocationMessage[]>(() => {
    try {
      const saved = sessionStorage.getItem('locationMessages')
      return saved ? JSON.parse(saved) : []
    } catch {
      return []
    }
  })
  const [interactableCooldowns, setInteractableCooldowns] = useState<Record<string, number>>({})
  const [loadedTabs, setLoadedTabs] = useState<Set<string>>(new Set())
  const [_forceUpdate, _setForceUpdate] = useState(0)
  // @ts-ignore - WebSocket state is set in useEffect
  const [webSocket, setWebSocket] = useState<WebSocket | null>(null)

  // Refs
  const lastSeenPvPActionRef = useRef<string | null>(null)

  // Movement cooldown countdown effect
  useEffect(() => {
    if (movementCooldown > 0) {
      const timer = setInterval(() => {
        setMovementCooldown((prev: number) => {
          const newVal = prev - 1
          return newVal > 0 ? newVal : 0
        })
      }, 1000)
      return () => clearInterval(timer)
    }
  }, [movementCooldown])

  // Interactable cooldown live countdown re-render
  useEffect(() => {
    if (Object.keys(interactableCooldowns).length > 0) {
      const timer = setInterval(() => {
        _setForceUpdate(Date.now())
      }, 1000)
      return () => clearInterval(timer)
    }
  }, [Object.keys(interactableCooldowns).length])

  // Helper function to add messages to location log
  const addLocationMessage = useCallback((msg: string) => {
    const now = new Date()
    const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })
    const locationName = location?.name || ''

    setLocationMessages((prev: LocationMessage[]) => {
      const newMessages = [...prev, { time: timeStr, message: msg, location_name: locationName }]
      // Keep only last 50 messages
      const trimmed = newMessages.slice(-50)
      // Persist to sessionStorage
      try {
        sessionStorage.setItem('locationMessages', JSON.stringify(trimmed))
      } catch (e) {
        // Ignore storage errors
      }
      return trimmed
    })
    setMessage(msg)
  }, [location])

  const addCombatLogEntry = useCallback((entry: CombatLogEntry) => {
    setCombatLog((prev: CombatLogEntry[]) => [{ ...entry, id: entry.id || Date.now() + Math.random() }, ...prev])
  }, [])

  // Fetch functions
  const fetchLocationData = useCallback(async () => {
    try {
      const locationRes = await api.get('/api/game/location')
      setLocation(locationRes.data)
    } catch (err) {
      console.error('Failed to fetch location:', err)
    }
  }, [])

  const fetchPlayerState = useCallback(async () => {
    try {
      const stateRes = await api.get('/api/game/state')
      const gameState = stateRes.data
      setPlayerState({
        location_id: gameState.player.location_id,
        location_name: gameState.location?.name || 'Unknown',
        health: gameState.player.hp,
        max_health: gameState.player.max_hp,
        stamina: gameState.player.stamina,
        max_stamina: gameState.player.max_stamina,
        inventory: gameState.inventory || [],
        status_effects: gameState.player.status_effects || []
      })
      setEquipment(gameState.equipment || {})

      if (gameState.player.movement_cooldown !== undefined) {
        const cooldown = gameState.player.movement_cooldown
        setMovementCooldown(cooldown > 0 ? Math.ceil(cooldown) + 1 : 0)
      }
    } catch (err) {
      console.error('Failed to fetch player state:', err)
    }
  }, [])

  const fetchGameData = useCallback(async (skipCombatLogInit: boolean = false) => {
    try {
      const [stateRes, locationRes, profileRes, combatRes, pvpRes, activeQuestsRes, availableQuestsRes] = await Promise.all([
        api.get('/api/game/state'),
        api.get('/api/game/location'),
        api.get('/api/game/profile'),
        api.get('/api/game/combat'),
        api.get('/api/game/pvp/status'),
        api.get('/api/quests/active'),
        api.get('/api/quests/available')
      ])

      const gameState = stateRes.data

      // Update quests
      setQuests({
        active: activeQuestsRes.data || [],
        available: availableQuestsRes.data || []
      })

      setPlayerState({
        location_id: gameState.player.location_id,
        location_name: gameState.location?.name || 'Unknown',
        health: gameState.player.hp,
        max_health: gameState.player.max_hp,
        stamina: gameState.player.stamina,
        max_stamina: gameState.player.max_stamina,
        inventory: gameState.inventory || [],
        status_effects: gameState.player.status_effects || []
      })

      setLocation(locationRes.data)
      setProfile(profileRes.data.player || profileRes.data)
      setEquipment(gameState.equipment || {})

      // Initialize interactable cooldowns
      if (locationRes.data.interactables) {
        const cooldowns: Record<string, number> = {}
        for (const interactable of locationRes.data.interactables) {
          if (interactable.actions) {
            for (const action of interactable.actions) {
              if (action.on_cooldown && action.cooldown_remaining > 0) {
                const cooldownKey = `${interactable.instance_id}:${action.id}`
                cooldowns[cooldownKey] = Date.now() / 1000 + action.cooldown_remaining
              }
            }
          }
        }
        setInteractableCooldowns((prev: Record<string, number>) => ({ ...prev, ...cooldowns }))
      }

      if (gameState.player.movement_cooldown !== undefined) {
        const cooldown = gameState.player.movement_cooldown
        setMovementCooldown(cooldown > 0 ? Math.ceil(cooldown) + 1 : 0)
      }

      // Handle PvP combat
      if (pvpRes.data.in_pvp_combat) {
        const newCombatState = { ...pvpRes.data, is_pvp: true }
        setCombatState(newCombatState)

        // Extract opponent info for GameHeader and state
        const opponent = pvpRes.data.pvp_combat.is_attacker ?
          pvpRes.data.pvp_combat.defender :
          pvpRes.data.pvp_combat.attacker

        if (opponent) {
          setEnemyName(opponent.username || opponent.name || 'Unknown Player')
          // If players have avatars in the future, set it here. For now default or specific image if available.
          setEnemyImage(opponent.image || '')
        }

        if (pvpRes.data.pvp_combat.last_action &&
          pvpRes.data.pvp_combat.last_action !== lastSeenPvPActionRef.current) {

          setLastSeenPvPAction(pvpRes.data.pvp_combat.last_action)
          lastSeenPvPActionRef.current = pvpRes.data.pvp_combat.last_action

          const now = new Date()
          const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })
          const [lastAction] = pvpRes.data.pvp_combat.last_action.split('|')

          const yourUsername = pvpRes.data.pvp_combat.is_attacker ?
            pvpRes.data.pvp_combat.attacker.username :
            pvpRes.data.pvp_combat.defender.username

          const isYourAction = lastAction.startsWith(yourUsername + ' ')

          setCombatLog((prev: any) => [{
            time: timeStr,
            message: lastAction,
            isPlayer: isYourAction
          }, ...prev])
        }

        if (!skipCombatLogInit && combatLog.length === 0) {
          const now = new Date()
          const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })
          const opponent = pvpRes.data.pvp_combat.is_attacker ?
            pvpRes.data.pvp_combat.defender :
            pvpRes.data.pvp_combat.attacker
          setCombatLog([{
            id: 'pvp-combat-init',
            time: timeStr,
            message: `PvP combat with ${opponent.username} (Lv. ${opponent.level})!`,
            isPlayer: true
          }])
        }
      } else if (lastSeenPvPAction !== null) {
        setLastSeenPvPAction(null)
        lastSeenPvPActionRef.current = null
      } else if (combatRes.data.in_combat) {
        setCombatState(combatRes.data)
        if (!skipCombatLogInit && combatLog.length === 0) {
          const now = new Date()
          const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })
          setCombatLog([{
            id: 'combat-in-progress',
            time: timeStr,
            message: 'Combat in progress...',
            isPlayer: true
          }])
        }
      }
    } catch (error) {
      console.error('Failed to fetch game data:', error)
      setMessage('Failed to load game data')
    } finally {
      setLoading(false)
    }
  }, [combatLog.length, lastSeenPvPAction])

  // Movement handler - placeholder, needs full implementation
  const handleMove = useCallback(async (direction: string) => {
    if (combatState) {
      setMessage('Cannot move while in combat!')
      return
    }

    if (showCraftingMenu || showRepairMenu) {
      setShowCraftingMenu(false)
      setShowRepairMenu(false)
    }

    setMobileMenuOpen('none')

    // Capture old location name before moving (translated)
    const oldLocationName = location?.name ? getTranslatedText(location.name) : ''

    try {
      setMessage('Moving...')
      const response = await api.post('/api/game/move', { direction })
      setMessage(response.data.message)
      playSfx('/audio/sfx/step.wav')

      // Get new location name and stamina from response (translated if possible)
      // The response might contain the location object      // Add to location log
      // Use the location name directly from the response if available (it handles localization on the backend)
      let newLocationName = 'Unknown'

      if (response.data.new_location_name) {
        // Prefer the name from the response object which we just added to the backend
        newLocationName = getTranslatedText(response.data.new_location_name)
      } else if (response.data.location?.name) {
        // Fallback to location object if present in response (legacy)
        newLocationName = getTranslatedText(response.data.location.name)
      } else if (response.data.new_location_id) {
        // Fallback to ID if no name provided (shouldn't happen with new backend)
        newLocationName = response.data.new_location_id
      }

      const staminaSpent = response.data.stamina_spent || 1

      const msg = t('location.movedTo', {
        from: oldLocationName || 'Unknown',
        to: newLocationName,
        stamina: staminaSpent
      })
      // Add movement message to activity log (only if location changed)
      if (oldLocationName && newLocationName && oldLocationName !== newLocationName) {
        addLocationMessage(msg)
      }

      if (response.data.encounter && response.data.encounter.triggered) {
        const encounter = response.data.encounter
        setMessage(encounter.message)
        setEnemyName(encounter.combat.npc_name)
        setEnemyImage(encounter.combat.npc_image)

        setCombatState({
          in_combat: true,
          combat_over: false,
          player_won: false,
          combat: encounter.combat
        })

        setCombatLog([])
        const now = new Date()
        const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })
        setCombatLog([{
          id: Date.now() + Math.random(),
          time: timeStr,
          message: { type: 'combat_start', data: { npc_name: encounter.combat.npc_name } },
          isPlayer: false
        }])

        await fetchGameData(true)
      } else {
        await fetchGameData()
      }
    } catch (error: any) {
      setMessage(error.response?.data?.detail || 'Move failed')
    }
  }, [combatState, showCraftingMenu, showRepairMenu, fetchGameData, location, t, addLocationMessage])

  // Simplified placeholder handlers
  // (Full implementations would be moved from Game.tsx)
  const handlePickup = async (itemId: number, quantity: number = 1) => {
    try {
      const response = await api.post('/api/game/pickup', { item_id: itemId, quantity })
      addLocationMessage(response.data.message || 'Item picked up!')
      playSfx('/audio/sfx/pickup.wav')
      setFailedActionItemId(null)
      fetchGameData()
    } catch (error: any) {
      addLocationMessage(error.response?.data?.detail || 'Failed to pick up item')
      setFailedActionItemId(itemId)
      // Reset shake after animation
      setTimeout(() => setFailedActionItemId(null), 500)
      // On error (e.g. full inventory), we don't strictly need to refresh game data immediately
      // and it might interfere with the animation if it triggers a re-render.
    }
  }

  const handleOpenCrafting = async () => {
    try {
      const response = await api.get('/api/game/craftable')
      setCraftableItems(response.data.craftable_items)
      setShowCraftingMenu(true)
      setShowRepairMenu(false)
      setWorkbenchTab('craft')
      setLoadedTabs(new Set(['craft']))
    } catch (error: any) {
      setMessage(error.response?.data?.detail || 'Failed to load crafting menu')
    }
  }

  const handleCloseCrafting = () => {
    setShowCraftingMenu(false)
    setShowRepairMenu(false)
    setCraftableItems([])
    setRepairableItems([])
    setUncraftableItems([])
    setCraftFilter('')
    setRepairFilter('')
    setUncraftFilter('')
    setLoadedTabs(new Set())
  }

  const updateStatusEffect = useCallback((effectName: string | any, remainingTicks: number) => {
    setPlayerState((prev: PlayerState | null) => {
      if (!prev) return null

      if (!prev) return null
      const target = typeof effectName === 'object'
        ? (effectName.en || Object.values(effectName)[0])
        : effectName

      if (remainingTicks <= 0) {
        return {
          ...prev,
          status_effects: prev.status_effects.filter(e => {
            const current = typeof e.effect_name === 'object'
              ? (e.effect_name.en || Object.values(e.effect_name)[0])
              : e.effect_name
            return current !== target
          })
        }
      }

      return {
        ...prev,
        status_effects: prev.status_effects.map(e => {
          const current = typeof e.effect_name === 'object'
            ? (e.effect_name.en || Object.values(e.effect_name)[0])
            : e.effect_name
          if (current === target) {
            return { ...e, ticks_remaining: remainingTicks }
          }
          return e
        })
      }
    })
  }, [])

  const updateQuests = useCallback((active: any[], available: any[]) => {
    setQuests({ active, available })
  }, [])

  const handleQuestUpdate = useCallback((quest: any) => {
    setQuests(prev => {
      // 1. Update active quests list
      let newActive = [...prev.active]
      const idx = newActive.findIndex(q => q.quest_id === quest.quest_id)

      // If quest is active or completed, it should be in the active list
      if (quest.status === 'active' || quest.status === 'completed' || quest.status === 'can_turn_in') {
        if (idx >= 0) {
          // Update existing
          newActive[idx] = { ...newActive[idx], ...quest }
        } else {
          // Add new
          newActive.push(quest)
        }
      } else {
        // If failed or cancelled, maybe keep it or update status?
        if (idx >= 0) newActive[idx] = { ...newActive[idx], ...quest }
      }

      // 2. Remove from available list if it was there (since it's now active/completed)
      // Only if status is active/completed. If it became available, we'd need logic for that.
      let newAvailable = prev.available
      if (quest.status === 'active' || quest.status === 'completed') {
        newAvailable = prev.available.filter(q => q.quest_id !== quest.quest_id)
      } else if (quest.status === 'available') {
        // It became available (e.g. repeatable cooldown finished?)
        if (!newAvailable.find(q => q.quest_id === quest.quest_id)) {
          newAvailable = [...newAvailable, quest]
        }
      }

      return { active: newActive, available: newAvailable }
    })
  }, [])

  // State object
  const state: GameEngineState = {
    playerState,
    location,
    profile,
    loading,
    message,
    quests,
    combatState,
    combatLog,
    enemyName,
    enemyImage,
    enemyTurnMessage: '', // Placeholder as it's now handled locally in Combat.tsx
    selectedItem,
    collapsedCategories,
    expandedCorpse,
    corpseDetails,
    movementCooldown,
    equipment,
    failedActionItemId,
    showCraftingMenu,
    showRepairMenu,
    craftableItems,
    repairableItems,
    uncraftableItems,
    workbenchTab,
    craftFilter,
    craftCategoryFilter,
    repairFilter,
    uncraftFilter,
    inventoryFilter,
    inventoryCategoryFilter,
    lastSeenPvPAction,
    pvpTimeRemaining: _pvpTimeRemaining,
    mobileMenuOpen,
    mobileHeaderOpen,
    locationMessages,
    interactableCooldowns,
    forceUpdate: _forceUpdate
  }

  const handleUseItem = async (itemId: string) => {
    try {
      setMessage('Using item...')
      const response = await api.post('/api/game/use_item', { item_id: itemId })
      const data = response.data

      if (combatState && data.in_combat) {
        const now = new Date()
        const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })
        const messages = data.message.split('\n').filter((m: string) => m.trim())
        const parsedMessages = messages.map((msg: string) => {
          try {
            if (msg.trim().startsWith('{')) {
              const parsed = JSON.parse(msg)
              if (parsed.type && parsed.data) return parsed
            }
          } catch (e) { }
          return msg
        })

        const newEntries = parsedMessages.map((msg: any) => ({
          id: `item-use-${Date.now()}-${Math.random()}`,
          time: timeStr,
          message: msg,
          isPlayer: typeof msg === 'object'
            ? msg.type !== 'enemy_attack' && msg.type !== 'flee_fail'
            : !msg.includes('attacks') && !msg.includes('hits')
        }))
        setCombatLog((prev: any) => [...newEntries, ...prev])

        if (data.combat_over) {
          setCombatState({
            ...combatState,
            combat_over: true,
            player_won: data.player_won
          })
        }
      } else {
        const msg = data.message || 'Item used!'
        addLocationMessage(msg)
      }

      fetchGameData()
    } catch (error: any) {
      setMessage(error.response?.data?.detail || 'Failed to use item')
    }
  }

  const handleEquipItem = async (inventoryId: number) => {
    try {
      setMessage('Equipping item...')
      const response = await api.post('/api/game/equip', { inventory_id: inventoryId })
      const msg = response.data.message || 'Item equipped!'
      addLocationMessage(msg)
      fetchGameData()
    } catch (error: any) {
      setMessage(error.response?.data?.detail || 'Failed to equip item')
    }
  }

  const handleUnequipItem = async (slot: string) => {
    try {
      setMessage('Unequipping item...')
      const response = await api.post('/api/game/unequip', { slot })
      const msg = response.data.message || 'Item unequipped!'
      addLocationMessage(msg)
      fetchGameData()
    } catch (error: any) {
      setMessage(error.response?.data?.detail || 'Failed to unequip item')
    }
  }

  const handleDropItem = async (itemId: string, quantity: number = 1, inventoryId?: number) => {
    try {
      setMessage(`Dropping ${quantity} item(s)...`)
      const payload: any = { item_id: itemId, quantity }
      if (inventoryId) {
        payload.inventory_id = inventoryId
      }
      const response = await api.post('/api/game/item/drop', payload)
      const msg = response.data.message || 'Item dropped!'
      addLocationMessage(msg)
      fetchGameData()
    } catch (error: any) {
      setMessage(error.response?.data?.detail || 'Failed to drop item')
    }
  }

  const refreshWorkbenchData = async () => {
    // Always fetch game data (inventory, stats)
    await fetchGameData()

    // Refresh the current tab's data
    if (workbenchTab === 'craft') {
      const res = await api.get('/api/game/craftable')
      setCraftableItems(res.data.craftable_items)
    } else if (workbenchTab === 'repair') {
      const res = await api.get('/api/game/repairable')
      setRepairableItems(res.data.repairable_items)
    } else if (workbenchTab === 'uncraft') {
      const res = await api.get('/api/game/salvageable')
      setUncraftableItems(res.data.salvageable_items)
    }

    // Invalidate other tabs so they refresh when visited
    setLoadedTabs(new Set([workbenchTab]))
  }

  const handleCraft = async (itemId: string) => {
    try {
      // setMessage('Crafting...') // Loading state ok to keep specific or remove? Let's remove to avoid spam
      const response = await api.post('/api/game/craft_item', { item_id: itemId })
      addLocationMessage(response.data.message || 'Item crafted!')
      await refreshWorkbenchData()
    } catch (error: any) {
      addLocationMessage(error.response?.data?.detail || 'Failed to craft item')
    }
  }

  const handleOpenRepair = async () => {
    try {
      const response = await api.get('/api/game/repairable')
      setRepairableItems(response.data.repairable_items)
      setShowRepairMenu(true)
      setShowCraftingMenu(false)
      setWorkbenchTab('repair')
      setLoadedTabs(new Set(['repair']))
    } catch (error: any) {
      addLocationMessage(error.response?.data?.detail || 'Failed to load repair menu')
    }
  }

  const handleRepairFromMenu = async (uniqueItemId: number, inventoryId?: number) => {
    try {
      // setMessage('Repairing...')
      const response = await api.post('/api/game/repair_item', {
        unique_item_id: uniqueItemId,
        inventory_id: inventoryId
      })
      addLocationMessage(response.data.message || 'Item repaired!')
      await refreshWorkbenchData()
    } catch (error: any) {
      addLocationMessage(error.response?.data?.detail || 'Failed to repair item')
    }
  }

  const handleUncraft = async (uniqueItemId: number, inventoryId: number, quantity: number = 1) => {
    try {
      // setMessage('Salvaging...')
      const response = await api.post('/api/game/uncraft_item', {
        unique_item_id: uniqueItemId,
        inventory_id: inventoryId,
        quantity: quantity
      })
      const data = response.data
      let msg = data.message || 'Item salvaged!'
      if (data.materials_yielded && data.materials_yielded.length > 0) {
        msg += '\n✅ Yielded: ' + data.materials_yielded.map((m: any) => `${m.emoji} ${m.name} x${m.quantity}`).join(', ')
      }
      if (data.materials_lost && data.materials_lost.length > 0) {
        msg += '\n⚠️ Lost: ' + data.materials_lost.map((m: any) => `${m.emoji} ${m.name} x${m.quantity}`).join(', ')
      }
      addLocationMessage(msg)
      await refreshWorkbenchData()
    } catch (error: any) {
      addLocationMessage(error.response?.data?.detail || 'Failed to uncraft item')
    }
  }

  const handleSwitchWorkbenchTab = async (tab: 'craft' | 'repair' | 'uncraft') => {
    setWorkbenchTab(tab)

    if (loadedTabs.has(tab)) {
      return
    }

    try {
      if (tab === 'craft') {
        const response = await api.get('/api/game/craftable')
        setCraftableItems(response.data.craftable_items)
      } else if (tab === 'repair') {
        const response = await api.get('/api/game/repairable')
        setRepairableItems(response.data.repairable_items)
      } else if (tab === 'uncraft') {
        const salvageableRes = await api.get('/api/game/salvageable')
        setUncraftableItems(salvageableRes.data.salvageable_items)
      }
      setLoadedTabs(prev => new Set(prev).add(tab))
    } catch (error: any) {
      setMessage(error.response?.data?.detail || 'Failed to load items')
    }
  }

  const handleInitiateCombat = async (enemyId: number) => {
    try {
      setMobileMenuOpen('none')

      const response = await api.post('/api/game/combat/initiate', { enemy_id: enemyId })

      // Properly structure combat state with in_combat flag and nested combat object
      setCombatState({
        in_combat: true,
        combat_over: false,
        player_won: false,
        combat: response.data.combat
      })

      setEnemyName(response.data.combat.npc_name)
      setEnemyImage(response.data.combat.npc_image)

      const now = new Date()
      const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })
      setCombatLog([{
        id: Date.now() + Math.random(),
        time: timeStr,
        message: { type: 'combat_start', data: { npc_name: response.data.combat.npc_name } },
        isPlayer: true
      }])

      const locationRes = await api.get('/api/game/location')
      setLocation(locationRes.data)
    } catch (error: any) {
      setMessage(error.response?.data?.detail || 'Failed to initiate combat')
    }
  }

  const handleCombatAction = async (action: string) => {
    try {
      let payload: any = { action }
      if (action.startsWith('skill:')) {
        payload = { action: 'skill', skill_id: action.substring(6) }
      } else if (action.includes(':')) {
        const [act, itemId] = action.split(':')
        payload = { action: act, item_id: itemId }
      }

      const response = await api.post('/api/game/combat/action', payload)

      if (response.data.quest_updates) {
        response.data.quest_updates.forEach((q: any) => handleQuestUpdate(q))
      }

      return response.data
    } catch (error: any) {
      setMessage(error.response?.data?.detail || 'Combat action failed')
      throw error
    }
  }

  const handleExitCombat = () => {
    setCombatState(null)
    setCombatLog([])
    setEnemyName('')
    setEnemyImage('')
    fetchGameData()
  }

  const handleExitPvPCombat = async () => {
    if (combatState?.pvp_combat?.id) {
      try {
        await api.post('/api/game/pvp/acknowledge', { combat_id: combatState.pvp_combat.id })
      } catch (error) {
        console.error('Failed to acknowledge PvP combat:', error)
      }
    }
    setCombatState(null)
    setCombatLog([])
    setLastSeenPvPAction(null)
    lastSeenPvPActionRef.current = null
    fetchGameData()
  }

  const handlePvPAction = async (action: string, _targetId: number) => {
    try {
      let payload: any = { action }
      if (action.includes(':')) {
        const [act, itemId] = action.split(':')
        payload = { action: act, item_id: itemId }
      }

      const response = await api.post('/api/game/pvp/action', payload)
      setMessage(response.data.message || 'Action performed!')
      await fetchGameData()
      return response.data // Return data so caller can use it
    } catch (error: any) {
      setMessage(error.response?.data?.detail || 'PvP action failed')
      throw error // Re-throw so caller knows it failed
    }
  }

  const handlePvPAcknowledge = async () => {
    if (combatState?.pvp_combat?.id) {
      try {
        await api.post('/api/game/pvp/acknowledge', { combat_id: combatState.pvp_combat.id })
        await handleExitPvPCombat()
      } catch (error: any) {
        setMessage(error.response?.data?.detail || 'Failed to acknowledge')
      }
    }
  }

  const handleFlee = async () => {
    await handleCombatAction('flee')
  }

  const handleInteract = async (interactableId: string, actionId: string) => {
    if (combatState) {
      setMessage('Cannot interact with objects while in combat!')
      return
    }

    setMobileMenuOpen('none')

    try {
      const response = await api.post('/api/game/interact', {
        interactable_id: interactableId,
        action_id: actionId
      })
      const data = response.data
      let msg = data.message
      if (data.items_found && data.items_found.length > 0) {
        msg += '\n\n📦 ' + t('game.found') + ': ' + data.items_found.join(', ')
      }
      if (data.hp_change) {
        msg += `\n❤️ HP: ${data.hp_change > 0 ? '+' : ''}${data.hp_change}`
      }
      addLocationMessage(msg)
      fetchGameData()
    } catch (error: any) {
      addLocationMessage(error.response?.data?.detail || 'Interaction failed')
    }
  }

  const handleViewCorpseDetails = async (corpseId: string) => {
    try {
      const response = await api.get(`/api/game/corpse/${corpseId}`)
      setCorpseDetails(response.data)
      setExpandedCorpse(corpseId)
    } catch (error: any) {
      setMessage(error.response?.data?.detail || 'Failed to examine corpse')
    }
  }

  const handleLootCorpseItem = async (corpseId: string, itemIndex: number | null = null) => {
    try {
      setMessage('Looting...')
      const response = await api.post('/api/game/loot_corpse', {
        corpse_id: corpseId,
        item_index: itemIndex
      })

      addLocationMessage(response.data.message)
      setTimeout(() => { }, 5000)

      if (response.data.corpse_empty) {
        setExpandedCorpse(null)
        setCorpseDetails(null)
      } else if (expandedCorpse === corpseId) {
        try {
          const detailsResponse = await api.get(`/api/game/corpse/${corpseId}`)
          setCorpseDetails(detailsResponse.data)
        } catch (err) {
          setExpandedCorpse(null)
          setCorpseDetails(null)
        }
      }

      fetchGameData()
    } catch (error: any) {
      addLocationMessage(error.response?.data?.detail || 'Failed to loot corpse')
    }
  }

  const handleLootCorpse = async (corpseId: string) => {
    handleViewCorpseDetails(corpseId)
  }

  const handleSpendPoint = async (stat: string) => {
    try {
      // setMessage(`Increasing ${stat}...`)
      const response = await api.post(`/api/game/spend_point?stat=${stat}`)
      addLocationMessage(response.data.message || 'Stat increased!')
      fetchGameData()
    } catch (error: any) {
      addLocationMessage(error.response?.data?.detail || 'Failed to spend point')
    }
  }

  const handleInitiatePvP = async (targetPlayerId: number) => {
    try {
      const response = await api.post('/api/game/pvp/initiate', { target_player_id: targetPlayerId })
      setMessage(response.data.message || 'PvP combat initiated!')
      await fetchGameData()
    } catch (error: any) {
      setMessage(error.response?.data?.detail || 'Failed to initiate PvP')
    }
  }

  // WebSocket helper functions - for updating state directly from WebSocket messages
  const refreshLocation = async () => {
    try {
      await fetchLocationData()
    } catch (error) {
      console.error('Failed to refresh location:', error)
    }
  }

  const refreshCombat = async () => {
    try {
      const combatRes = await api.get('/api/game/combat')
      if (combatRes.data.in_combat) {
        // Set combat state with proper structure
        setCombatState({
          in_combat: true,
          combat_over: false,
          combat: combatRes.data.combat
        })

        // Update enemy name/image state
        if (combatRes.data.combat?.npc_name) {
          setEnemyName(combatRes.data.combat.npc_name)
        }
        if (combatRes.data.combat?.npc_image) {
          setEnemyImage(combatRes.data.combat.npc_image)
        }
      } else {
        setCombatState(null)
        setEnemyName('')
        setEnemyImage('')
      }
    } catch (error) {
      console.error('Failed to refresh combat:', error)
    }
  }

  const updatePlayerState = (playerData: any) => {
    // Map API field names to playerState field names
    const mappedData: any = {}

    // HP updates are now controlled by Combat.tsx - it calls updatePlayerState at the right time
    if (playerData.hp !== undefined) {
      mappedData.health = playerData.hp
    }
    if (playerData.max_hp !== undefined) {
      mappedData.max_health = playerData.max_hp
    }
    if (playerData.stamina !== undefined) {
      mappedData.stamina = playerData.stamina
    }
    if (playerData.max_stamina !== undefined) {
      mappedData.max_stamina = playerData.max_stamina
    }

    // Update playerState with mapped fields
    if (Object.keys(mappedData).length > 0) {
      setPlayerState((prev: any) => prev ? { ...prev, ...mappedData } : null)
    }

    // Also update profile for consistency
    if (playerData.hp !== undefined && profile) {
      setProfile((prev: any) => prev ? { ...prev, hp: playerData.hp } : null)
    }
    if (playerData.xp !== undefined && profile) {
      setProfile((prev: any) => prev ? { ...prev, xp: playerData.xp } : null)
    }
    if (playerData.level !== undefined && profile) {
      setProfile((prev: any) => prev ? { ...prev, level: playerData.level } : null)
    }
  }

  const updateCombatState = (combatData: any) => {
    setCombatState((prev: any) => {
      // If we have no previous state, but we're receiving combat data, initialize it
      if (!prev) {
        if (combatData.in_combat || combatData.pvp_combat) {
          return {
            in_combat: true,
            combat_over: false,
            ...combatData,
            combat: combatData.combat || (combatData.pvp_combat ? null : {})
          }
        }
        return null
      }

      // Preserve enemy name/image when updating combat state
      return {
        ...prev,
        ...combatData,
        combat: combatData.combat ? {
          ...combatData.combat,
          npc_name: enemyName || combatData.combat.npc_name,
          npc_image: enemyImage || combatData.combat.npc_image
        } : prev.combat
      }
    })
  }

  const updateCooldowns = (cooldowns: Record<string, number>) => {
    setInteractableCooldowns((prev: any) => ({ ...prev, ...cooldowns }))
  }

  const addPlayerToLocation = (player: any) => {
    setLocation((prev: any) => {
      if (!prev) return prev
      // Check if player already exists
      const playerExists = prev.other_players?.some((p: any) => p.id === player.id)
      if (playerExists) return prev
      return {
        ...prev,
        other_players: [...(prev.other_players || []), player]
      }
    })
  }

  const removePlayerFromLocation = (playerId: number) => {
    setLocation((prev: any) => {
      if (!prev) return prev
      return {
        ...prev,
        other_players: (prev.other_players || []).filter((p: any) => p.id !== playerId)
      }
    })
  }

  const addNPCToLocation = (npc: any) => {
    setLocation((prev: any) => {
      if (!prev) return prev
      return {
        ...prev,
        npcs: [...(prev.npcs || []), npc]
      }
    })
  }

  const removeNPCFromLocation = (enemyId: string) => {
    setLocation((prev: any) => {
      if (!prev) return prev
      return {
        ...prev,
        npcs: (prev.npcs || []).filter((npc: any) =>
          !(npc.type === 'enemy' && npc.is_wandering && npc.id === enemyId)
        )
      }
    })
  }

  // Actions object
  const actions: GameEngineActions = {
    fetchGameData,
    fetchLocationData,
    fetchPlayerState,
    handleMove,
    handlePickup,
    handleUseItem,
    handleEquipItem,
    handleUnequipItem,
    handleDropItem,
    handleOpenCrafting,
    handleCloseCrafting,
    handleCraft,
    handleOpenRepair,
    handleRepairFromMenu,
    handleUncraft,
    handleSwitchWorkbenchTab,
    handleInitiateCombat,
    handleCombatAction,
    handleExitCombat,
    handleExitPvPCombat,
    handleInitiatePvP,
    handlePvPAction,
    handlePvPAcknowledge,
    handleFlee,
    handleInteract,
    handleViewCorpseDetails,
    handleCloseCorpseDetails: () => {
      setExpandedCorpse(null)
      setCorpseDetails(null)
    },
    handleLootCorpse,
    handleLootCorpseItem,
    handleSpendPoint,
    addLocationMessage,
    setMessage,
    setSelectedItem,
    setMobileMenuOpen,
    setMobileHeaderOpen,
    setCraftFilter,
    setCraftCategoryFilter,
    setRepairFilter,
    setUncraftFilter,
    setInventoryFilter,
    setInventoryCategoryFilter,
    // WebSocket helper functions
    refreshLocation,
    refreshCombat,
    updatePlayerState,
    updateCombatState,
    updateCooldowns,
    addPlayerToLocation,
    removePlayerFromLocation,
    addNPCToLocation,
    removeNPCFromLocation,
    addCombatLogEntry,
    toggleCategoryCollapse: (category: string) => {
      setCollapsedCategories((prev: Set<string>) => {
        const newSet = new Set(prev)
        if (newSet.has(category)) {
          newSet.delete(category)
        } else {
          newSet.add(category)
        }
        return newSet
      })
    },
    updateStatusEffect,
    updateQuests,
    handleQuestUpdate
  }

  // Polling fallback for PvP Combat reliability
  // Polling fallback for PvP Combat reliability
  // optimized: poll less frequently (15s) and rely on WS reconnect event
  useEffect(() => {
    // 1. Listen for WebSocket reconnection to fetch immediately
    const handleReconnect = () => {
      console.log("[PvP] WebSocket reconnected, fetching fresh state...");
      fetchGameData(true);
    };
    window.addEventListener('game-ws-connected', handleReconnect);

    // 2. Slow polling as safety net
    let interval: ReturnType<typeof setInterval> | null = null;
    if (combatState?.is_pvp && !combatState?.combat_over) {
      interval = setInterval(() => {
        fetchGameData(true);
      }, 15000); // Poll every 15s instead of 3s
    }

    return () => {
      window.removeEventListener('game-ws-connected', handleReconnect);
      if (interval) clearInterval(interval);
    };
  }, [combatState?.is_pvp, combatState?.combat_over, fetchGameData]);

  // Initial data load
  useEffect(() => {
    if (token) {
      fetchGameData()
    }
  }, [token])

  // WebSocket Event Bus Listener
  // Instead of maintaining a second connection, we listen to the global connection managed by GameHeader
  useEffect(() => {
    const handleGameMessage = (event: Event) => {
      const customEvent = event as CustomEvent;
      if (customEvent.detail) {
        _handleWebSocketMessage(customEvent.detail);
      }
    };

    window.addEventListener('game-ws-message', handleGameMessage);

    return () => {
      window.removeEventListener('game-ws-message', handleGameMessage);
    };
  }, [_handleWebSocketMessage]);

  return [state, actions]
}
