"""
Empirical Challenger Test Suite for Milestone 1 (R1: Wilderness Map Expansion).
Tests all 11 zones, SQLite database values, boundary physics, ambient tile math, and file hygiene.
"""

from pathlib import Path
import sqlite3
import pytest

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


BASELINE_AND_EXPANDED_SPECS = {
    # zone_id: (is_sanctuary, expected_w, expected_h)
    "zone_boundless_sanctuary": (True, 800.0, 800.0),
    "zone_player_hideout": (True, 800.0, 800.0),
    "zone_tang_kiem_nhai": (False, 2000.0, 2000.0),
    "zone_ancient_sword_barrow": (False, 2400.0, 2400.0),
    "zone_boundless_sandstorm": (False, 3000.0, 3000.0),
    "zone_blood_scale_ruins": (False, 3200.0, 3200.0),
    "zone_five_elements_altar": (False, 3600.0, 3600.0),
    "zone_abyssal_ice_pond": (False, 4000.0, 4000.0),
    "zone_infinite_blood_rift": (False, 4400.0, 4400.0),
    "zone_purgatory_lava_cavern": (False, 4800.0, 4800.0),
    "zone_boundless_celestial_palace": (False, 5000.0, 5000.0),
}


class TestChallengerMilestone1:
    @pytest.fixture(autouse=True)
    def setup_engine(self) -> None:
        self.engine = ZoneEngine()

    def test_zone_counts_and_type_distribution(self) -> None:
        """Verify exactly 11 zones: 2 sanctuaries and 9 wilderness."""
        zones = list(self.engine.zones.values())
        assert len(zones) == 11, f"Expected 11 zones, found {len(zones)}"

        sanctuaries = [z for z in zones if z.zone_type == ZoneType.SANCTUARY]
        wilderness = [z for z in zones if z.zone_type == ZoneType.OPEN_WORLD]

        assert len(sanctuaries) == 2, f"Expected 2 sanctuaries, found {len(sanctuaries)}"
        assert len(wilderness) == 9, f"Expected 9 wilderness zones, found {len(wilderness)}"

        sanctuary_ids = {z.zone_id for z in sanctuaries}
        assert sanctuary_ids == {"zone_boundless_sanctuary", "zone_player_hideout"}

    def test_all_11_zones_exact_dimensions(self) -> None:
        """Verify exact dimensions for all 11 zones."""
        for zone_id, (is_sanctuary, exp_w, exp_h) in BASELINE_AND_EXPANDED_SPECS.items():
            zone = self.engine.get_zone(zone_id)
            assert zone is not None, f"Missing zone {zone_id}"
            if is_sanctuary:
                assert zone.zone_type == ZoneType.SANCTUARY
                assert (zone.bounds_width, zone.bounds_height) == (800.0, 800.0)
            else:
                assert zone.zone_type == ZoneType.OPEN_WORLD
                assert (zone.bounds_width, zone.bounds_height) == (exp_w, exp_h)
                if zone_id == "zone_tang_kiem_nhai":
                    assert zone.bounds_width >= 1600.0 and zone.bounds_height >= 1600.0

    def test_waypoints_and_portals_within_bounds(self) -> None:
        """Adversarial check: ensure no waypoints or portals are outside expanded bounds."""
        for zone in self.engine.zones.values():
            half_w = zone.bounds_width / 2.0
            half_h = zone.bounds_height / 2.0

            for wp_id, wp in zone.waypoints.items():
                assert abs(wp.x) <= half_w, f"Waypoint {wp_id} x={wp.x} outside half-width {half_w}"
                assert abs(wp.y) <= half_h, f"Waypoint {wp_id} y={wp.y} outside half-height {half_h}"

            for portal_id, portal in zone.portals.items():
                assert abs(portal.source_x) <= half_w, f"Portal {portal_id} src_x outside {half_w}"
                assert abs(portal.source_y) <= half_h, f"Portal {portal_id} src_y outside {half_h}"

    def test_sqlite_matrix_db_invariants(self) -> None:
        """Empirically query data/game_design_matrix.db and assert exact dimensions."""
        db_path = Path("data/game_design_matrix.db")
        assert db_path.exists(), "game_design_matrix.db missing"

        conn = sqlite3.connect(str(db_path))
        try:
            cursor = conn.cursor()
            rows = cursor.execute(
                "SELECT zone_id, zone_type, bounds_width, bounds_height FROM zones"
            ).fetchall()
            assert len(rows) == 11, f"Expected 11 rows in zones table, got {len(rows)}"

            db_map = {r[0]: (r[1], float(r[2]), float(r[3])) for r in rows}
            for zone_id, (is_sanctuary, exp_w, exp_h) in BASELINE_AND_EXPANDED_SPECS.items():
                assert zone_id in db_map, f"Zone {zone_id} missing from SQLite DB"
                z_type, w, h = db_map[zone_id]
                expected_type = "SANCTUARY" if is_sanctuary else "OPEN_WORLD"
                assert z_type == expected_type, f"Zone {zone_id} type mismatch: {z_type} vs {expected_type}"
                assert (w, h) == (exp_w, exp_h), f"Zone {zone_id} DB bounds mismatch: ({w},{h}) vs ({exp_w},{exp_h})"
        finally:
            conn.close()

    def test_hygiene_line_limits(self) -> None:
        """Verify strict line limits across affected files."""
        limits = {
            Path("server/world/zone_catalog.py"): 700,
            Path("client/webapp/js/engine/collision_engine.js"): 500,
            Path("client/webapp/js/engine/world_renderer.js"): 500,
            Path("tests/unit/test_zone_bounds_expansion.py"): 500,
        }
        for file_path, max_lines in limits.items():
            assert file_path.exists(), f"File {file_path} not found"
            lines = len(file_path.read_text(encoding="utf-8").splitlines())
            assert lines <= max_lines, f"{file_path} has {lines} lines, exceeding limit {max_lines}"

    def test_ambient_tile_math_stress(self) -> None:
        """Stress-test ambient tile mathematics for extreme camera positions."""
        tile_w = 64
        tile_h = 32
        viewport_w = 1920
        viewport_h = 1080
        cx = viewport_w / 2.0
        cy = viewport_h / 2.0

        def world_to_iso(wx: float, wy: float, cam_x: float, cam_y: float) -> tuple[float, float]:
            rel_wx = wx - cam_x
            rel_wy = wy - cam_y
            return (
                (rel_wx - rel_wy) * (tile_w / 2.0) + cx,
                (rel_wx + rel_wy) * (tile_h / 2.0) + cy,
            )

        test_cameras = [
            (0.0, 0.0),
            (-1000000.0, 1000000.0),
            (1e7, -1e7),
            (-22.0, 17.0),
            (2500.0, 2500.0),
            (-2500.0, -2500.0),
        ]

        tile_radius = int((max(viewport_w, viewport_h) / (tile_h * 2)) + 2)

        for cam_x, cam_y in test_cameras:
            cam_wx = round(cam_x)
            cam_wy = round(cam_y)
            drawn = 0
            culled = 0
            for x in range(cam_wx - tile_radius, cam_wx + tile_radius + 1):
                for y in range(cam_wy - tile_radius, cam_wy + tile_radius + 1):
                    pt_x, pt_y = world_to_iso(x, y, cam_x, cam_y)
                    if (
                        pt_x < -tile_w
                        or pt_x > viewport_w + tile_w
                        or pt_y < -tile_h
                        or pt_y > viewport_h + tile_h
                    ):
                        culled += 1
                        continue
                    drawn += 1

            assert drawn > 0, f"Expected visible tiles at camera ({cam_x}, {cam_y})"
            assert drawn + culled == (2 * tile_radius + 1) ** 2
