"""
Monster & Boss Scaling Engine for FreeExile (PoE2 Spirit).
Calculates level-based power curves (Level 1..100), rank multipliers (Common, Magic, Rare, Boss),
Boss Stagger/Poise thresholds, and Boss Enrage phase transitions.
"""

from __future__ import annotations
import math
from typing import Dict, List, Optional

from combat.combat_types import (
    BossPhase,
    BossStaggerState,
    CombatantType,
    DamageChannel,
    DamagePacket,
    DamageVector,
    DefenseSheet,
)


class MonsterScalingEngine:
    """Server-authoritative mathematical formulas for monster and boss stat curves."""

    def calculate_base_life(self, level: int) -> float:
        """Level 1..100 exponential life baseline curve."""
        lvl = max(1, level)
        return round(50.0 * math.pow(1.0 + (lvl / 10.0), 1.8) + (30.0 * lvl), 1)

    def calculate_base_damage(self, level: int) -> float:
        """Level 1..100 exponential damage baseline curve (calibrated for level 1 survivability)."""
        lvl = max(1, level)
        if lvl == 1:
            return 5.5
        return round(8.0 * math.pow(1.0 + (lvl / 12.0), 1.6) + (4.0 * lvl), 1)

    def calculate_base_armour(self, level: int) -> float:
        """Level 1..100 armour curve."""
        lvl = max(1, level)
        return round(15.0 * math.pow(lvl, 1.2), 1)

    def calculate_base_evasion(self, level: int) -> float:
        """Level 1..100 evasion curve."""
        lvl = max(1, level)
        return round(20.0 * math.pow(lvl, 1.15), 1)

    def create_monster_defense(
        self, level: int, rank: CombatantType, party_size: int = 1
    ) -> DefenseSheet:
        """Constructs a fully scaled DefenseSheet for a monster, factoring in level, rank, and party size."""
        base_hp = self.calculate_base_life(level)
        base_armour = self.calculate_base_armour(level)
        base_evasion = self.calculate_base_evasion(level)

        resistances: Dict[DamageChannel, float] = {
            DamageChannel.FIRE: 0.0,
            DamageChannel.COLD: 0.0,
            DamageChannel.LIGHTNING: 0.0,
            DamageChannel.CHAOS: 0.0,
        }
        aoe_reduction = 0.0

        if rank == CombatantType.MONSTER_COMMON:
            hp_mult = 1.0
            armour_mult = 1.0
        elif rank == CombatantType.MONSTER_MAGIC:
            hp_mult = 2.2
            armour_mult = 1.3
            for ch in resistances:
                resistances[ch] = 0.15
        elif rank == CombatantType.MONSTER_RARE:
            hp_mult = 4.5
            armour_mult = 1.8
            for ch in resistances:
                resistances[ch] = 0.30
        else:  # BOSS
            hp_mult = 20.0
            armour_mult = 2.5
            resistances[DamageChannel.FIRE] = 0.40
            resistances[DamageChannel.COLD] = 0.40
            resistances[DamageChannel.LIGHTNING] = 0.40
            resistances[DamageChannel.CHAOS] = 0.25
            aoe_reduction = 0.25

        # PoE2 Dynamic Party Scaling (+50% Common/Magic, +70% Rare, +100% Boss HP; +10% Armour, +2% Res)
        extra = max(0, party_size - 1)
        if rank == CombatantType.BOSS:
            hp_party_mult = 1.0 + 1.00 * extra
        elif rank == CombatantType.MONSTER_RARE:
            hp_party_mult = 1.0 + 0.70 * extra
        else:
            hp_party_mult = 1.0 + 0.50 * extra

        armour_party_mult = 1.0 + 0.10 * extra
        res_party_bonus = 0.02 * extra

        final_hp = round(base_hp * hp_mult * hp_party_mult, 1)
        final_armour = round(base_armour * armour_mult * armour_party_mult, 1)
        for ch in resistances:
            resistances[ch] = min(0.75, resistances[ch] + res_party_bonus)

        return DefenseSheet(
            defender_id=0,
            combatant_type=rank,
            defender_level=level,
            max_life=final_hp,
            current_life=final_hp,
            max_ward=0.0,
            current_ward=0.0,
            armour=final_armour,
            evasion_rating=base_evasion,
            resistances=resistances,
            max_resistance_caps={
                DamageChannel.FIRE: 0.75,
                DamageChannel.COLD: 0.75,
                DamageChannel.LIGHTNING: 0.75,
                DamageChannel.CHAOS: 0.75,
            },
            aoe_damage_reduction_pct=aoe_reduction,
        )

    def calculate_party_scaled_defense(
        self, level: int, rank: CombatantType, party_size: int = 1
    ) -> DefenseSheet:
        """Alias for create_monster_defense with explicit party_size parameter."""
        return self.create_monster_defense(level=level, rank=rank, party_size=party_size)

    def create_boss_stagger_state(self, boss_max_life: float, level: int) -> BossStaggerState:
        """
        Creates PoE2 Stagger Poise meter:
        Capacity is 15% of Boss Max Life, ensuring deliberate heavy combo hits are needed.
        """
        stagger_poise = round(boss_max_life * 0.15, 1)
        return BossStaggerState(
            max_stagger_poise=max(100.0, stagger_poise),
            current_stagger=0.0,
            is_staggered=False,
            stagger_damage_amplification=1.30,
        )

    def evaluate_boss_phase(self, current_hp: float, max_hp: float) -> BossPhase:
        """Determines Boss Enrage phase based on current HP percentage."""
        if max_hp <= 0.0:
            return BossPhase.PHASE_1_NORMAL
        ratio = current_hp / max_hp
        if ratio <= 0.33:
            return BossPhase.PHASE_3_SAVAGE_FRENZY
        elif ratio <= 0.66:
            return BossPhase.PHASE_2_ENRAGED
        return BossPhase.PHASE_1_NORMAL

    def create_boss_attack_packet(
        self, boss_id: int, level: int, phase: BossPhase = BossPhase.PHASE_1_NORMAL
    ) -> DamagePacket:
        """Constructs an escalated outgoing attack packet according to Boss Phase."""
        base_dmg = self.calculate_base_damage(level) * 2.5
        accuracy = round(30.0 * math.pow(level, 1.1), 1)

        penetrations: Dict[DamageChannel, float] = {}
        more_mults: List[float] = []
        is_aoe = False

        if phase == BossPhase.PHASE_1_NORMAL:
            pass
        elif phase == BossPhase.PHASE_2_ENRAGED:
            more_mults.append(1.25)
            penetrations[DamageChannel.FIRE] = 0.10
        elif phase == BossPhase.PHASE_3_SAVAGE_FRENZY:
            more_mults.append(1.50)
            penetrations[DamageChannel.FIRE] = 0.25
            penetrations[DamageChannel.PHYSICAL] = 0.20
            is_aoe = True

        return DamagePacket(
            attacker_id=boss_id,
            combatant_type=CombatantType.BOSS,
            attacker_level=level,
            damage_vector=DamageVector(
                physical=round(base_dmg * 0.60, 1),
                fire=round(base_dmg * 0.40, 1),
            ),
            accuracy=accuracy,
            crit_chance=0.10,
            crit_multiplier=1.60,
            elemental_penetration=penetrations,
            more_damage_multipliers=more_mults,
            is_aoe=is_aoe,
            stagger_impact=0.0,
        )
