"""
Adversarial Empirical Challenge Suite: Boss Gate & Server Spawn Validation.
Covers:
1. 1,000 randomized tile coordinates across real procedural maps:
   - 100% rejection on WALL, CHASM, WATER, BOSS_GATE, VOID.
   - 100% acceptance on FLOOR, PATH, POI, ENCOUNTER_*.
2. Safe Haven Purity (zone_player_hideout, zone_boundless_sanctuary):
   - 100% rejection of hostile spawns across 200 trials.
   - Explicit verification of target dummy exemption.
3. Server Boss Gate Lifecycle & Mutation:
   - BOSS_GATE spawn rejection, breach mutation, walkable flag.
4. Out-of-Bounds & Extreme Coordinate Fuzzing (negative, large, sys.maxsize).
5. Dual signature compatibility (positional vs keyword arguments).
"""

import random
import sys
import unittest

from server.world.map_data_types import TileType, MapGridData
from server.world.procedural_map_engine import ProceduralMapEngine
from server.world.zone_engine import ZoneEngine


class TestBossGateAndSpawnValidationAdversarial(unittest.TestCase):
    def setUp(self):
        self.engine = ZoneEngine()
        ProceduralMapEngine.clear_zone_maps()

    def test_01_randomized_coordinates_reject_impassable_tiles_1000_samples(self):
        """Generates actual maps and tests 1,000 randomized tile lookups."""
        zones = ["zone_tang_kiem_nhai", "zone_ancient_sword_barrow", "zone_boundless_sandstorm"]
        random.seed(20261001)

        impassable_count = 0
        passable_count = 0

        for zone_id in zones:
            map_data = ProceduralMapEngine.generate_wilderness_map(zone_id, seed=1337)
            ProceduralMapEngine.set_zone_map(zone_id, map_data)

            # Sample 350 coordinates per zone (total > 1,000)
            for _ in range(350):
                tx = random.randint(-5, map_data.width + 5)
                ty = random.randint(-5, map_data.height + 5)

                tile_type = ProceduralMapEngine.get_tile_type(zone_id, tx, ty)
                allowed, reason = self.engine.validate_monster_spawn(zone_id, tx=tx, ty=ty)

                if tile_type in (TileType.WALL, TileType.CHASM, TileType.WATER, TileType.BOSS_GATE, TileType.VOID) or not tile_type.is_passable():
                    self.assertFalse(allowed, f"Tile {tile_type.name} at ({tx}, {ty}) should be rejected!")
                    self.assertIn("không thể đi qua", reason)
                    impassable_count += 1
                else:
                    self.assertTrue(allowed, f"Tile {tile_type.name} at ({tx}, {ty}) should be allowed!")
                    self.assertIn("hợp lệ", reason)
                    passable_count += 1

        self.assertGreater(impassable_count, 500, "Should have tested significant impassable tiles")
        self.assertGreater(passable_count, 100, "Should have tested significant passable tiles")
        self.assertGreaterEqual(impassable_count + passable_count, 1000, "Must test at least 1,000 coordinates")

    def test_02_safe_haven_strict_purity_100_percent_rejection(self):
        """Verifies 100% rejection of hostile monster spawns in Safe Haven zones across 200 trials."""
        safe_zones = ["zone_player_hideout", "zone_boundless_sanctuary"]
        random.seed(9999)

        for zone_id in safe_zones:
            self.assertFalse(self.engine.can_spawn_hostile_monsters(zone_id))

            for _ in range(100):
                rx = random.uniform(-50.0, 50.0)
                ry = random.uniform(-50.0, 50.0)
                rtx = random.randint(-20, 50)
                rty = random.randint(-20, 50)

                # Test purely by zone
                allowed, msg = self.engine.validate_monster_spawn(zone_id, is_dummy=False)
                self.assertFalse(allowed)
                self.assertIn("Nghiêm cấm sản sinh quái vật trong khu vực An Toàn", msg)

                # Test with float coordinates
                allowed, msg = self.engine.validate_monster_spawn(zone_id, is_dummy=False, x=rx, y=ry)
                self.assertFalse(allowed)
                self.assertIn("Nghiêm cấm sản sinh quái vật trong khu vực An Toàn", msg)

                # Test with tile coordinates
                allowed, msg = self.engine.validate_monster_spawn(zone_id, is_dummy=False, tx=rtx, ty=rty)
                self.assertFalse(allowed)
                self.assertIn("Nghiêm cấm sản sinh quái vật trong khu vực An Toàn", msg)

                # Target dummy MUST be exempt
                dummy_allowed, dummy_msg = self.engine.validate_monster_spawn(zone_id, is_dummy=True, tx=rtx, ty=rty)
                self.assertTrue(dummy_allowed)
                self.assertIn("Khôi Lỗi Luyện Võ", dummy_msg)

    def test_03_boss_gate_server_lifecycle_and_mutation(self):
        """Verifies server-side boss gate tile blocking and breach mutation."""
        zone_id = "zone_tang_kiem_nhai"
        map_data = ProceduralMapEngine.generate_wilderness_map(zone_id, seed=777)
        ProceduralMapEngine.set_zone_map(zone_id, map_data)

        self.assertIsNotNone(map_data.boss_gate)
        gx, gy = map_data.boss_gate

        # Initially boss gate tile is impassable
        self.assertEqual(map_data.tiles[gy][gx].tile_type, TileType.BOSS_GATE)
        self.assertFalse(map_data.tiles[gy][gx].walkable)
        allowed, reason = self.engine.validate_monster_spawn(zone_id, tx=gx, ty=gy)
        self.assertFalse(allowed)
        self.assertIn("BOSS_GATE", reason)

        # Breach the boss gate
        breached = ProceduralMapEngine().breach_boss_gate(map_data)
        self.assertTrue(breached)
        self.assertTrue(map_data.boss_gate_breached)
        self.assertTrue(map_data.tiles[gy][gx].walkable)

        # Obstacle associated with gate is unblocked
        obs = map_data.obstacles.get((gx, gy))
        if obs:
            self.assertTrue(obs.is_destroyed)
            self.assertFalse(obs.blocks_movement)

    def test_04_adversarial_coordinate_fuzzing(self):
        """Fuzzes extreme, negative, and out-of-bounds coordinates."""
        zone_id = "zone_tang_kiem_nhai"
        map_data = ProceduralMapEngine.generate_wilderness_map(zone_id, seed=123)
        ProceduralMapEngine.set_zone_map(zone_id, map_data)

        extreme_coords = [
            (-1, -1),
            (-99999, 10),
            (10, -99999),
            (map_data.width, map_data.height),
            (map_data.width + 100, 5),
            (5, map_data.height + 100),
            (sys.maxsize, 0),
            (-sys.maxsize, 0),
            (0, sys.maxsize),
            (0, -sys.maxsize),
        ]

        for tx, ty in extreme_coords:
            # Out-of-bounds MUST return WALL
            tile_type = ProceduralMapEngine.get_tile_type(zone_id, tx, ty)
            self.assertEqual(tile_type, TileType.WALL)

            allowed, reason = self.engine.validate_monster_spawn(zone_id, tx=tx, ty=ty)
            self.assertFalse(allowed)
            self.assertIn("không thể đi qua", reason)

    def test_05_dual_invocation_signatures(self):
        """Ensures backward compatibility between positional and keyword calls."""
        zone_id = "zone_tang_kiem_nhai"
        map_data = ProceduralMapEngine.generate_wilderness_map(zone_id, seed=888)
        ProceduralMapEngine.set_zone_map(zone_id, map_data)

        # Positional dispatch style: validate_monster_spawn(zone_id, tx, ty)
        allowed_pos, _ = self.engine.validate_monster_spawn(zone_id, 0, 0)
        # Keyword style: validate_monster_spawn(zone_id, tx=0, ty=0)
        allowed_kw, _ = self.engine.validate_monster_spawn(zone_id, tx=0, ty=0)

        self.assertEqual(allowed_pos, allowed_kw)


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