"""
Unit Test Suite for FreeExile Anti-Fog Memory Tampering & Anti-Exploit Verifier.
Validates:
1. Fog Hash Challenge-Response (HMAC-SHA256 integrity verification).
2. Detection of Client RAM tampering (hacker forcing fog_matrix to VISIBLE).
3. Server-Authoritative Action Line-of-Sight (LoS) Sanity Validation.
4. Detection of out-of-vision targeting and wall-penetrating clicks.
5. Integration with Independent Security Division (Audit telemetry & flagging).
"""

import pytest
import hmac
import hashlib
from typing import Tuple

from server.world.map_data_types import (
    FogState,
    MapGridData,
    PlayerVisibilityState,
)
from server.world.war_fog_engine import WarFogEngine
from server.world.procedural_map_engine import ProceduralMapEngine
from server.security.fog_integrity_verifier import (
    FogIntegrityVerifier,
    FogTamperViolationException,
)


class TestFogIntegrityAndMemoryTamperDetection:
    @pytest.fixture
    def setup_system(self):
        engine = ProceduralMapEngine(width=32, height=32)
        map_data = engine.generate_map(seed=12345, biome_id="BLEACHED_BONE_CANYON")
        fog_engine = WarFogEngine()
        vis = fog_engine.create_player_visibility(player_id="player_honest", width=32, height=32)
        sx, sy = map_data.spawn_point
        fog_engine.update_visibility(vis, map_data, player_x=sx, player_y=sy, view_radius=8)
        verifier = FogIntegrityVerifier()
        return map_data, fog_engine, vis, verifier

    def test_honest_client_fog_hash_passes(self, setup_system):
        """Honest client computing hash matching server fog_matrix succeeds and consumes challenge."""
        map_data, fog_engine, vis, verifier = setup_system
        nonce = verifier.generate_fog_challenge(vis.player_id)
        
        # Client computes correct hash
        expected_hash = verifier.compute_expected_fog_hash(vis, nonce)
        is_valid = verifier.verify_client_fog_hash(vis, expected_hash, nonce)
        assert is_valid is True

        # Challenge nonce is consumed upon verification; replay without new challenge must fail
        is_replay_valid = verifier.verify_client_fog_hash(vis, expected_hash)
        assert is_replay_valid is False

    def test_ram_tampered_client_fog_hash_fails(self, setup_system):
        """Hacker modifying RAM to remove fog of war produces hash mismatch and is flagged."""
        map_data, fog_engine, vis, verifier = setup_system
        nonce = verifier.generate_fog_challenge(vis.player_id)

        # Hacker tampers client fog matrix (sets all tiles to VISIBLE=2)
        tampered_matrix = [[FogState.VISIBLE.value for _ in range(32)] for _ in range(32)]
        tampered_bytes = bytes([cell for row in tampered_matrix for cell in row])
        hacker_hash = hmac.new(nonce.encode("utf-8"), tampered_bytes, hashlib.sha256).hexdigest()

        # Server verifies against authoritative server matrix
        is_valid = verifier.verify_client_fog_hash(vis, hacker_hash, nonce)
        assert is_valid is False

    def test_action_target_inside_los_allowed(self, setup_system):
        """Actions targeting entities inside VISIBLE Line-of-Sight are legitimate."""
        map_data, fog_engine, vis, verifier = setup_system
        sx, sy = map_data.spawn_point

        # Target adjacent cell that is VISIBLE
        target_pos = (sx + 1, sy)
        vis.fog_matrix[sy][sx + 1] = FogState.VISIBLE.value

        is_allowed, reason = verifier.validate_player_action_los(
            vis, map_data, player_pos=(sx, sy), target_pos=target_pos
        )
        assert is_allowed is True
        assert reason == "IN_LINE_OF_SIGHT"

    def test_action_target_in_fog_or_unexplored_blocked(self, setup_system):
        """Actions targeting monsters hidden in fog/unexplored are blocked with exploit flag."""
        map_data, fog_engine, vis, verifier = setup_system
        sx, sy = map_data.spawn_point
        bx, by = map_data.boss_point  # Far away in fog

        # Ensure boss point is UNEXPLORED or EXPLORED_FOGGED
        assert vis.fog_matrix[by][bx] != FogState.VISIBLE.value

        is_allowed, reason = verifier.validate_player_action_los(
            vis, map_data, player_pos=(sx, sy), target_pos=(bx, by)
        )
        assert is_allowed is False
        assert "TARGET_NOT_VISIBLE_IN_FOG" in reason

    def test_action_target_behind_solid_wall_blocked(self, setup_system):
        """Actions targeting through solid walls are blocked as wall-hack exploit."""
        map_data, fog_engine, vis, verifier = setup_system
        sx, sy = map_data.spawn_point

        # Find a wall coordinate
        wall_pos = None
        for y in range(32):
            for x in range(32):
                if map_data.blocks_vision(x, y):
                    wall_pos = (x, y)
                    break
            if wall_pos:
                break

        assert wall_pos is not None
        # Even if hacker modifies client visibility to mark wall VISIBLE, server checks obstacle
        is_allowed, reason = verifier.validate_player_action_los(
            vis, map_data, player_pos=(sx, sy), target_pos=wall_pos
        )
        assert is_allowed is False
