"""
Unit tests for Requirement R1: Wilderness Map Expansion in FreeExile.
Verifies that:
1. All 9 wilderness zones have their bounds doubled from original specs.
2. The initial wilderness zone (zone_tang_kiem_nhai) has bounds >= 1600.0 x 1600.0.
3. Safe Haven / Sanctuary zones (zone_boundless_sanctuary, zone_player_hideout)
   remain strictly at 800.0 x 800.0.
4. Server-authoritative movement engine (ZoneEngine.update_player_position)
   correctly permits player navigation into the expanded coordinates (e.g. x=900.0)
   and clamps/rejects movements beyond the new half-extents.
5. Central Game Design Matrix SQLite database reflects the expanded dimensions.
"""

from pathlib import Path
import sqlite3
import pytest

from server.world.zone_engine import ZoneEngine
from server.world.zone_types import ZoneType


EXPECTED_WILDERNESS_BOUNDS: dict[str, tuple[float, float, float, float]] = {
    # zone_id: (baseline_w, baseline_h, expected_doubled_w, expected_doubled_h)
    "zone_tang_kiem_nhai": (1000.0, 1000.0, 2000.0, 2000.0),
    "zone_ancient_sword_barrow": (1200.0, 1200.0, 2400.0, 2400.0),
    "zone_boundless_sandstorm": (1500.0, 1500.0, 3000.0, 3000.0),
    "zone_blood_scale_ruins": (1600.0, 1600.0, 3200.0, 3200.0),
    "zone_five_elements_altar": (1800.0, 1800.0, 3600.0, 3600.0),
    "zone_abyssal_ice_pond": (2000.0, 2000.0, 4000.0, 4000.0),
    "zone_infinite_blood_rift": (2200.0, 2200.0, 4400.0, 4400.0),
    "zone_purgatory_lava_cavern": (2400.0, 2400.0, 4800.0, 4800.0),
    "zone_boundless_celestial_palace": (2500.0, 2500.0, 5000.0, 5000.0),
}


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


class TestZoneBoundsExpansion:
    """Validates wilderness zone expansion across server engine, catalog, and database."""

    def test_tang_kiem_nhai_exceeds_minimum_expanded_threshold(
        self, zone_engine: ZoneEngine
    ) -> None:
        """Verifies zone_tang_kiem_nhai meets or exceeds the 1600.0 x 1600.0 requirement."""
        zone = zone_engine.get_zone("zone_tang_kiem_nhai")
        assert zone is not None
        assert zone.bounds_width >= 1600.0
        assert zone.bounds_height >= 1600.0
        assert zone.bounds_width == 2000.0
        assert zone.bounds_height == 2000.0

    def test_all_nine_wilderness_zones_doubled(
        self, zone_engine: ZoneEngine
    ) -> None:
        """Verifies that all 9 wilderness zones have their width and height doubled."""
        for zone_id, (base_w, base_h, exp_w, exp_h) in EXPECTED_WILDERNESS_BOUNDS.items():
            zone = zone_engine.get_zone(zone_id)
            assert zone is not None, f"Zone '{zone_id}' not found in ZoneEngine"
            assert zone.zone_type == ZoneType.OPEN_WORLD
            assert zone.bounds_width == exp_w, (
                f"Zone '{zone_id}' width {zone.bounds_width} != expected {exp_w} (2x {base_w})"
            )
            assert zone.bounds_height == exp_h, (
                f"Zone '{zone_id}' height {zone.bounds_height} != expected {exp_h} (2x {base_h})"
            )

    def test_sanctuary_zones_remain_strictly_800(
        self, zone_engine: ZoneEngine
    ) -> None:
        """Ensures sanctuary hubs and hideouts are never expanded beyond 800.0 x 800.0."""
        sanctuary = zone_engine.get_zone("zone_boundless_sanctuary")
        assert sanctuary is not None
        assert sanctuary.zone_type == ZoneType.SANCTUARY
        assert sanctuary.bounds_width == 800.0
        assert sanctuary.bounds_height == 800.0

        hideout = zone_engine.get_zone("zone_player_hideout")
        assert hideout is not None
        assert hideout.zone_type == ZoneType.SANCTUARY
        assert hideout.bounds_width == 800.0
        assert hideout.bounds_height == 800.0

    def test_movement_engine_allows_expanded_coordinates_in_tang_kiem(
        self, zone_engine: ZoneEngine
    ) -> None:
        """Tests that player movement allows coordinates in expanded half-bounds (up to 1000.0)."""
        player_id = "test_wanderer_01"
        zone_engine.spawn_player(player_id, "zone_tang_kiem_nhai")

        # 900.0 is beyond the old half-bound of 500.0, but within the new 1000.0 half-bound
        success, err = zone_engine.update_player_position(player_id, 900.0, 900.0)
        assert success is True
        assert err == ""

        loc = zone_engine.get_player_location(player_id)
        assert loc.x == 900.0
        assert loc.y == 900.0

        # Negative coordinate in expanded range
        neg_ok, neg_err = zone_engine.update_player_position(player_id, -950.0, -850.0)
        assert neg_ok is True
        assert neg_err == ""

        # Coordinate strictly exceeding the new 1000.0 half-bound must be rejected
        exceed_ok, exceed_err = zone_engine.update_player_position(
            player_id, 1050.0, 50.0
        )
        assert exceed_ok is False
        assert "Vượt quá ranh giới bản đồ" in exceed_err

    def test_movement_engine_respects_sanctuary_800_bounds(
        self, zone_engine: ZoneEngine
    ) -> None:
        """Verifies that sanctuary movement boundary remains strictly at half-bound 400.0."""
        player_id = "test_wanderer_sanctuary"
        zone_engine.spawn_player(player_id, "zone_boundless_sanctuary")

        # Inside 400.0 half-bound
        ok, err = zone_engine.update_player_position(player_id, 350.0, 350.0)
        assert ok is True
        assert err == ""

        # Outside 400.0 half-bound
        blocked, block_err = zone_engine.update_player_position(player_id, 450.0, 0.0)
        assert blocked is False
        assert "Vượt quá ranh giới bản đồ" in block_err

    def test_movement_engine_in_highest_tier_wilderness(
        self, zone_engine: ZoneEngine
    ) -> None:
        """Verifies bounds in zone_boundless_celestial_palace (5000.0 x 5000.0, half-bound 2500.0)."""
        player_id = "test_wanderer_celestial"
        zone_engine.spawn_player(player_id, "zone_boundless_celestial_palace")

        # Position at 2400.0 (beyond old 1250.0 half-extent, valid in 2500.0)
        ok, err = zone_engine.update_player_position(player_id, 2400.0, -2400.0)
        assert ok is True
        assert err == ""

        # Position at 2600.0 (exceeds 2500.0)
        blocked, block_err = zone_engine.update_player_position(player_id, 2600.0, 0.0)
        assert blocked is False
        assert "Vượt quá ranh giới bản đồ" in block_err

    def test_database_matrix_sync_reflects_expanded_bounds(self) -> None:
        """Verifies that data/game_design_matrix.db contains the synchronized expanded bounds."""
        db_path = Path("data/game_design_matrix.db")
        assert db_path.exists(), "game_design_matrix.db must exist"

        conn = sqlite3.connect(str(db_path))
        try:
            cursor = conn.cursor()
            rows = cursor.execute(
                "SELECT zone_id, bounds_width, bounds_height FROM zones"
            ).fetchall()
            bounds_by_zone = {row[0]: (float(row[1]), float(row[2])) for row in rows}

            # Sanctuaries
            assert bounds_by_zone["zone_boundless_sanctuary"] == (800.0, 800.0)
            assert bounds_by_zone["zone_player_hideout"] == (800.0, 800.0)

            # Wilderness
            for zone_id, (_, _, exp_w, exp_h) in EXPECTED_WILDERNESS_BOUNDS.items():
                assert zone_id in bounds_by_zone
                assert bounds_by_zone[zone_id] == (exp_w, exp_h), (
                    f"DB bounds for {zone_id}: {bounds_by_zone[zone_id]} != expected {(exp_w, exp_h)}"
                )
        finally:
            conn.close()

    def test_client_collision_engine_has_expanded_bounds(self) -> None:
        """Verifies that client/webapp/js/engine/collision_engine.js has expanded bounds."""
        collision_file = Path("client/webapp/js/engine/collision_engine.js")
        assert collision_file.exists()
        content = collision_file.read_text(encoding="utf-8")

        # Verify zone_tang_kiem_nhai expanded bounds
        assert "minWx: -22.0, maxWx: 22.0" in content
        assert "minWy: -17.0, maxWy: 17.0" in content
        assert "minCamWx: -16.5, maxCamWx: 16.5" in content
        assert "minCamWy: -12.5, maxCamWy: 12.5" in content

        # Verify zone_ancient_sword_barrow expanded bounds
        assert "minWx: -20.0, maxWx: 20.0" in content
        assert "minCamWx: -15.0, maxCamWx: 15.0" in content

        # Verify sanctuary bounds
        assert "minWx: -45.0, maxWx: 45.0" in content
        assert "minCamWx: -40.0, maxCamWx: 40.0" in content

    def test_client_world_renderer_centers_ambient_tiles_on_camera(self) -> None:
        """Verifies that client/webapp/js/engine/world_renderer.js centers tile loop on camera."""
        renderer_file = Path("client/webapp/js/engine/world_renderer.js")
        assert renderer_file.exists()
        content = renderer_file.read_text(encoding="utf-8")

        assert "camWx - tileRadius" in content
        assert "camWy - tileRadius" in content

