"""Tests for mob AI integration with behavior states."""

from pathlib import Path
from unittest.mock import AsyncMock, MagicMock

import pytest

from mudlib.combat.commands import combat_moves
from mudlib.combat.engine import active_encounters
from mudlib.combat.moves import load_moves
from mudlib.entity import Mob
from mudlib.mob_ai import process_mob_movement
from mudlib.mobs import mobs
from mudlib.npc_behavior import transition_state
from mudlib.player import Player, players
from mudlib.zone import Zone


@pytest.fixture(autouse=True)
def clear_state():
    """Clear mobs, encounters, and players before and after each test."""
    mobs.clear()
    active_encounters.clear()
    players.clear()
    yield
    mobs.clear()
    active_encounters.clear()
    players.clear()


@pytest.fixture
def test_zone():
    """Create a test zone for entities."""
    terrain = [["." for _ in range(256)] for _ in range(256)]
    zone = Zone(
        name="testzone",
        width=256,
        height=256,
        toroidal=True,
        terrain=terrain,
        impassable=set(),
    )
    return zone


@pytest.fixture
def mock_writer():
    writer = MagicMock()
    writer.write = MagicMock()
    writer.drain = AsyncMock()
    return writer


@pytest.fixture
def mock_reader():
    return MagicMock()


@pytest.fixture
def player(mock_reader, mock_writer, test_zone):
    p = Player(name="Hero", x=0, y=0, reader=mock_reader, writer=mock_writer)
    p.location = test_zone
    test_zone._contents.append(p)
    players[p.name] = p
    return p


@pytest.fixture
def moves():
    """Load combat moves from content directory."""
    content_dir = Path(__file__).parent.parent / "content" / "combat"
    return load_moves(content_dir)


@pytest.fixture(autouse=True)
def inject_moves(moves):
    """Inject loaded moves into combat commands module."""
    combat_moves.update(moves)
    yield
    combat_moves.clear()


class TestConverseBehaviorBlocksMovement:
    @pytest.mark.asyncio
    async def test_converse_state_prevents_wander(self, test_zone):
        """Mob in converse state doesn't wander (stays put)."""
        mob = Mob(
            name="librarian",
            x=10,
            y=10,
            location=test_zone,
            home_x_min=0,
            home_x_max=5,
            home_y_min=0,
            home_y_max=5,
        )
        mobs.append(mob)
        mob.next_action_at = 0.0  # cooldown expired

        # Transition to converse state
        transition_state(mob, "converse")

        # Process movement
        await process_mob_movement()

        # Mob should not have moved
        assert mob.x == 10
        assert mob.y == 10


class TestWorkingBehaviorBlocksMovement:
    @pytest.mark.asyncio
    async def test_working_state_prevents_wander(self, test_zone):
        """Mob in working state doesn't wander (stays at post)."""
        mob = Mob(
            name="blacksmith",
            x=20,
            y=20,
            location=test_zone,
            home_x_min=0,
            home_x_max=5,
            home_y_min=0,
            home_y_max=5,
        )
        mobs.append(mob)
        mob.next_action_at = 0.0  # cooldown expired

        # Transition to working state
        transition_state(mob, "working")

        # Process movement
        await process_mob_movement()

        # Mob should not have moved
        assert mob.x == 20
        assert mob.y == 20


class TestPatrolBehaviorUsesWaypoints:
    @pytest.mark.asyncio
    async def test_patrol_moves_toward_waypoint_not_home(self, test_zone):
        """Mob in patrol state moves toward waypoint, not home region."""
        mob = Mob(
            name="guard",
            x=10,
            y=10,
            location=test_zone,
            home_x_min=0,
            home_x_max=5,
            home_y_min=0,
            home_y_max=5,
        )
        mobs.append(mob)
        mob.next_action_at = 0.0

        # Set patrol with waypoint to the east
        waypoints = [{"x": 20, "y": 10}]
        transition_state(mob, "patrol", {"waypoints": waypoints, "waypoint_index": 0})

        # Process movement
        await process_mob_movement()

        # Mob should have moved east (toward waypoint at 20,10)
        # NOT west toward home region (0-5, 0-5)
        assert mob.x == 11
        assert mob.y == 10

    @pytest.mark.asyncio
    async def test_patrol_advances_waypoint_on_arrival(self, test_zone):
        """Mob in patrol state advances waypoint when reaching it."""
        waypoints = [{"x": 10, "y": 10}, {"x": 20, "y": 20}]
        mob = Mob(
            name="guard",
            x=10,
            y=10,
            location=test_zone,
            home_x_min=0,
            home_x_max=5,
            home_y_min=0,
            home_y_max=5,
        )
        mobs.append(mob)
        mob.next_action_at = 0.0
        transition_state(mob, "patrol", {"waypoints": waypoints, "waypoint_index": 0})

        # At first waypoint already
        await process_mob_movement()

        # Waypoint should advance (handled by process_behavior in npc_behavior)
        # But movement is skipped since we're at the waypoint
        # Mob advances waypoint, then on next tick moves toward new waypoint
        assert mob.behavior_data["waypoint_index"] == 1


class TestFleeBehavior:
    @pytest.mark.asyncio
    async def test_flee_moves_away_from_threat(self, test_zone):
        """Mob in flee state moves away from threat."""
        mob = Mob(
            name="rabbit",
            x=10,
            y=10,
            location=test_zone,
            home_x_min=8,
            home_x_max=12,
            home_y_min=8,
            home_y_max=12,
        )
        mobs.append(mob)
        mob.next_action_at = 0.0

        # Threat to the east
        flee_data = {"flee_from": {"x": 15, "y": 10}}
        transition_state(mob, "flee", flee_data)

        # Process movement
        await process_mob_movement()

        # Mob should have moved west (away from threat)
        assert mob.x == 9
        assert mob.y == 10


class TestIdleBehaviorUsesHomeRegion:
    @pytest.mark.asyncio
    async def test_idle_uses_original_wander_logic(self, test_zone):
        """Mob in idle state uses original home-region wander logic."""
        mob = Mob(
            name="wanderer",
            x=50,
            y=50,
            location=test_zone,
            home_x_min=0,
            home_x_max=5,
            home_y_min=0,
            home_y_max=5,
        )
        mobs.append(mob)
        mob.next_action_at = 0.0

        # Explicitly set idle state
        transition_state(mob, "idle")

        original_x = mob.x
        original_y = mob.y

        # Process movement
        await process_mob_movement()

        # Mob should have moved toward home region (toward 0-5, 0-5)
        # Since mob is at (50, 50) and home is (0-5, 0-5), should move west or north
        assert mob.x < original_x or mob.y < original_y
