"""
Unit tests for Autonomous Agent Decision Core in FreeExile.
Verifies Perception extraction from ServerEngineLoop, high-level tactical intent,
low-level 30Hz reflex behavior (Ngũ Hành counter, auto-dodge, auto-loot),
and command dispatch to Server Engine queues.
"""

import unittest
import sys
import os

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

from world.server_engine_loop import ServerEngineLoop, ClientMoveCommand, ClientSkillCommand
from world.martial_matrix import FiveElements
from world.combat_engine import CombatActor
from agent.agent_decision_core import (
    AgentPerceptionSnapshot,
    TacticalStance,
    AgentDecisionCore,
    AgentActionPlan
)
from agent.llm_provider_client import AgentActionType, LLMProviderType, LLMProviderConfig


class TestAgentDecisionCore(unittest.TestCase):
    def setUp(self):
        self.engine = ServerEngineLoop()
        # Register player 1
        self.engine.register_player(
            entity_id=1,
            initial_x=10.0,
            initial_y=10.0,
            initial_z=0.0,
            element=FiveElements.KIM,
            move_speed=6.0,
            max_hp=1000.0
        )
        # Register enemy 101 (Thổ element)
        self.engine.register_player(
            entity_id=101,
            initial_x=12.0,
            initial_y=10.0,
            initial_z=0.0,
            element=FiveElements.THO,
            move_speed=4.0,
            max_hp=500.0
        )
        self.core = AgentDecisionCore(
            player_id=1,
            provider_config=LLMProviderConfig(provider=LLMProviderType.LOCAL_TACTICAL),
            stance=TacticalStance.BALANCED
        )

    def test_perception_extraction_from_world(self):
        """Verifies that Agent extracts spatial coordinates, HP ratio, and visible entities accurately."""
        perception = self.core.build_perception(self.engine)
        self.assertEqual(perception.player_id, 1)
        self.assertAlmostEqual(perception.player_x, 10.0)
        self.assertAlmostEqual(perception.player_y, 10.0)
        self.assertAlmostEqual(perception.hp_ratio, 1.0)
        # Enemy 101 is 2 units away, must be detected in nearby entities
        self.assertTrue(any(e.entity_id == 101 for e in perception.nearby_entities))

    def test_combat_element_counter_selection(self):
        """When targeting an Earth enemy (Thổ), Agent chooses Mộc (Wood overcomes Earth) or optimal skill."""
        perception = self.core.build_perception(self.engine)
        plan = self.core.evaluate_tick_action(perception)

        self.assertIsInstance(plan, AgentActionPlan)
        # Must engage enemy 101
        self.assertIn(plan.action_type, [AgentActionType.ATTACK_SKILL, AgentActionType.MOVE_TOWARDS])
        if plan.action_type == AgentActionType.ATTACK_SKILL:
            self.assertEqual(plan.target_entity_id, 101)

    def test_emergency_survival_reflex_when_low_hp(self):
        """When HP drops below 25%, agent immediately reflexes into DODGE or retreats."""
        actor = self.engine.combat_engine.actors[1]
        actor.current_hp = 200.0  # 20% HP remaining

        perception = self.core.build_perception(self.engine)
        self.assertLess(perception.hp_ratio, 0.25)

        plan = self.core.evaluate_tick_action(perception)
        # Reflex action must prioritize survival
        self.assertIn(plan.action_type, [AgentActionType.DODGE, AgentActionType.USE_POTION, AgentActionType.MOVE_AWAY])

    def test_treasure_hunter_stance_prioritizes_loot(self):
        """In TREASURE_HUNTER stance, rare drops like Primal Chaos Stone take priority over standard farming."""
        self.core.stance = TacticalStance.TREASURE_HUNTER
        # Add a dropped item 3 units away
        self.core.register_ground_drop(drop_id="drop_chaos_1", item_id="curr_chaos_stone", x=11.5, y=10.0, rarity=2)

        perception = self.core.build_perception(self.engine)
        plan = self.core.evaluate_tick_action(perception)

        self.assertEqual(plan.action_type, AgentActionType.LOOT)
        self.assertEqual(plan.target_drop_id, "drop_chaos_1")

    def test_dispatch_commands_to_server_engine(self):
        """Verifies that generated plan translates into valid commands submitted to ServerEngineLoop."""
        plan = AgentActionPlan(
            action_type=AgentActionType.MOVE_TOWARDS,
            target_x=12.0,
            target_y=10.0,
            reasoning="Closing distance to attack enemy"
        )
        self.core.dispatch_plan_to_server(plan, self.engine, dt=0.0333)
        # Move queue in engine must contain the command
        self.assertEqual(len(self.engine.move_queue), 1)
        cmd = self.engine.move_queue[0]
        self.assertEqual(cmd.entity_id, 1)
        self.assertGreater(cmd.dir_x, 0.0)  # Moving towards x=12.0


if __name__ == "__main__":
    unittest.main()
