"""
Crafting router.
Auto-generated from main.py migration.
"""
from fastapi import APIRouter, HTTPException, Depends, status
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, calculate_crafting_stamina_cost, get_locale_string
from .. import database as db
from ..items import ItemsManager
from .. import game_logic
from ..core.websockets import manager
from .equipment import consume_tool_durability

logger = logging.getLogger(__name__)

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

def init_router_dependencies(locations, items_manager, world):
    """Initialize router with game data dependencies"""
    global LOCATIONS, ITEMS_MANAGER, WORLD
    LOCATIONS = locations
    ITEMS_MANAGER = items_manager
    WORLD = world

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



# Endpoints

@router.get("/api/game/craftable")
async def get_craftable_items(current_user: dict = Depends(get_current_user)):
    """Get all craftable items with material requirements and availability"""
    try:
        player = current_user  # current_user is already the character dict
        if not player:
            raise HTTPException(status_code=404, detail="Player not found")
        
        # Get player's inventory with quantities
        inventory = await db.get_inventory(current_user['id'])
        inventory_counts = {}
        for inv_item in inventory:
            item_id = inv_item['item_id']
            quantity = inv_item.get('quantity', 1)
            inventory_counts[item_id] = inventory_counts.get(item_id, 0) + quantity
        
        craftable_items = []
        for item_id, item_def in ITEMS_MANAGER.items.items():
            if not getattr(item_def, 'craftable', False):
                continue
            
            craft_materials = getattr(item_def, 'craft_materials', [])
            if not craft_materials:
                continue
            
            # Check material availability
            materials_info = []
            can_craft = True
            for material in craft_materials:
                mat_item_id = material['item_id']
                required = material['quantity']
                available = inventory_counts.get(mat_item_id, 0)
                
                mat_item_def = ITEMS_MANAGER.items.get(mat_item_id)
                materials_info.append({
                    'item_id': mat_item_id,
                    'name': mat_item_def.name if mat_item_def else mat_item_id,
                    'emoji': mat_item_def.emoji if mat_item_def else '📦',
                    'required': required,
                    'available': available,
                    'has_enough': available >= required
                })
                
                if available < required:
                    can_craft = False
            
            # Check tool requirements
            craft_tools = getattr(item_def, 'craft_tools', [])
            tools_info = []
            for tool_req in craft_tools:
                tool_id = tool_req['item_id']
                durability_cost = tool_req['durability_cost']
                tool_def = ITEMS_MANAGER.items.get(tool_id)
                
                # Check if player has this tool
                has_tool = False
                tool_durability = 0
                for inv_item in inventory:
                    # Check if player has this tool (find one with highest durability)
                    has_tool = False
                    tool_durability = 0
                    best_tool_unique = None
                    
                    for inv_item in inventory:
                        if inv_item['item_id'] == tool_id and inv_item.get('unique_item_id'):
                            unique = await db.get_unique_item(inv_item['unique_item_id'])
                            if unique and unique.get('durability', 0) >= durability_cost:
                                if best_tool_unique is None or unique.get('durability', 0) > best_tool_unique.get('durability', 0):
                                    best_tool_unique = unique
                                    has_tool = True
                                    tool_durability = unique.get('durability', 0)
                
                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': has_tool,
                    'tool_durability': tool_durability
                })
                
                if not has_tool:
                    can_craft = False
            
            # Check level requirement
            craft_level = getattr(item_def, 'craft_level', 1)
            player_level = player.get('level', 1)
            meets_level = player_level >= craft_level
            
            # Don't show recipes above player level
            if player_level < craft_level:
                continue
            
            if not meets_level:
                can_craft = False
            
            craftable_items.append({
                'item_id': item_id,
                'name': item_def.name,
                'emoji': item_def.emoji,
                'description': item_def.description,
                'tier': getattr(item_def, 'tier', 1),
                'type': item_def.type,
                'category': item_def.type,  # Add category for filtering
                'slot': getattr(item_def, 'slot', None),
                'materials': materials_info,
                'tools': tools_info,
                'craft_level': craft_level,
                'meets_level': meets_level,
                'uncraftable': getattr(item_def, 'uncraftable', False),
                'can_craft': can_craft,
                'stamina_cost': calculate_crafting_stamina_cost(getattr(item_def, 'tier', 1), 'craft'),
                'stamina_cost': calculate_crafting_stamina_cost(getattr(item_def, 'tier', 1), 'craft'),
                'base_stats': {k: int(v) if isinstance(v, (int, float)) else v for k, v in getattr(item_def, 'stats', {}).items()}
            })
        
        # Sort: craftable items first, then by tier, then by name
        craftable_items.sort(key=lambda x: (not x['can_craft'], x['tier'], get_locale_string(x['name'])))
        
        return {'craftable_items': craftable_items}
    
    except Exception as e:
        print(f"Error getting craftable items: {e}")
        import traceback
        traceback.print_exc()
        raise HTTPException(status_code=500, detail=str(e))


class CraftItemRequest(BaseModel):
    item_id: str


@router.post("/api/game/craft_item")
async def craft_item(request: CraftItemRequest, current_user: dict = Depends(get_current_user)):
    """Craft an item, consuming materials and creating item with random stats for unique items"""
    try:
        player = current_user  # current_user is already the character dict
        if not player:
            raise HTTPException(status_code=404, detail="Player not found")
        
        # Get derived stats for crafting bonus
        from ..services.stats import calculate_derived_stats
        stats = await calculate_derived_stats(player['id'], redis_manager)
        crafting_bonus = stats.get('crafting_bonus', 0.0)
        
        location_id = player['location_id']
        location = LOCATIONS.get(location_id)
        
        # Check if player is at a workbench
        if not location or 'workbench' not in getattr(location, 'tags', []):
            raise HTTPException(status_code=400, detail="You must be at a workbench to craft items")
        
        # Get item definition
        item_def = ITEMS_MANAGER.items.get(request.item_id)
        if not item_def:
            raise HTTPException(status_code=404, detail="Item not found")
        
        if not getattr(item_def, 'craftable', False):
            raise HTTPException(status_code=400, detail="This item cannot be crafted")
        
        # Check level requirement
        craft_level = getattr(item_def, 'craft_level', 1)
        player_level = player.get('level', 1)
        if player_level < craft_level:
            raise HTTPException(status_code=400, detail=f"You need to be level {craft_level} to craft this item (you are level {player_level})")
        
        craft_materials = getattr(item_def, 'craft_materials', [])
        if not craft_materials:
            raise HTTPException(status_code=400, detail="No crafting recipe found")
        
        # Check if player has all materials
        inventory = await db.get_inventory(current_user['id'])
        inventory_counts = {}
        inventory_items_map = {}
        
        for inv_item in inventory:
            item_id = inv_item['item_id']
            quantity = inv_item.get('quantity', 1)
            inventory_counts[item_id] = inventory_counts.get(item_id, 0) + quantity
            if item_id not in inventory_items_map:
                inventory_items_map[item_id] = []
            inventory_items_map[item_id].append(inv_item)
        
        # Check tools requirement
        craft_tools = getattr(item_def, 'craft_tools', [])
        if craft_tools:
            success, error_msg, tools_consumed = await consume_tool_durability(current_user['id'], craft_tools, inventory)
            if not success:
                raise HTTPException(status_code=400, detail=error_msg)
        else:
            tools_consumed = []
        
        # Verify all materials are available
        for material in craft_materials:
            required = material['quantity']
            available = inventory_counts.get(material['item_id'], 0)
            if available < required:
                raise HTTPException(
                    status_code=400,
                    detail=f"Not enough {material['item_id']}. Need {required}, have {available}"
                )
        
        # Consume materials
        materials_used = []
        for material in craft_materials:
            item_id = material['item_id']
            quantity_needed = material['quantity']
            
            items_of_type = inventory_items_map[item_id]
            for inv_item in items_of_type:
                if quantity_needed <= 0:
                    break
                
                inv_quantity = inv_item.get('quantity', 1)
                to_remove = min(quantity_needed, inv_quantity)
                
                if inv_quantity > to_remove:
                    # Update quantity
                    await db.update_inventory_item(
                        inv_item['id'],
                        quantity=inv_quantity - to_remove
                    )
                else:
                    # Remove entire stack - use item_id string, not inventory row id
                    await db.remove_item_from_inventory(current_user['id'], item_id, to_remove)
                
                quantity_needed -= to_remove
            
            mat_item_def = ITEMS_MANAGER.items.get(item_id)
            materials_used.append({
                'item_id': item_id,
                'name': mat_item_def.name if mat_item_def else item_id,
                'quantity': material['quantity']
            })
        
        # Calculate stamina cost
        stamina_cost = calculate_crafting_stamina_cost(getattr(item_def, 'tier', 1), 'craft')
        
        # Check stamina
        if player['stamina'] < stamina_cost:
            raise HTTPException(status_code=400, detail=f"Not enough stamina. Need {stamina_cost}, have {player['stamina']}")
        
        # Deduct stamina
        new_stamina = max(0, player['stamina'] - stamina_cost)
        await db.update_player_stamina(current_user['id'], new_stamina)
        
        # Generate random stats for unique items
        import random
        created_item = None
        
        if hasattr(item_def, 'durability') and item_def.durability:
            # This is a unique item - generate random stats
            base_durability = item_def.durability
            
            # Random durability: 90-110% of base, plus crafting_bonus (e.g. +0.05 from Intellect)
            base_roll = random.uniform(0.9, 1.1)
            durability_percent = base_roll + crafting_bonus
            random_durability = int(base_durability * durability_percent)
            
            # Generate tier based on the final durability percentage
            if durability_percent >= 1.08:
                tier = 5  # Gold
            elif durability_percent >= 1.04:
                tier = 4  # Purple
            elif durability_percent >= 1.0:
                tier = 3  # Blue
            elif durability_percent >= 0.96:
                tier = 2  # Green
            else:
                tier = 1  # White
            
            # Generate random stats if item has stats
            random_stats = {}
            if hasattr(item_def, 'stats') and item_def.stats:
                for stat_key, stat_value in item_def.stats.items():
                    if isinstance(stat_value, (int, float)):
                        # Random stat: same multiplier logic applied to base stats
                        stat_percent = random.uniform(0.9, 1.1) + crafting_bonus
                        random_stats[stat_key] = int(stat_value * stat_percent)
                    else:
                        random_stats[stat_key] = stat_value
            
            # Create unique item in database
            unique_item_id = await db.create_unique_item(
                item_id=request.item_id,
                durability=random_durability,
                max_durability=random_durability,
                tier=tier,
                unique_stats=random_stats
            )
            
            # Add to inventory
            await db.add_item_to_inventory(
                player_id=current_user['id'],
                item_id=request.item_id,
                quantity=1,
                unique_item_id=unique_item_id
            )
            
            created_item = {
                'item_id': request.item_id,
                'name': item_def.name,
                'emoji': item_def.emoji,
                'tier': tier,
                'durability': random_durability,
                'max_durability': random_durability,
                'stats': random_stats,
                'unique': True
            }
        else:
            # Stackable item - just add to inventory
            await db.add_item_to_inventory(
                player_id=current_user['id'],
                item_id=request.item_id,
                quantity=1
            )
            
            created_item = {
                'item_id': request.item_id,
                'name': item_def.name,
                'emoji': item_def.emoji,
                'tier': getattr(item_def, 'tier', 1),
                'unique': False
            }
        
        return {
            'success': True,
            'message': f"Successfully crafted {item_def.name}!",
            'item': created_item,
            'materials_consumed': materials_used,
            'tools_consumed': tools_consumed,
            'stamina_cost': stamina_cost,
            'new_stamina': new_stamina
        }
    
    except Exception as e:
        print(f"Error crafting item: {e}")
        import traceback
        traceback.print_exc()
        raise HTTPException(status_code=500, detail=str(e))


class UncraftItemRequest(BaseModel):
    inventory_id: int
    quantity: int = 1


@router.post("/api/game/uncraft_item")
async def uncraft_item(request: UncraftItemRequest, current_user: dict = Depends(get_current_user)):
    """Uncraft an item, returning materials with a chance of loss"""
    try:
        player = current_user  # current_user is already the character dict
        if not player:
            raise HTTPException(status_code=404, detail="Player not found")
        
        location_id = player['location_id']
        location = LOCATIONS.get(location_id)
        
        # Check if player is at a workbench
        if not location or 'workbench' not in getattr(location, 'tags', []):
            raise HTTPException(status_code=400, detail="You must be at a workbench to uncraft items")
        
        # Get inventory item
        inventory = await db.get_inventory(current_user['id'])
        inv_item = None
        for item in inventory:
            if item['id'] == request.inventory_id:
                inv_item = item
                break
        
        if not inv_item:
            raise HTTPException(status_code=404, detail="Item not found in inventory")
            
        # Check quantity
        if request.quantity <= 0:
            raise HTTPException(status_code=400, detail="Quantity must be greater than 0")
            
        current_quantity = inv_item.get('quantity', 1)
        if request.quantity > current_quantity:
            raise HTTPException(status_code=400, detail=f"Not enough items. Have {current_quantity}, requested {request.quantity}")
        
        # Get item definition
        item_def = ITEMS_MANAGER.items.get(inv_item['item_id'])
        if not item_def:
            raise HTTPException(status_code=404, detail="Item definition not found")
        
        if not getattr(item_def, 'uncraftable', False):
            raise HTTPException(status_code=400, detail="This item cannot be uncrafted")
        
        uncraft_yield = getattr(item_def, 'uncraft_yield', [])
        if not uncraft_yield:
            raise HTTPException(status_code=400, detail="No uncraft recipe found")
        
        # Check tools requirement (once per operation? or per item?)
        # Usually tools are checked once for the operation, but durability cost might be per item.
        # Logic above for crafting consumes tool durability for the batch?
        # In craft_item above, it loops through craft_tools but seemingly only once?
        # Wait, craft_item does NOT loop for quantity because craft_item only crafts 1 at a time (request has no quantity).
        
        # For uncrafting multiple, we should multiply tool cost.
        uncraft_tools = getattr(item_def, 'uncraft_tools', [])
        tools_consumed = []
        
        if uncraft_tools:
            # Scale tool cost by quantity
            scaled_uncraft_tools = []
            for tool_req in uncraft_tools:
                scaled_req = tool_req.copy()
                scaled_req['durability_cost'] = tool_req['durability_cost'] * request.quantity
                scaled_uncraft_tools.append(scaled_req)
                
            success, error_msg, tools_consumed = await consume_tool_durability(current_user['id'], scaled_uncraft_tools, inventory)
            if not success:
                raise HTTPException(status_code=400, detail=error_msg)
            
        # Calculate stamina cost
        base_stamina_cost = calculate_crafting_stamina_cost(getattr(item_def, 'tier', 1), 'uncraft')
        total_stamina_cost = base_stamina_cost * request.quantity
        
        # Check stamina
        if player['stamina'] < total_stamina_cost:
            raise HTTPException(status_code=400, detail=f"Not enough stamina. Need {total_stamina_cost}, have {player['stamina']}")
        
        # Deduct stamina
        new_stamina = max(0, player['stamina'] - total_stamina_cost)
        await db.update_player_stamina(current_user['id'], new_stamina)
        
        # Update inventory item
        if request.quantity == current_quantity:
            # Remove the item row entirely
            await db.remove_inventory_row(inv_item['id'])
        else:
            # Update quantity
            await db.update_inventory_item(
                inv_item['id'],
                quantity=current_quantity - request.quantity
            )
        
        # Calculate durability ratio for yield reduction
        durability_ratio = 1.0  # Default: full yield
        if inv_item.get('unique_item_id'):
            unique_item = await db.get_unique_item(inv_item['unique_item_id'])
            if unique_item:
                current_durability = unique_item.get('durability', 0)
                max_durability = unique_item.get('max_durability', 1)
                if max_durability > 0:
                    durability_ratio = current_durability / max_durability
        
        # Re-fetch inventory to get updated capacity
        inventory = await db.get_inventory(current_user['id'])
        current_weight, max_weight, current_volume, max_volume = await calculate_player_capacity(inventory, ITEMS_MANAGER)
        
        # Calculate materials
        import random
        loss_chance = getattr(item_def, 'uncraft_loss_chance', 0.3)
        
        materials_yielded_dict = {}
        materials_lost_dict = {}
        materials_dropped_dict = {}
        
        # Loop for each item being uncrafted to calculate yield fairly
        for _ in range(request.quantity):
            for material in uncraft_yield:
                # Apply durability reduction first
                base_quantity = material['quantity']
                
                # Calculate adjusted quantity based on durability
                adjusted_quantity = int(round(base_quantity * durability_ratio))
                
                mat_def = ITEMS_MANAGER.items.get(material['item_id'])
                mat_name = mat_def.name if mat_def else material['item_id']
                
                loss_key = (material['item_id'], mat_name)
                
                # If durability is too low (< 10%), yield nothing for this material
                if durability_ratio < 0.1 or adjusted_quantity <= 0:
                     if loss_key not in materials_lost_dict:
                         materials_lost_dict[loss_key] = 0
                     materials_lost_dict[loss_key] += base_quantity
                     continue
                
                # Roll for loss chance
                if random.random() < loss_chance:
                    # Lost this material
                    if loss_key not in materials_lost_dict:
                        materials_lost_dict[loss_key] = 0
                    materials_lost_dict[loss_key] += adjusted_quantity
                else:
                    # Check if it fits in inventory (incremental check?)
                    # For simplicity, check per unit or accumulate and check at end.
                    # Checking per unit is safer but slower.
                    # Since we are modifying inventory in loop (potentially), we should be careful.
                    # Actually, we should accumulate yield then add to inventory at end to optimize DB calls?
                    # But we need to check capacity.
                    # Let's accumulate pending yield.
                    
                    yield_key = (material['item_id'], mat_name, mat_def.emoji if mat_def else '📦', mat_def)
                    if yield_key not in materials_yielded_dict:
                        materials_yielded_dict[yield_key] = 0
                    materials_yielded_dict[yield_key] += adjusted_quantity
        
        # Now process the accumulated yield
        materials_yielded = []
        materials_lost = []
        materials_dropped = []
        
        # Convert lost dict to list
        for (item_id, name), qty in materials_lost_dict.items():
            materials_lost.append({
                'item_id': item_id,
                'name': name,
                'quantity': qty,
                'reason': 'lost_or_low_durability'
            })
            
        # Process yield
        for (item_id, name, emoji, mat_def), qty in materials_yielded_dict.items():
            mat_weight = getattr(mat_def, 'weight', 0) * qty
            mat_volume = getattr(mat_def, 'volume', 0) * qty
            
            # Simple check against capacity (assuming current_weight was just updated from DB)
            # Note: we might fill up mid-loop. ideally we add one by one or check total.
            # Let's check total.
            
            if current_weight + mat_weight <= max_weight and current_volume + mat_volume <= max_volume:
                # Fits
                await db.add_item_to_inventory(
                    player_id=current_user['id'],
                    item_id=item_id,
                    quantity=qty
                )
                current_weight += mat_weight
                current_volume += mat_volume
                
                materials_yielded.append({
                    'item_id': item_id,
                    'name': name,
                    'emoji': emoji,
                    'quantity': qty
                })
            else:
                # Drop
                await db.drop_item_to_world(
                    item_id=item_id,
                    quantity=qty,
                    location_id=player['location_id']
                )
                
                materials_dropped.append({
                    'item_id': item_id,
                    'name': name,
                    'emoji': emoji,
                    'quantity': qty
                })

        message = f"Uncrafted {request.quantity}x {item_def.name}!"
        if durability_ratio < 1.0:
            message += f" (Condition reduced yield)"
        if materials_lost:
            message += f" Lost materials."
        if materials_dropped:
            message += f" Inventory full! Dropped items."
        
        return {
            'success': True,
            'message': message,
            'item_name': item_def.name,
            'materials_yielded': materials_yielded,
            'materials_lost': materials_lost,
            'materials_dropped': materials_dropped,
            'tools_consumed': tools_consumed,
            'loss_chance': loss_chance,
            'durability_ratio': round(durability_ratio, 2),
            'stamina_cost': total_stamina_cost,
            'new_stamina': new_stamina
        }
    
    except Exception as e:
        print(f"Error uncrafting item: {e}")
        import traceback
        traceback.print_exc()
        raise HTTPException(status_code=500, detail=str(e))