"""
Game Routes router.
Auto-generated from main.py migration.
"""
from fastapi import APIRouter, HTTPException, Depends, status, Request
from fastapi.security import HTTPAuthorizationCredentials
from typing import Optional, Dict, Any
from datetime import datetime
import random
import json
import logging

from ..core.security import get_current_user, security, verify_internal_key
from ..services.models import *
from ..services.helpers import calculate_distance, calculate_stamina_cost, calculate_player_capacity, get_locale_string, get_game_message
from .. import database as db
from ..items import ItemsManager
from .. import game_logic
from ..core.websockets import manager
from ..services.stats import STAT_CAP, invalidate_stats_cache

logger = logging.getLogger(__name__)

# These will be injected by main.py
LOCATIONS = None
ITEMS_MANAGER = None
WORLD = None
redis_manager = None

def init_router_dependencies(locations, items_manager, world, redis_mgr=None):
    """Initialize router with game data dependencies"""
    print("🔧 INITIALIZING GAME ROUTE DEPENDENCIES")
    global LOCATIONS, ITEMS_MANAGER, WORLD, redis_manager
    LOCATIONS = locations
    ITEMS_MANAGER = items_manager
    WORLD = world
    redis_manager = redis_mgr
    
    print(f"🔧 Locations keys: {list(LOCATIONS.keys())}")
    
    # Load separate static NPCs
    from pathlib import Path
    try:
        # Use relative path consistent with Docker WORKDIR /app
        json_path = Path("./gamedata/static_npcs.json")
        with open(json_path, "r") as f:
            npc_data = json.load(f).get("static_npcs", {})
            print(f"🔧 Loaded static NPCs data keys: {list(npc_data.keys())}")
            
        for npc_id, npc_def in npc_data.items():
            loc_id = npc_def.get("location_id")
            if loc_id and loc_id in LOCATIONS:
                # Check for duplication
                location = LOCATIONS[loc_id]
                existing = False
                for existing_npc in location.npcs:
                    if isinstance(existing_npc, dict) and existing_npc.get("id") == npc_id:
                        existing = True
                        break
                
                if not existing:
                    # Inject
                    location.npcs.append({
                        "id": npc_id,
                        "name": npc_def.get("name"), # Keep as dict/string, frontend handles localization
                        "type": "npc",
                        "level": 1, 
                        "image_path": npc_def.get("image"),
                        "is_static": True,
                        "trade": npc_def.get("trade", {}) # Setup trade config for frontend checks
                    })
                    print(f"✅ Injected static NPC {npc_id} into {loc_id}")
            else:
                print(f"⚠️ Could not inject NPC {npc_id}: Location {loc_id} not found")
                    
    except Exception as e:
        print(f"❌ Failed to inject static NPCs: {e}")

router = APIRouter(tags=["game"])


@router.get("/api/cache/stats")
async def get_cache_stats(
    current_user: dict = Depends(get_current_user)
):
    """Get Redis cache statistics for monitoring performance"""
    if not redis_manager or not redis_manager.redis_client:
        return {
            "enabled": False,
            "message": "Redis caching is not available"
        }
    
    try:
        # Get Redis stats
        stats = await redis_manager.get_cache_stats()
        
        # Calculate hit rate
        hits = stats.get('keyspace_hits', 0)
        misses = stats.get('keyspace_misses', 0)
        total_requests = hits + misses
        hit_rate = (hits / total_requests * 100) if total_requests > 0 else 0
        
        # Check if current user's inventory is cached
        inventory_cached = await redis_manager.get_cached_inventory(current_user['id']) is not None
        
        return {
            "enabled": True,
            "redis_stats": {
                "total_commands_processed": stats.get('total_commands_processed', 0),
                "ops_per_second": stats.get('instantaneous_ops_per_sec', 0),
                "connected_clients": stats.get('connected_clients', 0),
            },
            "cache_performance": {
                "hits": hits,
                "misses": misses,
                "total_requests": total_requests,
                "hit_rate_percent": round(hit_rate, 2)
            },
            "current_user": {
                "inventory_cached": inventory_cached,
                "player_id": current_user['id']
            }
        }
    except Exception as e:
        logger.error(f"Error getting cache stats: {e}")
        return {
            "enabled": True,
            "error": str(e)
        }


async def _get_enriched_inventory(player_id: int):
    """
    Helper function to get enriched inventory data with durability, unique stats, and workbench flags.
    Returns: (inventory_list, total_weight, total_volume, max_weight, max_volume)
    """
    inventory_raw = await db.get_inventory(player_id)
    inventory = []
    total_weight = 0.0
    total_volume = 0.0
    max_weight = 10.0  # Base capacity
    max_volume = 10.0  # Base capacity
    
    for inv_item in inventory_raw:
        item = ITEMS_MANAGER.get_item(inv_item['item_id'])
        if item:
            item_weight = item.weight * inv_item['quantity']
            # Equipped items count for weight but not volume
            if not inv_item['is_equipped']:
                item_volume = item.volume * inv_item['quantity']
                total_volume += item_volume
            total_weight += item_weight
            
            # Get unique item data if this is a unique item
            durability = None
            max_durability = None
            tier = None
            unique_stats = None
            current_durability = None
            needs_repair = False
            
            if inv_item.get('unique_item_id'):
                unique_item = await db.get_unique_item(inv_item['unique_item_id'])
                if unique_item:
                    durability = unique_item.get('durability')
                    max_durability = unique_item.get('max_durability')
                    tier = unique_item.get('tier')
                    unique_stats = unique_item.get('unique_stats')
                    current_durability = durability
                    if durability is not None and max_durability is not None:
                        needs_repair = durability < max_durability
                    # Check for equipped bags/containers to increase capacity
                    if inv_item['is_equipped']:
                        max_weight += unique_stats.get('weight_capacity', 0)
                        max_volume += unique_stats.get('volume_capacity', 0)
            
            # Workbench flags
            is_repairable = getattr(item, 'repairable', False) and inv_item.get('unique_item_id') is not None
            is_salvageable = getattr(item, 'uncraftable', False)
            
            inventory.append({
                "id": inv_item['id'],
                "item_id": item.id,
                "name": item.name,
                "description": item.description,
                "type": item.type,
                "category": getattr(item, 'category', item.type),
                "quantity": inv_item['quantity'],
                "is_equipped": inv_item['is_equipped'],
                "equippable": item.equippable,
                "consumable": item.consumable,
                "weight": item.weight,
                "volume": item.volume,
                "image_path": item.image_path,
                "emoji": item.emoji,
                "slot": item.slot,
                "durability": durability if durability is not None else None,
                "max_durability": max_durability if max_durability is not None else None,
                "tier": tier if tier is not None else None,
                "unique_stats": unique_stats,
                "hp_restore": item.effects.get('hp_restore') if item.effects else None,
                "stamina_restore": item.effects.get('stamina_restore') if item.effects else None,
                "effects": item.effects,
                "damage_min": item.stats.get('damage_min') if item.stats else None,
                "damage_max": item.stats.get('damage_max') if item.stats else None,
                "stats": item.stats,
                # Workbench flags
                "value": getattr(item, 'value', 10),
                "is_repairable": is_repairable,
                "is_salvageable": is_salvageable,
                "current_durability": current_durability,
                "needs_repair": needs_repair
            })
    
    return inventory, total_weight, total_volume, max_weight, max_volume


# Endpoints

@router.get("/api/game/state")
async def get_game_state(current_user: dict = Depends(get_current_user)):
    """Get complete game state for the player"""
    player_id = current_user['id']
    
    # Get player data
    player = await db.get_player_by_id(player_id)
    if not player:
        raise HTTPException(status_code=404, detail="Player not found")
    
    # Get player status effects
    status_effects = await db.get_player_effects(player_id)
    player['status_effects'] = status_effects
    
    # Get location
    location = LOCATIONS.get(player['location_id'])
    
    # Get enriched inventory with capacity calculations
    inventory, total_weight, total_volume, max_weight, max_volume = await _get_enriched_inventory(player_id)
    
    # Get equipped items
    equipment_slots = await db.get_all_equipment(player_id)
    equipment = {}
    for slot, item_data in equipment_slots.items():
        if item_data and item_data['item_id']:
            inv_item = await db.get_inventory_item_by_id(item_data['item_id'])
            if inv_item:
                item_def = ITEMS_MANAGER.get_item(inv_item['item_id'])
                if item_def:
                    # Get unique item data if this is a unique item
                    durability = None
                    max_durability = None
                    tier = None
                    unique_stats = None
                    if inv_item.get('unique_item_id'):
                        unique_item = await db.get_unique_item(inv_item['unique_item_id'])
                        if unique_item:
                            durability = unique_item.get('durability')
                            max_durability = unique_item.get('max_durability')
                            tier = unique_item.get('tier')
                            unique_stats = unique_item.get('unique_stats')
                    
                    equipment[slot] = {
                        "inventory_id": item_data['item_id'],
                        "item_id": item_def.id,
                        "name": item_def.name,
                        "description": item_def.description,
                        "emoji": item_def.emoji,
                        "image_path": item_def.image_path,
                        "durability": durability if durability is not None else None,
                        "max_durability": max_durability if max_durability is not None else None,
                        "tier": tier if tier is not None else None,
                        "unique_stats": unique_stats,
                        "stats": item_def.stats,
                        "encumbrance": item_def.encumbrance,
                        "weapon_effects": item_def.weapon_effects if hasattr(item_def, 'weapon_effects') else {}
                    }
                else:
                    logger.error(f"❌ Item definition not found for equipped item: {inv_item['item_id']} (slot: {slot})")
            else:
                logger.warning(f"⚠️ Inventory item not found for equipped slot: {slot} (ID: {item_data['item_id']})")

        if slot not in equipment:
            equipment[slot] = None
    
    # Get active combat (PvE)
    combat = await db.get_active_combat(player_id)
    pvp_combat = None
    
    # If no PvE combat, check for PvP combat
    if not combat:
        pvp_combat = await db.get_pvp_combat_by_player(player_id)
        if pvp_combat:
            # Format PvP combat to match frontend expectations or pass as dedicated field
            # Ideally, we pass it as 'pvp_combat' in the response and let frontend handle it,
            # OR we standardize the 'combat' field. Game.tsx seems to handle both.
            # But let's check Game.tsx or Combat.tsx props.
            # Combat.tsx expects: initialCombatData which has { combat: ..., pvp_combat: ..., is_pvp: bool }
            # If we return it in the main dict, Game.tsx passes the whole response to Combat.
            
            # Enrich PvP combat with opponent data for the API response
            is_attacker = pvp_combat['attacker_character_id'] == player_id
            opponent_id = pvp_combat['defender_character_id'] if is_attacker else pvp_combat['attacker_character_id']
            opponent = await db.get_player_by_id(opponent_id)
            
            if is_attacker:
                pvp_combat['attacker'] = player
                pvp_combat['defender'] = opponent
                pvp_combat['is_attacker'] = True
            else:
                pvp_combat['attacker'] = opponent
                pvp_combat['defender'] = player
                pvp_combat['is_attacker'] = False
                
            # Determine if it's "combat_over" based on fled status or HP
            # This helps the frontend break out of the loop
            if pvp_combat.get('attacker_fled') or pvp_combat.get('defender_fled') or \
               pvp_combat.get('attacker_acknowledged') and pvp_combat.get('defender_acknowledged'): # Wait, if both ack, it's deleted.
                # If just fled, it's over but waiting for ack
                pass
            
    if combat:
        # Ensure intent is present (handle legacy)
        if 'npc_intent' not in combat or not combat['npc_intent']:
            combat['npc_intent'] = 'attack'
    
    # Get dropped items at location and enrich with item data
    dropped_items_raw = await db.get_dropped_items(player['location_id'])
    dropped_items = []
    for dropped_item in dropped_items_raw:
        item = ITEMS_MANAGER.get_item(dropped_item['item_id'])
        if item:
            # Get unique item data if this is a unique item
            durability = None
            max_durability = None
            tier = None
            if dropped_item.get('unique_item_id'):
                unique_item = await db.get_unique_item(dropped_item['unique_item_id'])
                if unique_item:
                    durability = unique_item.get('durability')
                    max_durability = unique_item.get('max_durability')
                    tier = unique_item.get('tier')
            
            dropped_items.append({
                "id": dropped_item['id'],
                "item_id": item.id,
                "name": item.name,
                "description": item.description,
                "type": item.type,
                "quantity": dropped_item['quantity'],
                "image_path": item.image_path,
                "emoji": item.emoji,
                "weight": item.weight,
                "volume": item.volume,
                "durability": durability if durability is not None else None,
                "max_durability": max_durability if max_durability is not None else None,
                "tier": tier if tier is not None else None,
                "hp_restore": item.effects.get('hp_restore') if item.effects else None,
                "stamina_restore": item.effects.get('stamina_restore') if item.effects else None,
                "effects": item.effects,
                "damage_min": item.stats.get('damage_min') if item.stats else None,
                "damage_max": item.stats.get('damage_max') if item.stats else None
            })
    
    # Convert location to dict
    location_dict = None
    if location:
        location_dict = {
            "id": location.id,
            "name": location.name,
            "description": location.description,
            "exits": location.exits,
            "image_path": location.image_path,
            "x": getattr(location, 'x', 0.0),
            "y": getattr(location, 'y', 0.0),
            "tags": getattr(location, 'tags', [])
        }
    
    # Add weight/volume to player data
    player_with_capacity = dict(player)
    player_with_capacity['current_weight'] = round(total_weight, 2)
    player_with_capacity['max_weight'] = round(max_weight, 2)
    player_with_capacity['current_volume'] = round(total_volume, 2)
    player_with_capacity['max_volume'] = round(max_volume, 2)
    
    # Calculate movement cooldown
    import time
    current_time = time.time()
    last_movement = player.get('last_movement_time', 0)
    time_since_movement = current_time - last_movement
    movement_cooldown = max(0, min(5, 5 - time_since_movement))
    player_with_capacity['movement_cooldown'] = int(movement_cooldown)
    
    return {
        "player": player_with_capacity,
        "location": location_dict,
        "inventory": inventory,
        "equipment": equipment,
        "combat": combat,
        "pvp_combat": pvp_combat,
        "is_pvp": pvp_combat is not None,
        "dropped_items": dropped_items
    }


@router.get("/api/game/profile")
async def get_player_profile(current_user: dict = Depends(get_current_user)):
    """Get player profile information"""
    player_id = current_user['id']
    
    player = await db.get_player_by_id(player_id)
    if not player:
        raise HTTPException(status_code=404, detail="Player not found")
        
    # Get player status effects
    status_effects = await db.get_player_effects(player_id)
    player['status_effects'] = status_effects
    
    # Get capacity metrics (weight/volume) using the helper function
    # We don't need the inventory array itself, just the capacity calculations
    _, total_weight, total_volume, max_weight, max_volume = await _get_enriched_inventory(player_id)
    
    # Add weight/volume to player data
    player_with_capacity = dict(player)
    player_with_capacity['current_weight'] = round(total_weight, 2)
    player_with_capacity['max_weight'] = round(max_weight, 2)
    player_with_capacity['current_volume'] = round(total_volume, 2)
    player_with_capacity['max_volume'] = round(max_volume, 2)
    
    # Calculate movement cooldown
    import time
    current_time = time.time()
    last_movement = player.get('last_movement_time', 0)
    time_since_movement = current_time - last_movement
    movement_cooldown = max(0, min(5, 5 - time_since_movement))
    player_with_capacity['movement_cooldown'] = round(movement_cooldown, 1)
    
    return {
        "player": player_with_capacity
    }


@router.post("/api/game/spend_point")
async def spend_stat_point(
    stat: str,
    current_user: dict = Depends(get_current_user)
):
    """Spend a stat point on a specific attribute"""
    player = current_user  # current_user is already the character dict
    
    if not player:
        raise HTTPException(status_code=404, detail="Player not found")
    
    if player['unspent_points'] < 1:
        raise HTTPException(status_code=400, detail="No unspent points available")
    
    # Valid stats
    valid_stats = ['strength', 'agility', 'endurance', 'intellect']
    if stat not in valid_stats:
        raise HTTPException(status_code=400, detail=f"Invalid stat. Must be one of: {', '.join(valid_stats)}")
    
    # Check stat cap
    if player[stat] >= STAT_CAP:
        raise HTTPException(status_code=400, detail=f"{stat.capitalize()} is already at maximum ({STAT_CAP})")
    
    # Update the stat and decrease unspent points
    update_data = {
        stat: player[stat] + 1,
        'unspent_points': player['unspent_points'] - 1
    }
    
    # Endurance increases max HP and max stamina
    if stat == 'endurance':
        update_data['max_hp'] = player['max_hp'] + 5
        update_data['hp'] = min(player['hp'] + 5, update_data['max_hp'])  # Also heal by 5
        update_data['max_stamina'] = player['max_stamina'] + 2
        update_data['stamina'] = min(player['stamina'] + 2, update_data['max_stamina'])  # Also restore by 2
    
    await db.update_character(current_user['id'], **update_data)
    
    # Invalidate cached derived stats
    await invalidate_stats_cache(current_user['id'], redis_manager)
    
    return {
        "success": True,
        "message": f"Increased {stat} by 1!",
        "new_value": player[stat] + 1,
        "remaining_points": player['unspent_points'] - 1
    }


@router.get("/api/game/location")
async def get_current_location(request: Request, current_user: dict = Depends(get_current_user)):
    """Get current location information"""
    # Extract locale from Accept-Language header
    locale = request.headers.get('Accept-Language', 'en')
    
    location_id = current_user['location_id']
    location = LOCATIONS.get(location_id)
    
    if not location:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail=f"Location {location_id} not found"
        )
    
    # Get dropped items at location
    dropped_items = await db.get_dropped_items(location_id)
    
    # Get wandering enemies at location
    wandering_enemies = await db.get_wandering_enemies_in_location(location_id)
    
    # Format interactables for response with cooldown info
    interactables_data = []
    for interactable in location.interactables:
        # Check if locked
        if getattr(interactable, 'locked', False):
            continue

        actions_data = []
        for action in interactable.actions:
            # Check cooldown status for this specific action
            cooldown_expiry = await db.get_interactable_cooldown(interactable.id, action.id)
            import time
            is_on_cooldown = False
            remaining_cooldown = 0
            
            if cooldown_expiry:
                current_time = time.time()
                if cooldown_expiry > current_time:
                    is_on_cooldown = True
                    remaining_cooldown = int(cooldown_expiry - current_time)
            
            actions_data.append({
                "id": action.id,
                "name": action.label,
                "stamina_cost": action.stamina_cost,
                "description": get_game_message('costs_stamina', locale, cost=action.stamina_cost),
                "on_cooldown": is_on_cooldown,
                "cooldown_remaining": remaining_cooldown
            })
        
        interactables_data.append({
            "instance_id": interactable.id,
            "name": interactable.name,
            "image_path": interactable.image_path,
            "actions": actions_data
        })
    
    # Fix image URL - image_path already contains the full path from images/
    image_url = f"/{location.image_path}" if location.image_path else "/images/locations/default.webp"
    
    # Calculate player's current weight for stamina cost adjustment
    player = current_user  # current_user is already the character dict
    
    if not player:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="No character selected. Please select a character first."
        )
    
    # Use helper function to calculate capacity
    inventory = await db.get_inventory(current_user['id'])
    total_weight, max_weight, total_volume, max_volume = await calculate_player_capacity(inventory, ITEMS_MANAGER)
    
    # Format directions with stamina costs (calculated from distance, weight, agility)
    directions_with_stamina = []
    player_agility = player.get('agility', 5)
    
    for direction in location.exits.keys():
        destination_id = location.exits[direction]
        destination_loc = LOCATIONS.get(destination_id)
        
        # Check if destination is locked
        if destination_loc and getattr(destination_loc, 'locked', False):
            continue 
        
        if destination_loc:
            # Calculate real distance using coordinates
            distance = calculate_distance(
                location.x, location.y,
                destination_loc.x, destination_loc.y
            )
            # Calculate stamina cost based on distance, weight, volume, capacity, and agility
            stamina_cost = calculate_stamina_cost(distance, total_weight, player_agility, max_weight, total_volume, max_volume)
            destination_name = destination_loc.name
        else:
            # Fallback if destination not found
            distance = 500  # Default 500m
            stamina_cost = calculate_stamina_cost(distance, total_weight, player_agility)
            destination_name = destination_id
        
        directions_with_stamina.append({
            "direction": direction,
            "stamina_cost": stamina_cost,
            "distance": int(distance),  # Round to integer meters
            "destination": destination_id,
            "destination_name": destination_name
        })
    
    # Format NPCs (wandering enemies + static NPCs from JSON)
    npcs_data = []
    
    # Add wandering enemies from database
    import sys
    sys.path.insert(0, '/app')
    from data.npcs import NPCS
    
    for enemy in wandering_enemies:
        npc_def = NPCS.get(enemy['npc_id'])
        npcs_data.append({
            "id": enemy['id'],
            "npc_id": enemy['npc_id'],
            "name": npc_def.name if npc_def else enemy['npc_id'].replace('_', ' ').title(),
            "type": "enemy",
            "level": enemy.get('level', 1),
            "is_wandering": True,
            "image_path": npc_def.image_path if npc_def else None
        })
    
    # Add static NPCs from location JSON (if any)
    for npc in location.npcs:
        if isinstance(npc, dict):
            npcs_data.append({
                "id": npc.get('id', npc.get('name', 'unknown')),
                "name": npc.get('name', 'Unknown NPC'),
                "type": npc.get('type', 'npc'),
                "level": npc.get('level'),
                "is_wandering": False,
                "image_path": npc.get('image_path'),
                "is_static": npc.get('is_static', False),
                "trade": npc.get('trade')
            })
    
        else:
            npcs_data.append({
                "id": npc,
                "name": npc,
                "type": "npc",
                "is_wandering": False
            })
    
    # Debug logging for missing NPCs - UNCONDITIONAL
    logger.info(f"📍 Requested Location: {location.id}, NPCs: {[n.get('id') for n in npcs_data]}")
    
    # Enrich dropped items with metadata - DON'T consolidate unique items!
    items_dict = {}
    for item in dropped_items:
        item_def = ITEMS_MANAGER.get_item(item['item_id'])
        if item_def:
            # Get unique item data if this is a unique item
            durability = None
            max_durability = None
            tier = None
            if item.get('unique_item_id'):
                unique_item = await db.get_unique_item(item['unique_item_id'])
                if unique_item:
                    durability = unique_item.get('durability')
                    max_durability = unique_item.get('max_durability')
                    tier = unique_item.get('tier')
            
            # Create a unique key for unique items to prevent stacking
            if item.get('unique_item_id'):
                dict_key = f"{item['item_id']}_{item['unique_item_id']}"
            else:
                dict_key = item['item_id']
            
            if dict_key not in items_dict:
                items_dict[dict_key] = {
                    "id": item['id'],  # Use first ID for pickup
                    "item_id": item['item_id'],
                    "name": item_def.name,
                    "description": item_def.description,
                    "quantity": item['quantity'],
                    "emoji": item_def.emoji,
                    "image_path": item_def.image_path,
                    "weight": item_def.weight,
                    "volume": item_def.volume,
                    "durability": durability,
                    "max_durability": max_durability,
                    "tier": tier,
                    "hp_restore": item_def.effects.get('hp_restore') if item_def.effects else None,
                    "stamina_restore": item_def.effects.get('stamina_restore') if item_def.effects else None,
                    "damage_min": item_def.stats.get('damage_min') if item_def.stats else None,
                    "damage_max": item_def.stats.get('damage_max') if item_def.stats else None
                }
            else:
                # Only stack if it's not a unique item (stackable items only)
                if not item.get('unique_item_id'):
                    items_dict[dict_key]['quantity'] += item['quantity']
    
    items_data = list(items_dict.values())
    
    # Get other players in the same location (characters from all accounts)
    other_players = []
    try:
        # Use Redis for player registry if available (includes disconnected players)
        if redis_manager:
            player_ids = await redis_manager.get_players_in_location(location_id)
            
            for pid in player_ids:
                if pid == current_user['id']:
                    continue
                
                # Get player session from Redis
                session = await redis_manager.get_player_session(pid)
                if session:
                    # Check if player is connected
                    is_connected = session.get('websocket_connected') == 'true'
                    
                    # Check disconnect duration
                    disconnect_duration = None
                    if not is_connected:
                        disconnect_duration = await redis_manager.get_disconnect_duration(pid)
                    
                    # Get player data from DB for combat checks
                    char = await db.get_player_by_id(pid)
                    if not char:
                        continue
                    
                    # Don't show dead players
                    if char.get('is_dead', False):
                        continue
                    
                    # Check if character is in any combat (PvE or PvP)
                    in_pve_combat = await db.get_active_combat(pid)
                    in_pvp_combat = await db.get_pvp_combat_by_player(pid)
                    
                    # Don't show characters who are in combat
                    if in_pve_combat or in_pvp_combat:
                        continue
                    
                    # Check if PvP is possible with this character
                    level_diff = abs(player['level'] - int(session.get('level', 0)))
                    can_pvp = location.danger_level >= 3 and level_diff <= 3
                    
                    other_players.append({
                        "id": pid,
                        "name": session.get('username'),
                        "level": int(session.get('level', 0)),
                        "username": session.get('username'),
                        "can_pvp": can_pvp,
                        "level_diff": level_diff,
                        "is_connected": is_connected,
                        "vulnerable": not is_connected and location.danger_level >= 3  # Disconnected in dangerous zone
                    })
        else:
            # Fallback: Query database directly (single worker mode)
            async with db.engine.begin() as conn:
                stmt = db.select(db.characters).where(
                    db.and_(
                        db.characters.c.location_id == location_id,
                        db.characters.c.id != current_user['id'],
                        db.characters.c.is_dead == False  # Don't show dead players
                    )
                )
                result = await conn.execute(stmt)
                characters_rows = result.fetchall()
                
                for char_row in characters_rows:
                    # Check if character is in any combat (PvE or PvP)
                    in_pve_combat = await db.get_active_combat(char_row.id)
                    in_pvp_combat = await db.get_pvp_combat_by_player(char_row.id)
                    
                    if in_pve_combat or in_pvp_combat:
                        continue
                    
                    # Check if PvP is possible with this character
                    level_diff = abs(player['level'] - char_row.level)
                    can_pvp = location.danger_level >= 3 and level_diff <= 3
                    
                    other_players.append({
                        "id": char_row.id,
                        "name": char_row.name,
                        "level": char_row.level,
                        "username": char_row.name,
                        "can_pvp": can_pvp,
                        "level_diff": level_diff,
                        "is_connected": True,  # Assume connected in fallback mode
                        "vulnerable": False
                    })
    except Exception as e:
        print(f"Error fetching other characters: {e}")
    
    # Get corpses at location
    npc_corpses = await db.get_npc_corpses_in_location(location_id)
    player_corpses = await db.get_player_corpses_in_location(location_id)
    
    # Format corpses for response
    corpses_data = []
    import json
    import sys
    sys.path.insert(0, '/app')
    from data.npcs import NPCS
    
    for corpse in npc_corpses:
        loot = json.loads(corpse['loot_remaining']) if corpse['loot_remaining'] else []
        npc_def = NPCS.get(corpse['npc_id'])
        corpses_data.append({
            "id": f"npc_{corpse['id']}",
            "type": "npc",
            "name": f"{get_locale_string(npc_def.name, locale) if npc_def else corpse['npc_id']} Corpse",
            "emoji": "💀",
            "loot_count": len(loot),
            "timestamp": corpse['death_timestamp'],
            "image_path": npc_def.image_path if npc_def else None
        })
    
    for corpse in player_corpses:
        items = json.loads(corpse['items']) if corpse['items'] else []
        corpses_data.append({
            "id": f"player_{corpse['id']}",
            "type": "player",
            "name": f"{corpse['player_name']}'s Corpse",
            "emoji": "⚰️",
            "loot_count": len(items),
            "timestamp": corpse['death_timestamp']
        })
    
    return {
        "id": location.id,
        "name": location.name,
        "description": location.description,
        "image_url": image_url,
        "directions": list(location.exits.keys()),  # Keep for backwards compatibility
        "directions_detailed": directions_with_stamina,  # New detailed format
        "danger_level": location.danger_level,
        "tags": location.tags if hasattr(location, 'tags') else [],  # Include location tags
        "npcs": npcs_data,
        "items": items_data,
        "interactables": interactables_data,
        "other_players": other_players,
        "corpses": corpses_data
    }


@router.post("/api/game/move")
async def move(
    move_req: MoveRequest,
    request: Request,
    current_user: dict = Depends(get_current_user)
):
    """Move player in a direction"""
    import time
    
    # Check if player is in PvP combat and hasn't acknowledged
    pvp_combat = await db.get_pvp_combat_by_player(current_user['id'])
    if pvp_combat:
        is_attacker = pvp_combat['attacker_character_id'] == current_user['id']
        acknowledged = pvp_combat.get('attacker_acknowledged', False) if is_attacker else pvp_combat.get('defender_acknowledged', False)
        
        # Check if combat ended - need to get actual player HP
        attacker = await db.get_player_by_id(pvp_combat['attacker_character_id'])
        defender = await db.get_player_by_id(pvp_combat['defender_character_id'])
        
        # Only block if combat is still active (not fled, not defeated) and player hasn't acknowledged
        combat_ended = pvp_combat.get('attacker_fled', False) or pvp_combat.get('defender_fled', False) or \
                      attacker['hp'] <= 0 or defender['hp'] <= 0
        
        if not acknowledged and not combat_ended:
            raise HTTPException(
                status_code=status.HTTP_400_BAD_REQUEST,
                detail="Cannot move while in PvP combat!"
            )
    
    # Check movement cooldown (5 seconds)
    player = current_user  # current_user is already the character dict
    current_time = time.time()
    last_movement = player.get('last_movement_time', 0)
    cooldown_remaining = max(0, 5 - (current_time - last_movement))
    
    if cooldown_remaining > 0:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=get_game_message('move_cooldown', locale, seconds=int(cooldown_remaining))
        )
    
    # Extract locale from Accept-Language header
    locale = request.headers.get('Accept-Language', 'en')
    
    success, message, new_location_id, stamina_cost, distance = await game_logic.move_player(
        current_user['id'],
        move_req.direction,
        LOCATIONS,
        locale
    )
    
    if not success:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=message
        )
    
    # Update last movement time
    await db.update_player(current_user['id'], last_movement_time=current_time)
    
    # Update Redis cache: Move player between locations
    if redis_manager:
        await redis_manager.move_player_between_locations(
            current_user['id'],
            player['location_id'],
            new_location_id
        )
        
        # Update player session with new location
        await redis_manager.update_player_session_field(current_user['id'], 'location_id', new_location_id)
        await redis_manager.update_player_session_field(current_user['id'], 'stamina', player['stamina'] - stamina_cost)
    
    # Track movement statistics - use actual distance in meters
    await db.update_player_statistics(current_user['id'], distance_walked=distance, increment=True)
    
    # Check for encounter upon arrival (if danger level > 1)
    import random
    import sys
    sys.path.insert(0, '/app')
    from data.npcs import get_random_npc_for_location, LOCATION_DANGER, NPCS
    
    new_location = LOCATIONS.get(new_location_id)
    encounter_triggered = False
    enemy_id = None
    combat_data = None
    
    if new_location and new_location.danger_level > 1:
        # Get encounter rate from danger config
        danger_data = LOCATION_DANGER.get(new_location_id)
        if danger_data:
            _, encounter_rate, _ = danger_data
            # Roll for encounter
            if random.random() < encounter_rate:
                # Get a random enemy for this location
                enemy_id = get_random_npc_for_location(new_location_id)
                if enemy_id:
                    # Check if player is already in combat
                    existing_combat = await db.get_active_combat(current_user['id'])
                    if not existing_combat:
                        # Get NPC definition
                        npc_def = NPCS.get(enemy_id)
                        if npc_def:
                            # Randomize HP
                            npc_hp = random.randint(npc_def.hp_min, npc_def.hp_max)
                            
                            # Create combat directly
                            # Generate initial intent
                            # Generate initial intent
                            # game_logic is already imported at module level
                            npc_def = NPCS.get(enemy_id)
                            initial_intent = game_logic.generate_npc_intent(npc_def, {
                                'npc_hp': npc_hp, 
                                'npc_max_hp': npc_hp
                            })
                            
                            combat = await db.create_combat(
                                player_id=current_user['id'],
                                npc_id=enemy_id,
                                npc_hp=npc_hp,
                                npc_max_hp=npc_hp,
                                location_id=new_location_id,
                                from_wandering=False,  # This is an encounter, not wandering
                                npc_intent=initial_intent['type']
                            )
                            
                            # Track combat initiation
                            await db.update_player_statistics(current_user['id'], combats_initiated=1, increment=True)
                            
                            encounter_triggered = True
                            combat_data = {
                                "npc_id": enemy_id,
                                "npc_name": npc_def.name,
                                "npc_hp": npc_hp,
                                "npc_max_hp": npc_hp,
                                "npc_image": npc_def.image_path,
                                "turn": "player",
                                "round": 1,
                                "npc_intent": initial_intent['type']
                            }
    
    response = {
        "success": True,
        "message": message,
        "new_location_id": new_location_id,
        "new_location_name": new_location.name if new_location else "Unknown" # Add location name for frontend
    }
    
    # Add encounter info if triggered
    if encounter_triggered:
        response["encounter"] = {
            "triggered": True,
            "enemy_id": enemy_id,
            "message": get_game_message('enemy_ambush', locale),
            "combat": combat_data
        }
    
    # Broadcast movement to WebSocket clients
    # Notify old location that player left
    await manager.send_to_location(
        player['location_id'],
        {
            "type": "location_update",
            "data": {
                "message": get_game_message('player_left', locale, player_name=player['name']),
                "action": "player_left",
                "player_id": current_user['id'],
                "player_name": player['name']
            },
            "timestamp": datetime.utcnow().isoformat()
        },
        exclude_player_id=current_user['id']
    )
    
    # Notify new location that player arrived
    await manager.send_to_location(
        new_location_id,
        {
            "type": "location_update",
            "data": {
                "message": get_game_message('player_arrived', locale, player_name=player['name']),
                "action": "player_arrived",
                "player_id": current_user['id'],
                "player_name": player['name'],
                "player_level": player['level'],
                "can_pvp": new_location.danger_level >= 3  # Full player data for UI update
            },
            "timestamp": datetime.utcnow().isoformat()
        },
        exclude_player_id=current_user['id']
    )
    
    # Send state update to the moving player
    await manager.send_personal_message(current_user['id'], {
        "type": "state_update",
        "data": {
            "player": {
                "stamina": player['stamina'] - stamina_cost,
                "location_id": new_location_id
            },
            "location": {
                "id": new_location.id,
                "name": new_location.name
            } if new_location else None,
            "encounter": response.get("encounter")
        },
        "timestamp": datetime.utcnow().isoformat()
    })
    
    return response


@router.post("/api/game/inspect")
async def inspect(request: Request, current_user: dict = Depends(get_current_user)):
    """Inspect the current area"""
    # Extract locale from Accept-Language header
    locale = request.headers.get('Accept-Language', 'en')
    
    location_id = current_user['location_id']
    location = LOCATIONS.get(location_id)
    
    if not location:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Location not found"
        )
    
    # Get dropped items
    dropped_items = await db.get_dropped_items(location_id)
    
    message = await game_logic.inspect_area(
        current_user['id'],
        location,
        {},  # interactables_data - not needed with new structure
        locale
    )
    
    return {
        "success": True,
        "message": message
    }


@router.post("/api/game/interact")
async def interact(
    interact_req: InteractRequest,
    request: Request,
    current_user: dict = Depends(get_current_user)
):
    """Interact with an object in the game world"""
    # Extract locale from Accept-Language header
    locale = request.headers.get('Accept-Language', 'en')
    
    # Check if player is in combat
    combat = await db.get_active_combat(current_user['id'])
    if combat:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=get_game_message('interact_in_combat', locale)
        )
    
    location_id = current_user['location_id']
    location = LOCATIONS.get(location_id)
    
    if not location:
        raise HTTPException(
            status_code=status.HTTP_404_NOT_FOUND,
            detail="Location not found"
        )
    
    result = await game_logic.interact_with_object(
        current_user['id'],
        interact_req.interactable_id,
        interact_req.action_id,
        location,
        ITEMS_MANAGER,
        locale
    )
    
    if not result['success']:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=result['message']
        )
    
    # Broadcast interactable cooldown to all players in location
    from datetime import datetime
    
    # Find the interactable name and action name
    interactable = None
    action_name = None
    for obj in location.interactables:
        if obj.id == interact_req.interactable_id:
            interactable = obj
            for act in obj.actions:
                if act.id == interact_req.action_id:
                    action_name = act.label
                    break
            break
    
    interactable_name = interactable.name if interactable else "Object"
    action_display = action_name if action_name else interact_req.action_id
    
    # Get the actual cooldown expiry from database and calculate remaining time
    cooldown_expiry = await db.get_interactable_cooldown(
        interact_req.interactable_id,
        interact_req.action_id
    )
    
    # Calculate remaining cooldown in seconds
    import time as time_module
    current_time = time_module.time()
    cooldown_remaining = 0
    if cooldown_expiry and cooldown_expiry > current_time:
        cooldown_remaining = int(cooldown_expiry - current_time)
    
    # Only broadcast if there are players in the location
    if manager.has_players_in_location(location_id):
        await manager.send_to_location(
            location_id=location_id,
            message={
                "type": "interactable_cooldown",
                "data": {
                    "instance_id": interact_req.interactable_id,
                    "action_id": interact_req.action_id,
                    "cooldown_remaining": cooldown_remaining,
                    "message": get_game_message('interactable_cooldown', locale, user=current_user['name'], interactable=get_locale_string(interactable_name, locale), action=get_locale_string(action_display, locale)),
                },
                "timestamp": datetime.utcnow().isoformat()
            }
        )
    
    return result




@router.post("/api/game/use_item")
async def use_item(
    use_req: UseItemRequest,
    request: Request,
    current_user: dict = Depends(get_current_user)
):
    """Use an item from inventory"""
    import random
    import sys
    sys.path.insert(0, '/app')
    from data.npcs import NPCS
    
    # Check if in combat
    combat = await db.get_active_combat(current_user['id'])
    in_combat = combat is not None
    
    # Extract locale from Accept-Language header
    locale = request.headers.get('Accept-Language', 'en')
    
    result = await game_logic.use_item(
        current_user['id'],
        use_req.item_id,
        ITEMS_MANAGER,
        locale
    )
    
    if not result['success']:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=result['message']
        )
    
    # If in combat, enemy gets a turn
    if in_combat and combat['turn'] == 'player':
        player = current_user  # current_user is already the character dict
        npc_def = NPCS.get(combat['npc_id'])
        
        # Enemy attacks
        npc_damage = random.randint(npc_def.damage_min, npc_def.damage_max)
        if combat['npc_hp'] / combat['npc_max_hp'] < 0.3:
            npc_damage = int(npc_damage * 1.5)
        
        new_player_hp = max(0, player['hp'] - npc_damage)
        combat_message = get_game_message('combat_enemy_attack', locale, name=npc_def.name, damage=npc_damage)
        
        if new_player_hp <= 0:
            combat_message += get_game_message('combat_defeated', locale)
            await db.update_player(current_user['id'], hp=0, is_dead=True)
            await db.end_combat(current_user['id'])
            result['combat_over'] = True
            result['player_won'] = False
            
            # Create corpse with player's inventory
            import json
            import time as time_module
            try:
                inventory = await db.get_inventory(current_user['id'])
                inventory_items = []
                for inv_item in inventory:
                    item_def = ITEMS_MANAGER.get_item(inv_item['item_id'])
                    inventory_items.append({
                        'item_id': inv_item['item_id'],
                        'name': item_def.name if item_def else inv_item['item_id'],
                        'emoji': item_def.emoji if (item_def and hasattr(item_def, 'emoji')) else '📦',
                        'quantity': inv_item['quantity'],
                        'durability': inv_item.get('durability'),
                        'max_durability': inv_item.get('max_durability'),
                        'tier': inv_item.get('tier')
                    })
                
                # Only create corpse if player has items
                corpse_data = None
                if inventory_items:
                    # Store minimal data in database
                    db_items = json.dumps([{'item_id': i['item_id'], 'quantity': i['quantity']} for i in inventory])
                    
                    logger.info(f"Creating player corpse for {player['name']} at {player['location_id']} with {len(inventory_items)} items")
                    
                    corpse_id = await db.create_player_corpse(
                        player_name=player['name'],
                        location_id=player['location_id'],
                        items=db_items
                    )
                    
                    logger.info(f"Successfully created player corpse: ID={corpse_id}, player={player['name']}, location={player['location_id']}, items_count={len(inventory_items)}")
                    
                    # Clear player's inventory (items are now in corpse)
                    await db.clear_inventory(current_user['id'])
                    
                    # Build corpse data for broadcast
                    corpse_data = {
                        "id": f"player_{corpse_id}",
                        "type": "player",
                        "name": f"{player['name']}'s Corpse",
                        "emoji": "⚰️",
                        "player_name": player['name'],
                        "loot_count": len(inventory_items),
                        "items": inventory_items,  # Full item list for UI
                        "timestamp": time_module.time()
                    }
                else:
                    logger.info(f"Player {player['name']} died (use_item combat) with no items, skipping corpse creation")
                
                # Broadcast to location that player died (and corpse if created)
                logger.info(f"Broadcasting player_died to location {player['location_id']} for player {player['name']}")
                broadcast_data = {
                    "message": get_game_message('player_defeated_broadcast', locale, player_name=player['name']),
                    "action": "player_died",
                    "player_id": player['id']
                }
                if corpse_data:
                    broadcast_data["corpse"] = corpse_data
                
                await manager.send_to_location(
                    location_id=player['location_id'],
                    message={
                        "type": "location_update",
                        "data": broadcast_data,
                        "timestamp": datetime.utcnow().isoformat()
                    },
                    exclude_player_id=current_user['id']
                )
            except Exception as e:
                logger.error(f"Error creating player corpse for {player['name']}: {e}", exc_info=True)
        else:
            await db.update_player(current_user['id'], hp=new_player_hp)
        
        result['message'] += combat_message
        result['in_combat'] = True
        result['combat_over'] = result.get('combat_over', False)
    
    return result


@router.post("/api/game/pickup")
async def pickup(
    pickup_req: PickupItemRequest,
    request: Request,
    current_user: dict = Depends(get_current_user)
):
    """Pick up an item from the ground"""
    # Extract locale from Accept-Language header
    locale = request.headers.get('Accept-Language', 'en')
    
    # Get item details for broadcast BEFORE picking it up (it will be removed from DB)
    # pickup_req.item_id is the dropped_item database ID, not the item_id string
    dropped_item = await db.get_dropped_item(pickup_req.item_id)
    if dropped_item:
        item_def = ITEMS_MANAGER.get_item(dropped_item['item_id'])
        item_name = get_locale_string(item_def.name, locale) if item_def else dropped_item['item_id']
    else:
        item_name = "item"
    
    result = await game_logic.pickup_item(
        current_user['id'],
        pickup_req.item_id,
        current_user['location_id'],
        pickup_req.quantity,
        ITEMS_MANAGER,
        locale
    )
    
    if not result['success']:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=result['message']
        )
    
    # Track pickup statistics
    quantity = pickup_req.quantity if pickup_req.quantity else 1
    await db.update_player_statistics(current_user['id'], items_collected=quantity, increment=True)
    
    # Broadcast pickup to other players in location
    player = current_user  # current_user is already the character dict
    await manager.send_to_location(
        player['location_id'],
        {
            "type": "location_update",
            "data": {
                "message": f"{player['name']} {get_game_message('picked_up', locale).lower()} {quantity}x {item_name}",
                "action": "item_picked_up"
            },
            "timestamp": datetime.utcnow().isoformat()
        },
        exclude_player_id=current_user['id']
    )
    
    # Send state update to the player
    await manager.send_personal_message(current_user['id'], {
        "type": "inventory_update",
        "timestamp": datetime.utcnow().isoformat()
    })
    
    return result


@router.get("/api/game/inventory")
async def get_inventory(current_user: dict = Depends(get_current_user)):
    """Get player inventory"""
    inventory = await db.get_inventory(current_user['id'])
    
    # Enrich with item data
    inventory_items = []
    for inv_item in inventory:
        item = ITEMS_MANAGER.get_item(inv_item['item_id'])
        if item:
            item_data = {
                "id": inv_item['id'],
                "item_id": item.id,
                "name": item.name,
                "description": item.description,
                "type": item.type,
                "quantity": inv_item['quantity'],
                "is_equipped": inv_item['is_equipped'],
                "equippable": item.equippable,
                "consumable": item.consumable,
                "image_path": item.image_path,
                "emoji": item.emoji if hasattr(item, 'emoji') else None,
                "weight": item.weight if hasattr(item, 'weight') else 0,
                "volume": item.volume if hasattr(item, 'volume') else 0,
                "uncraftable": getattr(item, 'uncraftable', False),
                "inventory_id": inv_item['id'],
                "unique_item_id": inv_item.get('unique_item_id')
            }
            # Add combat/consumable stats if they exist
            if hasattr(item, 'hp_restore'):
                item_data["hp_restore"] = item.hp_restore
            if hasattr(item, 'stamina_restore'):
                item_data["stamina_restore"] = item.stamina_restore
            if hasattr(item, 'damage_min'):
                item_data["damage_min"] = item.damage_min
            if hasattr(item, 'damage_max'):
                item_data["damage_max"] = item.damage_max
            
            # Add tier if unique item
            if inv_item.get('unique_item_id'):
                unique_item = await db.get_unique_item(inv_item['unique_item_id'])
                if unique_item:
                    item_data["tier"] = unique_item.get('tier', 1)
                    item_data["durability"] = unique_item.get('durability', 0)
                    item_data["max_durability"] = unique_item.get('max_durability', 100)
            
            # Add uncraft data if uncraftable
            if getattr(item, 'uncraftable', False):
                uncraft_yield = getattr(item, 'uncraft_yield', [])
                uncraft_tools = getattr(item, 'uncraft_tools', [])
                
                # Format materials
                yield_materials = []
                for mat in uncraft_yield:
                    mat_def = ITEMS_MANAGER.get_item(mat['item_id'])
                    yield_materials.append({
                        'item_id': mat['item_id'],
                        'name': mat_def.name if mat_def else mat['item_id'],
                        'emoji': mat_def.emoji if mat_def else '📦',
                        'quantity': mat['quantity']
                    })
                
                # Check tools availability
                tools_info = []
                can_uncraft = True
                for tool_req in uncraft_tools:
                    tool_id = tool_req['item_id']
                    durability_cost = tool_req['durability_cost']
                    tool_def = ITEMS_MANAGER.get_item(tool_id)
                    
                    # Check if player has this tool
                    tool_found = False
                    tool_durability = 0
                    for check_item in inventory:
                        if check_item['item_id'] == tool_id and check_item.get('unique_item_id'):
                            unique = await db.get_unique_item(check_item['unique_item_id'])
                            if unique and unique.get('durability', 0) >= durability_cost:
                                tool_found = True
                                tool_durability = unique.get('durability', 0)
                                break
                    
                    tools_info.append({
                        'item_id': tool_id,
                        'name': tool_def.name if tool_def else tool_id,
                        'emoji': tool_def.emoji if tool_def else '🔧',
                        'durability_cost': durability_cost,
                        'has_tool': tool_found,
                        'tool_durability': tool_durability
                    })
                    if not tool_found:
                        can_uncraft = False
                
                item_data["uncraft_yield"] = yield_materials
                item_data["uncraft_loss_chance"] = getattr(item, 'uncraft_loss_chance', 0.3)
                item_data["uncraft_tools"] = tools_info
                item_data["can_uncraft"] = can_uncraft
            
            inventory_items.append(item_data)
    
    return {"items": inventory_items}


@router.post("/api/game/item/drop")
async def drop_item(
    drop_req: dict,
    request: Request,
    current_user: dict = Depends(get_current_user)
):
    """Drop an item from inventory"""
    player_id = current_user['id']
    item_id = drop_req.get('item_id')  # This is the item_id string like "energy_bar"
    quantity = drop_req.get('quantity', 1)
    
    # Extract locale from Accept-Language header
    locale = request.headers.get('Accept-Language', 'en')
    
    # Get player to know their location
    player = await db.get_player_by_id(player_id)
    if not player:
        raise HTTPException(status_code=404, detail="Player not found")
    
    # Get inventory item by item_id (string), not database id
    inventory = await db.get_inventory(player_id)
    inv_item = None
    
    # If inventory_id is provided, use it to find precise item
    inventory_id = drop_req.get('inventory_id')
    
    if inventory_id:
        for item in inventory:
            if item['id'] == inventory_id:
                inv_item = item
                break
    else:
        # Fallback to legacy behavior (first matching item_id)
        for item in inventory:
            if item['item_id'] == item_id:
                inv_item = item
                break
    
    if not inv_item:
        raise HTTPException(status_code=404, detail="Item not found in inventory")
    
    if inv_item['quantity'] < quantity:
        raise HTTPException(status_code=400, detail="Not enough items to drop")
    
    # For unique items, we need to handle each one individually
    if inv_item.get('unique_item_id'):
        # This is a unique item - drop it and remove from inventory by row ID
        await db.add_dropped_item(
            player['location_id'], 
            inv_item['item_id'], 
            1,
            unique_item_id=inv_item['unique_item_id']
        )
        # Remove this specific inventory row (not by item_id, by row id)
        await db.remove_inventory_row(inv_item['id'])
    else:
        # Stackable item - drop the quantity requested
        await db.add_dropped_item(
            player['location_id'], 
            inv_item['item_id'], 
            quantity,
            unique_item_id=None
        )
        # Remove from inventory (handles quantity reduction automatically)
        await db.remove_item_from_inventory(player_id, inv_item['item_id'], quantity)
    
    # Track drop statistics
    await db.update_player_statistics(player_id, items_dropped=quantity, increment=True)
    
    # Invalidate inventory cache
    if redis_manager:
        await redis_manager.invalidate_inventory(player_id)
    
    # Get item details for broadcast
    item_def = ITEMS_MANAGER.get_item(inv_item['item_id'])
    
    # Broadcast to location that item was dropped
    await manager.send_to_location(
        location_id=player['location_id'],
        message={
            "type": "location_update",
            "data": {
                "message": get_game_message('dropped_item_success', locale, emoji=item_def.emoji, name=get_locale_string(item_def.name, locale), qty=quantity).replace('You', player['name']).replace('Has tirado', f"{player['name']} ha tirado"),
                "action": "item_dropped"
            },
            "timestamp": datetime.utcnow().isoformat()
        },
        exclude_player_id=player_id
    )
    
    return {
        "success": True,
        "message": get_game_message('dropped_item_success', locale, emoji=item_def.emoji, name=get_locale_string(item_def.name, locale), qty=quantity)
    }


@router.get("/api/game/character-sheet")
async def get_character_sheet(current_user: dict = Depends(get_current_user)):
    """Get the full character sheet with base stats, derived stats, skills, and perks."""
    from ..services.stats import calculate_derived_stats
    from ..services.skills import skills_manager, perks_manager, get_total_perk_points
    
    player = current_user
    character_id = player['id']
    
    # Get derived stats
    derived = await calculate_derived_stats(character_id, redis_manager)
    
    # Get available skills
    available_skills = skills_manager.get_available_skills(player)
    
    # Get owned perks
    owned_perks_rows = await db.get_character_perks(character_id)
    owned_perk_ids = [row['perk_id'] for row in owned_perks_rows]
    
    # Get all perks with availability
    all_perks = perks_manager.get_available_perks(player, owned_perk_ids)
    
    # Calculate perk points
    total_perk_points = get_total_perk_points(player['level'])
    used_perk_points = len(owned_perk_ids)
    available_perk_points = total_perk_points - used_perk_points
    
    return {
        "base_stats": {
            "strength": player['strength'],
            "agility": player['agility'],
            "endurance": player['endurance'],
            "intellect": player['intellect'],
            "unspent_points": player['unspent_points'],
            "stat_cap": STAT_CAP,
        },
        "derived_stats": derived,
        "skills": available_skills,
        "perks": {
            "available_points": available_perk_points,
            "total_points": total_perk_points,
            "used_points": used_perk_points,
            "all_perks": all_perks,
        },
        "character": {
            "name": player['name'],
            "level": player['level'],
            "xp": player['xp'],
            "hp": player['hp'],
            "max_hp": player['max_hp'],
            "stamina": player['stamina'],
            "max_stamina": player['max_stamina'],
            "avatar_data": player.get('avatar_data'),
        }
    }


@router.post("/api/game/select_perk")
async def select_perk(
    perk_id: str,
    current_user: dict = Depends(get_current_user)
):
    """Select a perk for the character."""
    from ..services.skills import perks_manager, get_total_perk_points
    
    player = current_user
    character_id = player['id']
    
    # Check perk exists
    perk = perks_manager.get_perk(perk_id)
    if not perk:
        raise HTTPException(status_code=404, detail="Perk not found")
    
    # Check perk points available
    owned_perks = await db.get_character_perks(character_id)
    owned_perk_ids = [row['perk_id'] for row in owned_perks]
    
    total_points = get_total_perk_points(player['level'])
    used_points = len(owned_perk_ids)
    
    if used_points >= total_points:
        raise HTTPException(status_code=400, detail="No perk points available")
    
    # Check if already owned
    if perk_id in owned_perk_ids:
        raise HTTPException(status_code=400, detail="Perk already selected")
    
    # Check requirements
    if not perks_manager.check_requirements(perk, player):
        raise HTTPException(status_code=400, detail="Requirements not met for this perk")
    
    # Add perk
    success = await db.add_character_perk(character_id, perk_id)
    if not success:
        raise HTTPException(status_code=400, detail="Failed to select perk")
    
    # Invalidate stats cache (perks affect derived stats)
    await invalidate_stats_cache(character_id, redis_manager)
    
    return {
        "success": True,
        "message": f"Perk '{perk_id}' selected!",
        "perk": {
            "id": perk.id,
            "name": perk.name,
            "description": perk.description,
            "icon": perk.icon,
        }
    }


@router.get("/api/game/available-skills")
async def get_available_skills(current_user: dict = Depends(get_current_user)):
    """Get available skills for the combat UI abilities dropdown."""
    from ..services.skills import skills_manager
    from .. import database as db
    
    player = current_user
    all_skills = skills_manager.get_available_skills(player)
    
    # Check cooldowns
    effects = await db.get_player_effects(player['id'])
    cooldowns = {eff['source']: eff['ticks_remaining'] for eff in effects if eff.get('effect_type') == 'cooldown'}
    
    # Only return unlocked skills for the combat dropdown
    unlocked = []
    for s in all_skills:
        if s['unlocked']:
            cd_source = f"cd:{s['id']}"
            s['current_cooldown'] = cooldowns.get(cd_source, 0)
            unlocked.append(s)
            
    return {"skills": unlocked}