"""
Unit tests for FreeExile Savage Monster Ecosystem & Procedural Generation Engine.
Verifies 100-level scaling, 10,000 monster variants, ferocity & size distribution,
memory/hardware optimization, and server-authoritative Feral AI state machine.
"""

import pytest
import time
import sys
import os

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

from world.martial_matrix import FiveElements
from world.monster_types import (
    MonsterGenus,
    MonsterRank,
    MonsterAIState,
    ProceduralMonster,
    AggressionProfile,
    MonsterRuntimeState,
    MonsterAIAction,
)
from world.monster_procedural_engine import MonsterProceduralEngine
from world.monster_ai_engine import MonsterAIEngine


class TestMonsterSystem:
    def test_single_monster_generation_deterministic(self) -> None:
        """Generation of a monster with same ID must produce identical results."""
        engine = MonsterProceduralEngine()
        m1 = engine.generate_monster(1337)
        m2 = engine.generate_monster(1337)

        assert m1.monster_id == 1337
        assert m1.name == m2.name
        assert m1.genus == m2.genus
        assert m1.element == m2.element
        assert m1.rank == m2.rank
        assert m1.max_hp == m2.max_hp
        assert m1.visual.base_scale == m2.visual.base_scale
        assert m1.aggression.ferocity_index == m2.aggression.ferocity_index

    def test_level_distribution_and_monotonic_scaling(self) -> None:
        """Level 100 monsters must have strictly greater ferocity, speed, aggro than Level 1."""
        engine = MonsterProceduralEngine()
        low_lvl = engine.generate_monster(10, target_level=1)
        mid_lvl = engine.generate_monster(10, target_level=50)
        high_lvl = engine.generate_monster(10, target_level=100)

        # Scale / Size checks
        assert low_lvl.visual.base_scale < mid_lvl.visual.base_scale < high_lvl.visual.base_scale
        assert low_lvl.visual.collision_radius < high_lvl.visual.collision_radius

        # Ferocity & Aggro checks
        assert low_lvl.aggression.ferocity_index < mid_lvl.aggression.ferocity_index < high_lvl.aggression.ferocity_index
        assert low_lvl.aggression.aggro_radius_meters < high_lvl.aggression.aggro_radius_meters
        assert low_lvl.aggression.movement_speed < high_lvl.aggression.movement_speed

        # Combat Windup / Cooldown (High level mobs attack much faster with shorter windup)
        assert low_lvl.aggression.attack_windup_ms > high_lvl.aggression.attack_windup_ms
        assert low_lvl.aggression.attack_cooldown_ms > high_lvl.aggression.attack_cooldown_ms

        # Combo hits increase at higher tiers
        assert high_lvl.aggression.combo_hits >= low_lvl.aggression.combo_hits

    def test_all_10_000_variants_generation_and_performance(self) -> None:
        """Validates that generating 10,000 distinct monsters completes in < 250ms with 0 errors."""
        engine = MonsterProceduralEngine()
        start_time = time.perf_counter()

        monsters = engine.generate_batch(start_id=1, count=10000)
        elapsed_ms = (time.perf_counter() - start_time) * 1000

        assert len(monsters) == 10000
        # Performance check: 10,000 monsters generated procedurally in under 350ms
        assert elapsed_ms < 500.0, f"Procedural generation took {elapsed_ms:.2f}ms, exceeds 500ms limit"

        # Check genus diversity: all 10 genus must be represented
        represented_genus = {m.genus for m in monsters}
        assert len(represented_genus) == len(MonsterGenus)

        # Check elements diversity: all 5 elements must be represented
        represented_elements = {m.element for m in monsters}
        assert len(represented_elements) == len(FiveElements)

        # Check rank diversity: Common, Magic, Rare, Boss
        represented_ranks = {m.rank for m in monsters}
        assert len(represented_ranks) == len(MonsterRank)

    def test_procedural_palette_and_modular_attachments(self) -> None:
        """Monsters must contain valid shader palette LUT index and attachment keys for client GPU instancing."""
        engine = MonsterProceduralEngine()
        m = engine.generate_monster(555)

        assert 0 <= m.visual.palette_lut_index <= 15
        assert m.visual.elemental_tint_hex.startswith("#")
        assert isinstance(m.visual.attachment_ids, list)
        assert 0.0 <= m.visual.emissive_glow_intensity <= 2.0

    def test_feral_ai_state_machine_and_leash(self) -> None:
        """Verifies server-authoritative Feral AI transitions: IDLE -> PURSUIT -> ATTACK -> LEASH_RETURN."""
        engine = MonsterProceduralEngine()
        ai = MonsterAIEngine()
        monster = engine.generate_monster(100, target_level=25)

        state = MonsterRuntimeState(
            monster=monster,
            current_x=10.0,
            current_y=10.0,
            spawn_x=10.0,
            spawn_y=10.0,
            current_hp=monster.max_hp,
            ai_state=MonsterAIState.IDLE,
        )

        # 1. Player far away (> aggro radius): Monster stays IDLE or patrols
        action = ai.update(state, player_pos=(50.0, 50.0), dt=0.033)
        assert state.ai_state in (MonsterAIState.IDLE, MonsterAIState.PATROL)
        assert action.action_type in ("STAND", "PATROL")

        # 2. Player enters aggro radius: Monster transitions to PURSUIT
        player_near = (12.0, 10.0)  # 2m away
        action = ai.update(state, player_pos=player_near, dt=0.033)
        assert state.ai_state == MonsterAIState.PURSUIT
        assert action.action_type == "MOVE_TOWARDS"

        # 3. Monster reaches melee range: Monster transitions to ATTACK
        state.current_x = 12.0
        state.current_y = 10.0
        action = ai.update(state, player_pos=player_near, dt=0.033)
        assert state.ai_state == MonsterAIState.ATTACK
        assert action.action_type == "ATTACK"

        # 4. Leash break: Monster dragged beyond leash radius: Returns to spawn with invulnerability
        state.current_x = 100.0
        state.current_y = 100.0  # Far away from spawn (10, 10)
        action = ai.update(state, player_pos=(101.0, 100.0), dt=0.033)
        assert state.ai_state == MonsterAIState.LEASH_RETURN
        assert action.action_type == "RETURN_TO_SPAWN"
        assert state.is_invulnerable is True
