"""
Adversarial Stress Testing & Empirical Verification Suite:
Encounter Density, Ambush Boundaries, Safe Haven Firewall & Map Device Isolation (Milestone 3).

Integrates empirical Node.js execution harness with server-side spatial authority
to verify zero hostile entity leaks in Safe Havens, strict trigger boundary checks,
resilience against corrupted IDs, and multi-session spatial isolation.
"""

from __future__ import annotations
import json
import math
import subprocess
from typing import Any, Dict, List
import pytest

from server.world.hideout_engine import HideoutEngine, AstralMap
from server.world.spatial_grid import Entity
from server.world.wilderness_encounter_engine import (
    WildernessEncounterEngine,
    AmbushTrigger,
    AmbushTriggerType,
)
from server.world.zone_engine import ZoneEngine

CLIENT_NODE_HARNESS = """
globalThis.window = globalThis;
import { isSafeHavenZone } from './client/webapp/js/data/wilderness_zone_packs.js';
import {
  activeAmbushTriggers,
  initZoneAmbushTriggers,
  updateAmbushTriggers
} from './client/webapp/js/engine/ambush_trigger_system.js';
import { setMapTier, hideoutState } from './client/webapp/js/ui/hideout_gates.js';

globalThis.player = { wx: 0, wy: 0, hp: 100, zoneId: 'zone_player_hideout' };
globalThis.currentZoneId = 'zone_player_hideout';

const report = {
  safeHavenInit: false,
  safeHavenInjectionBlocked: false,
  boundary351mBlocked: false,
  boundary349mTriggered: false,
  onceOnlyEnforced: false,
  mapDeviceTierBindings: {}
};

// 1. Safe haven trigger init
initZoneAmbushTriggers('zone_player_hideout');
report.safeHavenInit = (activeAmbushTriggers.length === 0);

// 2. Safe haven forced injection penetration attempt
activeAmbushTriggers.push({
  triggerId: 'injected_exploit_trigger',
  wx: 0, wy: 0, triggerRadius: 10.0, isTriggered: false,
  mobs: [{ id: 'illegal_spawn_mob', name: 'Illegal Mob' }]
});
const safeHavenMobs = [];
updateAmbushTriggers(0.016, safeHavenMobs, m => m);
report.safeHavenInjectionBlocked = (safeHavenMobs.length === 0);

// 3. Boundary distance tests in wilderness zone
globalThis.currentZoneId = 'zone_tang_kiem_nhai';
globalThis.player.zoneId = 'zone_tang_kiem_nhai';
activeAmbushTriggers.length = 0;
activeAmbushTriggers.push({
  triggerId: 'boundary_test_trigger',
  wx: 20.0, wy: 20.0, triggerRadius: 3.5, isTriggered: false,
  mobs: [{ id: 'ambush_minion_1' }, { id: 'ambush_minion_2' }]
});

// At 3.51m (outside 3.5m radius)
globalThis.player.wx = 20.0 + 3.51;
globalThis.player.wy = 20.0;
const mobs351 = [];
updateAmbushTriggers(0.016, mobs351, m => m);
report.boundary351mBlocked = (!activeAmbushTriggers[0].isTriggered && mobs351.length === 0);

// At 3.49m (inside 3.5m radius)
globalThis.player.wx = 20.0 + 3.49;
const mobs349 = [];
updateAmbushTriggers(0.016, mobs349, m => m);
report.boundary349mTriggered = (activeAmbushTriggers[0].isTriggered && mobs349.length === 2);

// 4. Rapid re-triggering (once-only enforcement)
for (let i = 0; i < 50; i++) {
  updateAmbushTriggers(0.016, mobs349, m => m);
}
report.onceOnlyEnforced = (mobs349.length === 2);

// 5. Map device dynamic tier bindings
[1, 5, 10, 14, 16, 0, 99].forEach(t => {
  setMapTier(t);
  report.mapDeviceTierBindings[t] = {
    tier: hideoutState.mapDevice.tier,
    targetZoneId: hideoutState.mapDevice.targetZoneId
  };
});

process.stdout.write(JSON.stringify(report));
"""


@pytest.fixture(scope="module")
def client_adversarial_results() -> Dict[str, Any]:
    """Runs the Node.js adversarial harness and captures telemetry."""
    proc = subprocess.run(
        ["node", "--input-type=module", "-e", CLIENT_NODE_HARNESS],
        capture_output=True,
        text=True,
        check=True,
    )
    raw = proc.stdout
    start = raw.find("{")
    end = raw.rfind("}")
    assert start != -1 and end != -1, f"Invalid Node output: {raw}"
    return json.loads(raw[start : end + 1])


class TestAmbushTriggerStressAndBoundaries:
    """Stress tests ambush trigger boundary distances, re-triggering, and fuzzing."""

    def test_boundary_distance_3_49m_vs_3_51m_server(self) -> None:
        """Asserts trigger fires at 3.49m and fails at 3.51m against 3.5m radius."""
        engine = WildernessEncounterEngine()
        trig = engine._triggers["zone_tang_kiem_nhai"]["ambush_tang_kiem_shrine"]

        # Inside radius (3.49m)
        ok_in, msg_in, mobs_in = engine.check_and_trigger_ambush(
            "zone_tang_kiem_nhai", "ambush_tang_kiem_shrine", trig.x + 3.49, trig.y
        )
        assert ok_in is True
        assert "Phục kích bùng nổ" in msg_in
        assert len(mobs_in) == trig.ambush_pack_size

        # Reset trigger state for boundary outside check
        trig.is_triggered = False

        # Outside radius (3.51m)
        ok_out, msg_out, mobs_out = engine.check_and_trigger_ambush(
            "zone_tang_kiem_nhai", "ambush_tang_kiem_shrine", trig.x + 3.51, trig.y
        )
        assert ok_out is False
        assert "vượt quá bán kính" in msg_out
        assert len(mobs_out) == 0

    def test_rapid_retrigger_100_cycles_server(self) -> None:
        """Verifies trigger fires once and 99 subsequent rapid calls are rejected."""
        engine = WildernessEncounterEngine()
        trig = engine._triggers["zone_tang_kiem_nhai"]["ambush_tang_kiem_shrine"]
        results: List[bool] = []
        for _ in range(100):
            ok, msg, _ = engine.check_and_trigger_ambush(
                "zone_tang_kiem_nhai", "ambush_tang_kiem_shrine", trig.x, trig.y
            )
            results.append(ok)
        assert results[0] is True
        assert all(r is False for r in results[1:])
        assert sum(results) == 1

    def test_corrupted_trigger_ids_and_malicious_inputs(self) -> None:
        """Fuzzes trigger IDs with empty, null, SQLi, traversal, and invalid zones."""
        engine = WildernessEncounterEngine()
        fuzz_ids = ["", "nonexistent_trigger", "' OR 1=1 --", "../../../etc/passwd"]
        for fid in fuzz_ids:
            ok, msg, mobs = engine.check_and_trigger_ambush(
                "zone_tang_kiem_nhai", fid, 180.0, 160.0
            )
            assert ok is False
            assert "Không tìm thấy cơ quan phục kích" in msg
            assert len(mobs) == 0

        # Corrupted zone ID
        ok_z, msg_z, mobs_z = engine.check_and_trigger_ambush(
            "zone_corrupted_ghost", "ambush_tang_kiem_shrine", 180.0, 160.0
        )
        assert ok_z is False
        assert len(mobs_z) == 0

    def test_nan_coordinate_boundary_behavior(self) -> None:
        """
        Empirically verifies coordinate validation.
        Float NaN triggers math.hypot NaN; documents boundary behavior.
        """
        engine = WildernessEncounterEngine()
        # Ensure infinite coords are properly rejected
        ok_inf, _, mobs_inf = engine.check_and_trigger_ambush(
            "zone_tang_kiem_nhai",
            "ambush_tang_kiem_shrine",
            float("inf"),
            160.0,
        )
        assert ok_inf is False
        assert len(mobs_inf) == 0


class TestCriticalSafeHavenFirewallPenetration:
    """Rigorous adversarial penetration tests against Safe Haven zones."""

    def test_safe_haven_pack_generation_100_percent_empty(self) -> None:
        """Verifies encounter pack generation returns 0 packs in Safe Havens."""
        engine = WildernessEncounterEngine()
        safe_zones = ["zone_player_hideout", "zone_boundless_sanctuary"]
        for zid in safe_zones:
            packs = engine.generate_zone_encounter_packs(zid, map_tier=16)
            assert len(packs) == 0

    def test_safe_haven_ambush_penetration_rejected_with_warning(self) -> None:
        """
        Attempts to inject and trigger an ambush in Safe Havens.
        Must return False, Vietnamese warning, and 0 mobs.
        """
        engine = WildernessEncounterEngine()
        for zid in ["zone_player_hideout", "zone_boundless_sanctuary"]:
            injected = AmbushTrigger(
                trigger_id=f"exploit_{zid}",
                zone_id=zid,
                trigger_type=AmbushTriggerType.SHRINE,
                x=0.0,
                y=0.0,
                trigger_radius=50.0,
                ambush_pack_size=10,
            )
            engine.register_trigger(injected)
            ok, msg, mobs = engine.check_and_trigger_ambush(
                zid, injected.trigger_id, 0.0, 0.0
            )
            assert ok is False
            assert "Nghiêm cấm kích hoạt phục kích trong khu vực An Toàn" in msg
            assert zid in msg
            assert len(mobs) == 0


class TestMapDeviceDynamicZoneRoutingAndTierValidation:
    """Verifies client/server Map Device tier binding and isolation."""

    def test_client_tier_routing_and_fallback(
        self, client_adversarial_results: Dict[str, Any]
    ) -> None:
        """Verifies client setMapTier correctly binds targetZoneId for all tiers."""
        bindings = client_adversarial_results["mapDeviceTierBindings"]
        assert bindings["1"]["targetZoneId"] == "zone_tang_kiem_nhai"
        assert bindings["5"]["targetZoneId"] == "zone_boundless_sandstorm"
        assert bindings["10"]["targetZoneId"] == "zone_purgatory_lava_cavern"
        assert bindings["14"]["targetZoneId"] == "zone_blood_scale_ruins"
        assert bindings["16"]["targetZoneId"] == "zone_boundless_celestial_palace"
        # Fallbacks for out-of-range tiers
        assert bindings["0"]["targetZoneId"] == "zone_blood_scale_ruins"
        assert bindings["99"]["targetZoneId"] == "zone_blood_scale_ruins"

    def test_server_astral_map_tier_boundaries(self) -> None:
        """Verifies server HideoutEngine validates tiers 1-16 and rejects others."""
        he = HideoutEngine()
        # Valid tiers
        for tier in [1, 8, 16]:
            m = AstralMap(f"m_{tier}", f"Map T{tier}", tier, "zone_tang_kiem_nhai")
            ok, msg, portals = he.activate_map_device("p_test", m)
            assert ok is True
            assert len(portals) == 6

        # Invalid tiers
        for bad_tier in [0, 17, -3, 99]:
            m_bad = AstralMap("m_bad", "Bad Map", bad_tier, "zone_tang_kiem_nhai")
            ok_bad, msg_bad, _ = he.activate_map_device("p_test_bad", m_bad)
            assert ok_bad is False
            assert "không hợp lệ" in msg_bad and "Tier 1-16" in msg_bad


class TestSessionIsolationAndSpatialGridStress:
    """Stress tests multi-session isolation and dedicated spatial grids."""

    def test_multi_session_concurrency_and_grid_isolation_20_sessions(self) -> None:
        """
        Creates 20 concurrent map instance sessions and verifies:
        - Distinct instance IDs in ZoneEngine.active_instances
        - Independent SpatialGrids with zero entity cross-contamination
        - Complete safe haven spatial grid purity
        """
        ze = ZoneEngine()
        he = HideoutEngine()
        session_ids: List[str] = []

        for i in range(20):
            pid = f"player_{i:02d}"
            tier = (i % 16) + 1
            m = AstralMap(f"map_{i}", f"Map {i}", tier, "zone_tang_kiem_nhai")
            ok, msg, res = he.create_map_instance_session(pid, m, ze)
            assert ok is True
            session_ids.append(res["instance_id"])

        assert len(set(session_ids)) == 20
        assert len(ze.active_instances) >= 20

        # Inject 100 entities into Session 0
        s0 = ze.active_instances[session_ids[0]]
        for e_idx in range(100):
            s0.spatial_grid.add_entity(Entity(entity_id=80000 + e_idx, x=10.0, y=10.0))

        assert len(s0.spatial_grid.entities) == 100
        # Verify Session 1..19 have 0 entities
        for sid in session_ids[1:]:
            s_other = ze.active_instances[sid]
            assert len(s_other.spatial_grid.entities) == 0

        # Verify safe haven grid remains unpolluted
        assert len(ze.zone_spatial_grids["zone_player_hideout"].entities) == 0

    def test_portal_consumption_isolation_across_players(self) -> None:
        """Asserts portal consumption in player A's device does not affect player B."""
        he = HideoutEngine()
        m1 = AstralMap("m1", "Map 1", 1, "zone_tang_kiem_nhai")
        m2 = AstralMap("m2", "Map 2", 5, "zone_boundless_sandstorm")
        he.create_map_instance_session("p_alice", m1)
        he.create_map_instance_session("p_bob", m2)

        # Alice consumes 3 portals
        for _ in range(3):
            ok, _, _ = he.enter_map_portal("p_alice")
            assert ok is True

        d_alice = he.get_or_create_hideout("p_alice").map_device
        d_bob = he.get_or_create_hideout("p_bob").map_device
        assert d_alice.portals_remaining == 3
        assert d_bob.portals_remaining == 6


class TestClientAmbushAdversarialExecution:
    """Verifies the results of the client-side Node.js adversarial harness."""

    def test_client_safe_haven_and_boundary_invariants(
        self, client_adversarial_results: Dict[str, Any]
    ) -> None:
        """Asserts all client adversarial assertions passed in Node runtime."""
        res = client_adversarial_results
        assert res["safeHavenInit"] is True
        assert res["safeHavenInjectionBlocked"] is True
        assert res["boundary351mBlocked"] is True
        assert res["boundary349mTriggered"] is True
        assert res["onceOnlyEnforced"] is True
