"""
Procedural 15-Tier Affix Catalog for FreeExile Monsters.
Defines T1 (Apex) to T15 (Novice) affixes across survival, offensive,
elemental, and speed categories.
"""

from typing import Dict, List, Optional, Tuple
try:
    from server.world.martial_matrix import FiveElements
    from server.world.monster_types import (
        MonsterAffix,
        MonsterAffixCategory,
        MonsterRank,
    )
except ImportError:
    from world.martial_matrix import FiveElements
    from world.monster_types import (
        MonsterAffix,
        MonsterAffixCategory,
        MonsterRank,
    )

# 15-Tier Specification Matrix (T1 Apex -> T15 Novice)
TIER_SPECS: List[Dict[str, float]] = [
    {"tier": 1, "min_lvl": 85, "weight": 20, "hp_m": 2.20, "arm_m": 1.50, "res_m": 0.45, "dmg_m": 1.85, "elem_m": 0.75, "spd_m": 0.45, "wup_m": -0.35},
    {"tier": 2, "min_lvl": 80, "weight": 45, "hp_m": 2.05, "arm_m": 1.30, "res_m": 0.40, "dmg_m": 1.75, "elem_m": 0.65, "spd_m": 0.40, "wup_m": -0.32},
    {"tier": 3, "min_lvl": 75, "weight": 75, "hp_m": 1.92, "arm_m": 1.15, "res_m": 0.35, "dmg_m": 1.65, "elem_m": 0.56, "spd_m": 0.35, "wup_m": -0.28},
    {"tier": 4, "min_lvl": 70, "weight": 110, "hp_m": 1.80, "arm_m": 1.00, "res_m": 0.30, "dmg_m": 1.56, "elem_m": 0.48, "spd_m": 0.30, "wup_m": -0.25},
    {"tier": 5, "min_lvl": 65, "weight": 160, "hp_m": 1.70, "arm_m": 0.88, "res_m": 0.26, "dmg_m": 1.48, "elem_m": 0.41, "spd_m": 0.26, "wup_m": -0.22},
    {"tier": 6, "min_lvl": 60, "weight": 220, "hp_m": 1.60, "arm_m": 0.76, "res_m": 0.22, "dmg_m": 1.41, "elem_m": 0.35, "spd_m": 0.22, "wup_m": -0.19},
    {"tier": 7, "min_lvl": 54, "weight": 290, "hp_m": 1.52, "arm_m": 0.65, "res_m": 0.19, "dmg_m": 1.35, "elem_m": 0.30, "spd_m": 0.19, "wup_m": -0.16},
    {"tier": 8, "min_lvl": 48, "weight": 370, "hp_m": 1.44, "arm_m": 0.55, "res_m": 0.16, "dmg_m": 1.29, "elem_m": 0.25, "spd_m": 0.16, "wup_m": -0.14},
    {"tier": 9, "min_lvl": 42, "weight": 460, "hp_m": 1.37, "arm_m": 0.45, "res_m": 0.13, "dmg_m": 1.24, "elem_m": 0.21, "spd_m": 0.13, "wup_m": -0.12},
    {"tier": 10, "min_lvl": 36, "weight": 560, "hp_m": 1.30, "arm_m": 0.36, "res_m": 0.11, "dmg_m": 1.20, "elem_m": 0.17, "spd_m": 0.11, "wup_m": -0.10},
    {"tier": 11, "min_lvl": 30, "weight": 670, "hp_m": 1.24, "arm_m": 0.28, "res_m": 0.09, "dmg_m": 1.16, "elem_m": 0.14, "spd_m": 0.09, "wup_m": -0.08},
    {"tier": 12, "min_lvl": 24, "weight": 790, "hp_m": 1.19, "arm_m": 0.21, "res_m": 0.07, "dmg_m": 1.12, "elem_m": 0.11, "spd_m": 0.07, "wup_m": -0.06},
    {"tier": 13, "min_lvl": 18, "weight": 920, "hp_m": 1.14, "arm_m": 0.15, "res_m": 0.05, "dmg_m": 1.09, "elem_m": 0.08, "spd_m": 0.05, "wup_m": -0.05},
    {"tier": 14, "min_lvl": 10, "weight": 1050, "hp_m": 1.10, "arm_m": 0.10, "res_m": 0.03, "dmg_m": 1.06, "elem_m": 0.06, "spd_m": 0.04, "wup_m": -0.04},
    {"tier": 15, "min_lvl": 1, "weight": 1200, "hp_m": 1.06, "arm_m": 0.05, "res_m": 0.02, "dmg_m": 1.04, "elem_m": 0.04, "spd_m": 0.03, "wup_m": -0.03},
]

TIER_NAME_PREFIXES: Dict[MonsterAffixCategory, str] = {
    MonsterAffixCategory.SURVIVAL_HP: "Thiết Cốt",
    MonsterAffixCategory.SURVIVAL_DEFENSE: "Kim Cang",
    MonsterAffixCategory.OFFENSIVE_DAMAGE: "Cuồng Bạo",
    MonsterAffixCategory.ELEMENTAL_FIRE: "Liệt Diễm",
    MonsterAffixCategory.ELEMENTAL_LIGHTNING: "Lôi Đình",
    MonsterAffixCategory.TACTICAL_SPEED: "Tật Phong",
}


def get_tier_for_level(level: int) -> int:
    """Returns the highest eligible affix tier (1 to 15) for a given level."""
    for spec in TIER_SPECS:
        if level >= int(spec["min_lvl"]):
            return int(spec["tier"])
    return 15


def _build_tier_affixes(spec: Dict[str, float]) -> List[MonsterAffix]:
    """Builds the 6 standard affixes for a single tier specification."""
    t = int(spec["tier"])
    lvl = int(spec["min_lvl"])
    return [
        MonsterAffix(
            affix_id=f"affix_hp_t{t}",
            name=f"{TIER_NAME_PREFIXES[MonsterAffixCategory.SURVIVAL_HP]} T{t}",
            category=MonsterAffixCategory.SURVIVAL_HP,
            tier=t,
            min_level=lvl,
            stat_multipliers={"hp_mult": spec["hp_m"]},
        ),
        MonsterAffix(
            affix_id=f"affix_def_t{t}",
            name=f"{TIER_NAME_PREFIXES[MonsterAffixCategory.SURVIVAL_DEFENSE]} T{t}",
            category=MonsterAffixCategory.SURVIVAL_DEFENSE,
            tier=t,
            min_level=lvl,
            stat_multipliers={"armor_mult": 1.0 + spec["arm_m"], "res_mult": 1.0 + spec["res_m"]},
        ),
        MonsterAffix(
            affix_id=f"affix_dmg_t{t}",
            name=f"{TIER_NAME_PREFIXES[MonsterAffixCategory.OFFENSIVE_DAMAGE]} T{t}",
            category=MonsterAffixCategory.OFFENSIVE_DAMAGE,
            tier=t,
            min_level=lvl,
            stat_multipliers={"dmg_mult": spec["dmg_m"]},
        ),
        MonsterAffix(
            affix_id=f"affix_fire_t{t}",
            name=f"{TIER_NAME_PREFIXES[MonsterAffixCategory.ELEMENTAL_FIRE]} T{t}",
            category=MonsterAffixCategory.ELEMENTAL_FIRE,
            tier=t,
            min_level=lvl,
            stat_multipliers={"elem_dmg_mult": 1.0 + spec["elem_m"]},
            element=FiveElements.HOA,
        ),
        MonsterAffix(
            affix_id=f"affix_lightning_t{t}",
            name=f"{TIER_NAME_PREFIXES[MonsterAffixCategory.ELEMENTAL_LIGHTNING]} T{t}",
            category=MonsterAffixCategory.ELEMENTAL_LIGHTNING,
            tier=t,
            min_level=lvl,
            stat_multipliers={"elem_dmg_mult": 1.0 + spec["elem_m"]},
            element=FiveElements.KIM,
        ),
        MonsterAffix(
            affix_id=f"affix_spd_t{t}",
            name=f"{TIER_NAME_PREFIXES[MonsterAffixCategory.TACTICAL_SPEED]} T{t}",
            category=MonsterAffixCategory.TACTICAL_SPEED,
            tier=t,
            min_level=lvl,
            stat_multipliers={"speed_mult": 1.0 + spec["spd_m"], "windup_mult": 1.0 + spec["wup_m"]},
        ),
    ]


MONSTER_AFFIX_CATALOG: Dict[int, List[MonsterAffix]] = {
    int(spec["tier"]): _build_tier_affixes(spec) for spec in TIER_SPECS
}


def roll_monster_affixes(level: int, rank: MonsterRank, seed: int) -> Tuple[MonsterAffix, ...]:
    """
    Rolls procedural affixes deterministically according to monster rank and level.
    Higher ranks roll more affixes. Apex tiers (T1-T5) require level >= 65.
    """
    if rank == MonsterRank.COMMON:
        affix_count = 1 if (seed % 3 == 0) else 0
    elif rank == MonsterRank.MAGIC:
        affix_count = 1 + (seed % 2)
    elif rank == MonsterRank.RARE:
        affix_count = 3 + (seed % 2)
    else:  # BOSS
        affix_count = 4 + (seed % 2)

    if affix_count == 0:
        return ()

    base_tier = get_tier_for_level(level)
    # Available tiers: base_tier up to min(15, base_tier + 2)
    chosen: List[MonsterAffix] = []
    used_categories: set = set()

    for idx in range(affix_count):
        tier_offset = (seed // (idx + 1) + idx * 7) % 3
        actual_tier = min(15, base_tier + tier_offset)
        pool = MONSTER_AFFIX_CATALOG.get(actual_tier, MONSTER_AFFIX_CATALOG[15])

        # Pick affix of distinct category if possible
        available = [a for a in pool if a.category not in used_categories]
        if not available:
            available = pool
        choice_idx = (seed + idx * 31) % len(available)
        affix = available[choice_idx]
        chosen.append(affix)
        used_categories.add(affix.category)

    return tuple(chosen)


def apply_affixes_to_stats(
    base_hp: float,
    base_damage: float,
    base_armor: float,
    affixes: Tuple[MonsterAffix, ...]
) -> Tuple[float, float, float]:
    """Aggregates affix stat multipliers and applies them to baseline stats."""
    hp_mult = 1.0
    dmg_mult = 1.0
    arm_mult = 1.0

    for affix in affixes:
        if "hp_mult" in affix.stat_multipliers:
            hp_mult *= affix.stat_multipliers["hp_mult"]
        if "dmg_mult" in affix.stat_multipliers:
            dmg_mult *= affix.stat_multipliers["dmg_mult"]
        if "armor_mult" in affix.stat_multipliers:
            arm_mult *= affix.stat_multipliers["armor_mult"]

    return (
        round(base_hp * hp_mult, 1),
        round(base_damage * dmg_mult, 1),
        round(base_armor * arm_mult, 1),
    )
