"""
Unit tests for PoE2 Zone Spawning Purity & Encounter Rules in FreeExile.
Verifies that Safe Havens (Sanctuary & Hideout) NEVER spawn hostile monsters,
restricting safe zones strictly to inanimate training dummies, while outer
exploration maps and dungeons support dynamic hostile monster populations.
Also validates spatial grid initialization, entity isolation, and portal traversal
for Player Hideout.
"""

from pathlib import Path
import pytest

from server.world.spatial_grid import Entity, SpatialGrid
from server.world.zone_engine import ZoneEngine
from server.world.zone_types import ZoneEnvironment, ZoneType


@pytest.fixture
def zone_engine() -> ZoneEngine:
    return ZoneEngine()


class TestPoE2ZoneSpawningPurity:
    """Tests authoritative safe-zone purity and monster spawn gating."""

    def test_sanctuary_zone_type_is_sanctuary(self, zone_engine: ZoneEngine) -> None:
        """Verifies that Outcast Encampment Hub is registered as SANCTUARY."""
        sanctuary = zone_engine.get_zone("zone_boundless_sanctuary")
        assert sanctuary is not None
        assert sanctuary.zone_type == ZoneType.SANCTUARY
        assert sanctuary.environment == ZoneEnvironment.NORMAL
        assert sanctuary.min_level == 1

    def test_player_hideout_zone_type_is_sanctuary(self, zone_engine: ZoneEngine) -> None:
        """Verifies that Player Hideout (Tiên Phủ Động Thiên) is registered as SANCTUARY."""
        hideout = zone_engine.get_zone("zone_player_hideout")
        assert hideout is not None
        assert hideout.zone_id == "zone_player_hideout"
        assert hideout.zone_type == ZoneType.SANCTUARY
        assert hideout.name == "Tiên Phủ Động Thiên (Hideout)"
        assert hideout.display_name == "Tiên Phủ Động Thiên (Hideout)"
        assert hideout.min_level == 1
        assert hideout.max_level == 100
        assert hideout.bounds_width == 800.0
        assert hideout.bounds_height == 800.0
        assert "wp_hideout_central" in hideout.waypoints
        assert "portal_hideout_to_sanctuary" in hideout.portals

    def test_outer_exploration_zones_are_open_world(self, zone_engine: ZoneEngine) -> None:
        """Verifies that wilderness maps are OPEN_WORLD where monsters roam."""
        bone_strand = zone_engine.get_zone("zone_tang_kiem_nhai")
        assert bone_strand.zone_type == ZoneType.OPEN_WORLD

        ancient_sword = zone_engine.get_zone("zone_ancient_sword_barrow")
        assert ancient_sword.zone_type == ZoneType.OPEN_WORLD

    def test_can_spawn_hostile_monsters_gate(self, zone_engine: ZoneEngine) -> None:
        """Verifies authoritative can_spawn_hostile_monsters rule."""
        # Safe Haven Hub and Hideout: ZERO hostile monsters allowed
        assert zone_engine.can_spawn_hostile_monsters("zone_boundless_sanctuary") is False
        assert zone_engine.can_spawn_hostile_monsters("zone_player_hideout") is False

        # Non-existent zone: Safely rejected
        assert zone_engine.can_spawn_hostile_monsters("invalid_unknown_zone") is False

        # Outer Wilderness Maps: Hostile monsters allowed
        assert zone_engine.can_spawn_hostile_monsters("zone_tang_kiem_nhai") is True
        assert zone_engine.can_spawn_hostile_monsters("zone_ancient_sword_barrow") is True
        assert zone_engine.can_spawn_hostile_monsters("zone_boundless_sandstorm") is True

    def test_validate_monster_spawn_enforcement(self, zone_engine: ZoneEngine) -> None:
        """Verifies validate_monster_spawn rejects hostile spawns in Sanctuary and Hideout."""
        # Hostile monster in Sanctuary -> REJECTED
        can_spawn_sanc, reason_sanc = zone_engine.validate_monster_spawn(
            zone_id="zone_boundless_sanctuary",
            is_dummy=False,
        )
        assert can_spawn_sanc is False
        assert "An Toàn" in reason_sanc or "Sanctuary" in reason_sanc

        # Hostile monster in Player Hideout -> REJECTED
        can_spawn_hideout, reason_hideout = zone_engine.validate_monster_spawn(
            zone_id="zone_player_hideout",
            is_dummy=False,
        )
        assert can_spawn_hideout is False
        assert "zone_player_hideout" in reason_hideout or "Sanctuary" in reason_hideout

        # Training dummy in Sanctuary -> ALLOWED
        can_dummy_sanc, reason_dummy_sanc = zone_engine.validate_monster_spawn(
            zone_id="zone_boundless_sanctuary",
            is_dummy=True,
        )
        assert can_dummy_sanc is True
        assert "Target Dummy" in reason_dummy_sanc or "Khôi Lỗi" in reason_dummy_sanc

        # Training dummy in Player Hideout -> ALLOWED
        can_dummy_hideout, reason_dummy_hideout = zone_engine.validate_monster_spawn(
            zone_id="zone_player_hideout",
            is_dummy=True,
        )
        assert can_dummy_hideout is True
        assert "Target Dummy" in reason_dummy_hideout or "Khôi Lỗi" in reason_dummy_hideout

        # Hostile monster in Outer Map -> ALLOWED
        can_wild, _ = zone_engine.validate_monster_spawn(
            zone_id="zone_tang_kiem_nhai",
            is_dummy=False,
        )
        assert can_wild is True

    def test_spawn_player_in_hideout_and_spatial_grid(self, zone_engine: ZoneEngine) -> None:
        """
        Verifies that spawning a player in the hideout succeeds without KeyError,
        sets valid player location coordinates, and guarantees hideout spatial grid initialization.
        """
        player_id = "player_hero_hideout_01"
        loc = zone_engine.spawn_player(player_id, "zone_player_hideout")
        assert loc is not None
        assert loc.player_id == player_id
        assert loc.zone_id == "zone_player_hideout"
        assert loc.x == 0.0
        assert loc.y == 0.0

        # Query player location via engine
        current_loc = zone_engine.get_player_location(player_id)
        assert current_loc.zone_id == "zone_player_hideout"
        assert current_loc.x == 0.0
        assert current_loc.y == 0.0

        # Verify hideout spatial grid exists and is isolated
        assert "zone_player_hideout" in zone_engine.zone_spatial_grids
        assert "zone_boundless_sanctuary" in zone_engine.zone_spatial_grids

        hideout_grid = zone_engine.zone_spatial_grids["zone_player_hideout"]
        sanctuary_grid = zone_engine.zone_spatial_grids["zone_boundless_sanctuary"]

        assert hideout_grid is not None
        assert isinstance(hideout_grid, SpatialGrid)
        assert hideout_grid is not sanctuary_grid
        assert hideout_grid.cell_size == 64.0

        # Verify cell coordinate projection
        cell_coords = hideout_grid.get_cell_coords(loc.x, loc.y)
        assert cell_coords == (0, 0)

    def test_spatial_grid_entity_isolation_hideout_vs_sanctuary(self, zone_engine: ZoneEngine) -> None:
        """
        Verifies that entities within the player hideout spatial grid are isolated
        from the sanctuary encampment and cannot be observed across zone boundaries.
        """
        hideout_grid = zone_engine.zone_spatial_grids["zone_player_hideout"]
        sanctuary_grid = zone_engine.zone_spatial_grids["zone_boundless_sanctuary"]

        dummy_entity = Entity(entity_id=8801, x=0.0, y=0.0)
        hideout_grid.add_entity(dummy_entity)

        assert 8801 in hideout_grid.entities
        assert 8801 not in sanctuary_grid.entities

        # Proximity query in hideout finds entity
        hideout_aoi = hideout_grid.get_entities_in_aoi(0.0, 0.0, radius_cells=1)
        assert 8801 in hideout_aoi

        # Proximity query in sanctuary does NOT find hideout entity
        sanctuary_aoi = sanctuary_grid.get_entities_in_aoi(0.0, 0.0, radius_cells=1)
        assert 8801 not in sanctuary_aoi

    def test_portal_traversal_sanctuary_hideout_roundtrip(self, zone_engine: ZoneEngine) -> None:
        """Verifies bidirectional portal traversal between Sanctuary and Hideout."""
        player_id = "wayfarer_hideout_traveler"
        zone_engine.spawn_player(player_id, "zone_boundless_sanctuary")

        ok, dest, _ = zone_engine.traverse_portal(player_id, "portal_sanctuary_to_hideout", player_level=1)
        assert ok is True
        assert dest == "zone_player_hideout"
        assert zone_engine.get_player_location(player_id).zone_id == "zone_player_hideout"

        ok_back, dest_back, _ = zone_engine.traverse_portal(
            player_id, "portal_hideout_to_sanctuary", player_level=1
        )
        assert ok_back is True
        assert dest_back == "zone_boundless_sanctuary"
        assert zone_engine.get_player_location(player_id).zone_id == "zone_boundless_sanctuary"

    def test_clean_get_zone_definition(self, zone_engine: ZoneEngine) -> None:
        """Verifies deduplicated get_zone_definition method behaves as expected."""
        hideout_def = zone_engine.get_zone_definition("zone_player_hideout")
        assert hideout_def is not None
        assert hideout_def.zone_type == ZoneType.SANCTUARY
        assert hideout_def.zone_id == "zone_player_hideout"

        sanctuary_def = zone_engine.get_zone_definition("zone_boundless_sanctuary")
        assert sanctuary_def is not None
        assert sanctuary_def.zone_type == ZoneType.SANCTUARY

        invalid_def = zone_engine.get_zone_definition("zone_unknown_missing")
        assert invalid_def is None

    def test_client_monster_system_js_no_hostile_mobs_in_hideout(self) -> None:
        """
        Regression test on client JavaScript engine:
        Guarantees that monster_system.js does NOT spawn hellhounds or skeletons
        in zone_player_hideout or zone_boundless_sanctuary.
        """
        js_path = Path("client/webapp/js/engine/monster_system.js")
        assert js_path.exists(), "monster_system.js must exist"
        content = js_path.read_text(encoding="utf-8")

        # Must not push hellhound_sanctuary or skeleton_sanctuary in safe zones
        assert "hellhound_sanctuary" not in content, (
            "CRITICAL BUG: hellhound_sanctuary must NOT exist in monster_system.js! "
            "Safe zones must have zero hostile monsters."
        )
        assert "skeleton_sanctuary" not in content, (
            "CRITICAL BUG: skeleton_sanctuary must NOT exist in monster_system.js! "
            "Safe zones must have zero hostile monsters."
        )

        # Must have dummy_primal_shrine as isDummy: true
        assert "isDummy: true" in content

    def test_client_spawner_blocks_hostile_in_safe_haven(self) -> None:
        """
        Verifies that client/webapp/js/engine/monster_system.js implements
        safe haven gating on spawnQAEnemy and spawnSpecificMonster with the exact
        warning notification.
        """
        content = Path("client/webapp/js/engine/monster_system.js").read_text(encoding="utf-8")
        assert "Nghiêm cấm sản sinh quái vật trong khu vực An Toàn" in content
        assert "isSafeHavenZone" in content
        assert "notifySafeHavenBlocked" in content

    def test_client_zone_transition_drop_and_vfx_flush(self) -> None:
        """
        Verifies that worldmap_dungeons.js flushes groundDrops and flying VFX projectiles
        upon zone teleportation.
        """
        content = Path("client/webapp/js/ui/worldmap_dungeons.js").read_text(encoding="utf-8")
        assert "clearGroundDrops" in content
        assert "clearVfxProjectiles" in content

    def test_client_monster_loot_dropper_clear_ground_drops(self) -> None:
        """
        Verifies that monster_loot_dropper.js exposes clearGroundDrops and blocks dummy loot.
        """
        content = Path("client/webapp/js/engine/monster_loot_dropper.js").read_text(encoding="utf-8")
        assert "clearGroundDrops" in content
        assert "monster.isDummy" in content

    def test_client_vfx_renderer_clear_vfx_projectiles(self) -> None:
        """
        Verifies that vfx_renderer.js exports clearVfxProjectiles to window.
        """
        content = Path("client/webapp/js/engine/vfx_renderer.js").read_text(encoding="utf-8")
        assert "clearVfxProjectiles" in content
        assert "window.clearVfxProjectiles = clearVfxProjectiles;" in content

