import random
from typing import Tuple, Dict, Any

from data.items import ITEMS
from data.models import Action, Outcome

def calculate_inventory_load(player_inventory: list) -> Tuple[float, float]:
    """Calculates the total weight and volume of a player's inventory."""
    total_weight = 0.0
    total_volume = 0.0
    for item in player_inventory:
        item_def = ITEMS.get(item["item_id"])
        if item_def:
            total_weight += item_def["weight"] * item["quantity"]
            total_volume += item_def["volume"] * item["quantity"]
    return round(total_weight, 2), round(total_volume, 2)

def get_player_capacity(player_inventory: list, player_stats: dict) -> Tuple[float, float]:
    """Calculates the total carrying capacity of a player."""
    base_weight_cap = player_stats['strength'] * 5  # Example formula
    base_volume_cap = player_stats['strength'] * 2  # Example formula

    for item in player_inventory:
        if item["is_equipped"]:
            item_def = ITEMS.get(item["item_id"])
            if item_def and item_def.get("type") == "equipment":
                effects = item_def.get("effects", {})
                base_weight_cap += effects.get("capacity_weight", 0)
                base_volume_cap += effects.get("capacity_volume", 0)
    
    return base_weight_cap, base_volume_cap

def resolve_action(player_stats: dict, action_obj: Action) -> Outcome:
    """
    Resolves a player action, like searching, based on stats and luck.
    Returns the resulting Outcome object.
    """
    # A simple success chance calculation
    base_chance = 50 + (player_stats.get('intellect', 5) * 2)
    roll = random.randint(1, 100)

    outcome_key = "failure"
    if roll <= 5 and "critical_failure" in action_obj.outcomes:
        outcome_key = "critical_failure"
    elif roll <= base_chance and "success" in action_obj.outcomes:
        outcome_key = "success"

    return action_obj.outcomes.get(outcome_key, action_obj.outcomes["failure"])

async def can_add_item_to_inventory(user_id: int, item_id: str, quantity: int) -> Tuple[bool, str]:
    """
    Check if an item can be added to the player's inventory.
    Returns (can_add, reason_if_not)
    """
    from .api_client import api_client
    
    player = await api_client.get_player(user_id)
    if not player:
        return False, "Player not found."
    
    inventory = await api_client.get_inventory(user_id)
    item_def = ITEMS.get(item_id)
    
    if not item_def:
        return False, "Invalid item."
    
    # Calculate current and projected weight/volume
    current_weight, current_volume = calculate_inventory_load(inventory)
    max_weight, max_volume = get_player_capacity(inventory, player)
    
    item_weight = item_def["weight"] * quantity
    item_volume = item_def["volume"] * quantity
    
    new_weight = current_weight + item_weight
    new_volume = current_volume + item_volume
    
    if new_weight > max_weight:
        return False, f"Too heavy! ({new_weight:.1f}/{max_weight:.1f} kg)"
    
    if new_volume > max_volume:
        return False, f"Not enough space! ({new_volume:.1f}/{max_volume:.1f} vol)"
    
    return True, ""

def calculate_travel_stamina_cost(player: dict, inventory: list, from_location, to_location) -> int:
    """
    Calculate stamina cost for traveling between locations.
    Based on distance, endurance (reduces cost), and carried weight (increases cost).
    
    Args:
        player: Player stats dictionary
        inventory: Player's inventory list
        from_location: Location object being traveled from
        to_location: Location object being traveled to
    """
    from data.travel_helpers import calculate_base_stamina_cost
    
    # Get base cost from shared helper (used by map and game)
    distance_cost = calculate_base_stamina_cost(from_location, to_location)
    
    # Endurance reduces cost (each point reduces by 0.5)
    endurance_reduction = player['endurance'] * 0.5
    
    # Calculate weight burden
    current_weight, _ = calculate_inventory_load(inventory)
    max_weight, _ = get_player_capacity(inventory, player)
    
    # Weight penalty: if carrying more than 50% capacity, add extra cost
    weight_ratio = current_weight / max_weight if max_weight > 0 else 0
    weight_penalty = 0
    
    if weight_ratio > 0.5:
        # Each 10% over 50% adds 1 stamina
        weight_penalty = int((weight_ratio - 0.5) * 10)
    
    # Calculate final cost (minimum 3)
    final_cost = max(3, int(distance_cost - endurance_reduction + weight_penalty))
    
    return final_cost
