"""
Unit tests for Monster Pack Spawning, 15-Tier Affixes, Leader Auras, and Coordinated AI.
Verifies PoE2 encounter standards, procedural affix distribution,
proximity aura propagation/decay, and pack alerting.
"""

import math
import os
import sys
import pytest

sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../server")))

from world.monster_types import (
    MonsterGenus,
    MonsterRank,
    MonsterAIState,
    PackAuraType,
    MonsterAffixCategory,
    MonsterRuntimeState,
)
from world.monster_procedural_engine import MonsterProceduralEngine
from world.monster_affix_catalog import (
    get_tier_for_level,
    roll_monster_affixes,
    apply_affixes_to_stats,
    MONSTER_AFFIX_CATALOG,
)
from world.monster_ai_engine import MonsterAIEngine


class TestMonsterPackAndAffixes:
    def test_pack_composition_and_leader_designation(self) -> None:
        """Verifies MonsterPack creates a Rare leader and tethered minions."""
        engine = MonsterProceduralEngine()
        pack = engine.generate_pack(
            pack_id="pack_test_alpha",
            zone_id="zone_tang_kiem_nhai",
            level=35,
            center_x=100.0,
            center_y=120.0,
            seed=42,
            minion_count=4,
        )

        assert pack.pack_id == "pack_test_alpha"
        assert pack.zone_id == "zone_tang_kiem_nhai"
        assert pack.leader.is_pack_leader is True
        assert pack.leader.rank == MonsterRank.RARE
        assert pack.leader.leader_aura in list(PackAuraType)
        assert len(pack.leader.affixes) >= 3
        assert len(pack.minions) == 4

        for minion in pack.minions:
            assert minion.is_pack_leader is False
            assert minion.pack_leader_id == pack.leader.monster_id
            assert minion.pack_id == "pack_test_alpha"
            assert minion.rank == MonsterRank.COMMON

    def test_fifteen_tier_affix_distribution(self) -> None:
        """Verifies the 15-tier affix hierarchy and stat multipliers."""
        assert len(MONSTER_AFFIX_CATALOG) == 15

        # Level to Tier mapping checks
        assert get_tier_for_level(95) == 1
        assert get_tier_for_level(85) == 1
        assert get_tier_for_level(80) == 2
        assert get_tier_for_level(50) == 8
        assert get_tier_for_level(10) == 14
        assert get_tier_for_level(1) == 15

        # Apex tier affixes require high level
        apex_affixes = roll_monster_affixes(level=88, rank=MonsterRank.RARE, seed=123)
        assert len(apex_affixes) >= 3
        for aff in apex_affixes:
            assert aff.tier <= 3, f"Expected Apex tier 1-3, got {aff.tier}"

        # Low level affixes get Novice tiers
        low_affixes = roll_monster_affixes(level=5, rank=MonsterRank.RARE, seed=123)
        for aff in low_affixes:
            assert aff.tier >= 13, f"Expected Novice tier 13-15, got {aff.tier}"

        # Stat application check
        base_hp, base_dmg, base_arm = 1000.0, 100.0, 50.0
        mod_hp, mod_dmg, mod_arm = apply_affixes_to_stats(base_hp, base_dmg, base_arm, apex_affixes)
        assert mod_hp >= base_hp
        assert mod_dmg >= base_dmg
        assert mod_arm >= base_arm

    def test_leader_aura_buff_application_and_decay(self) -> None:
        """Verifies minions receive buffs within 14.0m, and buffs decay if leader dies or separates."""
        engine = MonsterProceduralEngine()
        ai = MonsterAIEngine()

        pack = engine.generate_pack(
            pack_id="pack_aura_test",
            zone_id="zone_tang_kiem_nhai",
            level=30,
            center_x=50.0,
            center_y=50.0,
            seed=777,
            minion_count=3,
        )

        states = engine.create_pack_runtime_states(pack)
        leader_st = states[0]
        minion_st = states[1]

        # 1. Minion is near leader (< 14m): receives aura buff
        ai.update_pack(states, player_pos=(200.0, 200.0), dt=0.033)
        assert len(minion_st.active_buffs) > 0

        # 2. Leader killed: minion immediately loses aura buff
        leader_st.current_hp = 0.0
        leader_st.ai_state = MonsterAIState.DEAD
        ai.update_pack(states, player_pos=(200.0, 200.0), dt=0.033)
        assert len(minion_st.active_buffs) == 0

        # 3. Leader resurrected, but minion moved far away (> 14m)
        leader_st.current_hp = leader_st.monster.max_hp
        leader_st.ai_state = MonsterAIState.IDLE
        minion_st.current_x = 100.0
        minion_st.current_y = 100.0
        ai.update_pack(states, player_pos=(200.0, 200.0), dt=0.033)
        assert len(minion_st.active_buffs) == 0

    def test_pack_coordinated_aggro_broadcast(self) -> None:
        """Verifies that when one minion detects the player, all pack members join pursuit."""
        engine = MonsterProceduralEngine()
        ai = MonsterAIEngine()

        pack = engine.generate_pack(
            pack_id="pack_aggro_test",
            zone_id="zone_tang_kiem_nhai",
            level=20,
            center_x=30.0,
            center_y=30.0,
            seed=888,
            minion_count=3,
        )

        states = engine.create_pack_runtime_states(pack)
        # Place player right next to minion 1 (within aggro radius)
        minion_target = states[1]
        player_near = (minion_target.current_x + 1.0, minion_target.current_y)

        # Update pack AI: minion 1 will aggro and broadcast to pack
        ai.update_pack(states, player_pos=player_near, dt=0.033)

        # All living pack members must be in PURSUIT or ATTACK
        for s in states:
            assert s.ai_state in (MonsterAIState.PURSUIT, MonsterAIState.ATTACK)
