from typing import List, Optional, Tuple, cast

from django.conf import settings

import promptgame.settings.custom_types as settings_types


def _tiers() -> List[settings_types.TierDict]:
    # Type checkers cannot infer types for settings.* attributes, so we cast the
    # right type here to catch bugs. This is done in a function instead of at
    # the module level so that patching still works in unit tests.
    return cast(List[settings_types.TierDict], settings.TIERS)


def tier(
    balance: float | int, key: Optional[str] = None
) -> Optional[settings_types.TierDict]:
    for tier_info in _tiers():
        lower, upper = tier_info["range"]
        if lower <= balance <= upper:
            if key:
                return tier_info.get(key)
            return tier_info
    return None


def tier_index(balance: float | int) -> int:
    t = tier(balance)
    idx = _tiers().index(t)
    assert idx is not None
    return idx


def tier_bounds(balance: float | int) -> Tuple[float, float]:
    current_tier = tier(balance)

    assert current_tier is not None, f"{balance=} is not in any tier"

    lower_index = current_tier["neighbors"][0]
    upper_index = current_tier["neighbors"][1]

    lower_balance_bound = _tiers()[lower_index]["range"][0]
    upper_balance_bound = _tiers()[upper_index]["range"][1]

    return lower_balance_bound, upper_balance_bound


def valid_tier(attacker_balance: float | int, defender_balance: float | int) -> bool:
    if not attacker_balance or not defender_balance:
        return True

    lower_balance_bound, upper_balance_bound = tier_bounds(attacker_balance)

    return lower_balance_bound <= defender_balance <= upper_balance_bound
