"""AutoPOE2 - DirectX 12 Hardware Cursor Raycast & Foreground Security Test Suite.
==================================================================================
[MÔ PHỎNG THUẬT TOÁN HEADLESS - KHÔNG PHẢI LIVE GAME] (Rule 5 Compliance)

Domain: QA & Governance Sentinel (Sư đoàn 4)
Kiểm thử bất biến INV-DX12-HARDWARE-RAYCAST & INV-FOCUS-INTERLOCK-HARDWARE-SAFETY (Doc 74):
1. Khi game unfocused: CẤM tuyệt đối di chuyển con trỏ phần cứng hoặc bấm chuột ra desktop
   (0 pixel displacement, 0 mouse event leak).
2. Khi game focused: Bắt buộc đồng bộ con trỏ phần cứng (SetCursorPos) kết hợp mouse_event
   cho các tương tác 3D world raycast (Map Device, Portal, Chest).
3. Cơ chế Alt-key bypass trong ensure_window_focus vượt qua ForegroundLockTimeout trên Windows 10/11.
4. Thẩm định định lượng qua MasterInvariantsSentinel.
"""

from __future__ import annotations

import ctypes
import os
from unittest.mock import MagicMock, call, patch
import pytest

from src.agentic_engine.master_invariants_sentinel import (
    MasterInvariantsSentinel,
    InvariantStatus,
)
from src.common.win32_input import (
    click,
    hover,
    set_cursor_pos,
    set_input_simulation_mode,
)
from src.common.win32_window import ensure_window_focus


# =============================================================================
# 1. TEST SUITE: UNFOCUSED STATE - HARDWARE CURSOR & MOUSE LOCKDOWN
# =============================================================================

class TestUnfocusedHardwareCursorLockdown:
    """Kiểm tra khóa chặn tuyệt đối con trỏ và chuột khi cửa sổ game mất tiêu điểm."""

    def test_unfocused_blocks_set_cursor_pos_zero_displacement(self):
        """Khi game unfocused: set_cursor_pos trả về False và tuyệt đối không gọi Win32 SetCursorPos."""
        with patch("src.common.win32_input._is_physical_allowed", return_value=False):
            with patch("src.common.win32_input._user32.SetCursorPos") as mock_set_pos:
                res = set_cursor_pos(1280, 720)
                assert res is False
                assert mock_set_pos.call_count == 0

    def test_unfocused_blocks_mouse_click_and_mouse_event(self):
        """Khi game unfocused: click() không phát bất kỳ mouse_event nào ra màn hình desktop."""
        with patch("src.common.win32_input.is_poe2_window_focused", return_value=False):
            with patch("src.common.win32_input._is_physical_allowed", return_value=False):
                with patch("src.common.win32_input._user32.mouse_event") as mock_mouse_event:
                    with patch("src.common.win32_input._user32.SetCursorPos") as mock_set_pos:
                        # Khi game mất focus -> click từ chối và trả về False
                        res = click(1150, 600, is_simulated=False)
                        assert res is False
                        assert mock_mouse_event.call_count == 0
                        assert mock_set_pos.call_count == 0

    def test_unfocused_blocks_hover_cursor_movement(self):
        """Khi game unfocused: hover() từ chối di chuyển con trỏ phần cứng."""
        with patch("src.common.win32_input.is_poe2_window_focused", return_value=False):
            with patch("src.common.win32_input._is_physical_allowed", return_value=False):
                with patch("src.common.win32_input.set_cursor_pos") as mock_set_pos:
                    res = hover(1000, 500, is_simulated=False)
                    assert res is False
                    assert mock_set_pos.call_count == 0


# =============================================================================
# 2. TEST SUITE: FOCUSED STATE - HARDWARE CURSOR 3D WORLD RAYCAST
# =============================================================================

class TestFocusedHardwareCursor3dRaycast:
    """Kiểm tra đồng bộ con trỏ phần cứng khi game focused để kích hoạt DX12 3D World Raycast."""

    def test_focused_synchronizes_hardware_cursor_and_sends_mouse_event(self):
        """Khi game focused: click(x, y) bắt buộc gọi SetCursorPos(x, y) và mouse_event(LEFTDOWN/UP)."""
        with patch("src.common.win32_input._is_focus_allowed", return_value=True):
            with patch("src.common.win32_input._is_physical_allowed", return_value=True):
                with patch("src.common.win32_input._user32.SetCursorPos", return_value=True) as mock_set_pos:
                    with patch("src.common.win32_input._user32.mouse_event") as mock_mouse_event:
                        with patch("time.sleep"):
                            res = click(1150, 600, button="left", hold_ms=0.05, delay_after=0.1)
                            assert res is True
                            # 1. Bắt buộc đồng bộ vị trí con trỏ phần cứng
                            mock_set_pos.assert_called_once_with(1150, 600)
                            # 2. Bắt buộc phát xung chuột MOUSEEVENTF_LEFTDOWN (0x02) và MOUSEEVENTF_LEFTUP (0x04)
                            assert mock_mouse_event.call_count == 2
                            calls = mock_mouse_event.call_args_list
                            assert calls[0][0][0] == 0x0002  # MOUSEEVENTF_LEFTDOWN
                            assert calls[1][0][0] == 0x0004  # MOUSEEVENTF_LEFTUP

    def test_map_device_3d_interaction_refuses_pure_postmessage_mock(self):
        """Quy chuẩn bất biến: Tương tác 3D Map Device từ chối chỉ dùng PostMessage đơn thuần."""
        sentinel = MasterInvariantsSentinel()

        # Khi chỉ dùng PostMessageW mà không đồng bộ con trỏ phần cứng -> BLOCKED
        res_fail = sentinel.audit_inv_dx12_hardware_raycast(
            is_focused=True,
            hardware_cursor_synced=False,
            mouse_event_sent=False,
            postmessage_only_for_3d_rejected=False,
        )
        assert res_fail.status == InvariantStatus.BLOCKED
        assert "Thiếu đồng bộ con trỏ phần cứng" in res_fail.evidence

        # Khi đồng bộ con trỏ phần cứng thật và phát mouse_event -> PASSED
        res_ok = sentinel.audit_inv_dx12_hardware_raycast(
            is_focused=True,
            hardware_cursor_synced=True,
            mouse_event_sent=True,
            postmessage_only_for_3d_rejected=True,
        )
        assert res_ok.status == InvariantStatus.PASSED
        assert "DirectX 12 Viewport Raycasting nhận diện chính xác" in res_ok.evidence


# =============================================================================
# 3. TEST SUITE: ALT-KEY FOREGROUND BYPASS INTERLOCK
# =============================================================================

class TestAltKeyForegroundBypassInterlock:
    """Kiểm tra cơ chế Alt-key bypass trong ensure_window_focus vượt qua ForegroundLockTimeout."""

    def test_ensure_window_focus_executes_alt_key_bypass_sequence(self):
        """ensure_window_focus thực hiện chuỗi Alt down -> SetForegroundWindow -> Alt up an toàn."""
        fake_hwnd = 0x12345678

        mock_user32 = MagicMock()
        mock_kernel32 = MagicMock()

        mock_user32.IsWindow.return_value = True
        mock_user32.IsIconic.return_value = False
        mock_user32.GetForegroundWindow.side_effect = [0x9999, fake_hwnd]
        mock_kernel32.GetCurrentThreadId.return_value = 1000

        with patch("ctypes.windll.user32", mock_user32):
            with patch("ctypes.windll.kernel32", mock_kernel32):
                with patch("src.common.win32_window.attach_to_window_desktop"):
                    with patch("src.common.win32_window.is_poe2_window_focused", return_value=True):
                        with patch("time.sleep"):
                            focused = ensure_window_focus(fake_hwnd, timeout_ms=100)
                            assert focused is True

                            # 1. Kiểm tra mở khóa LockSetForegroundWindow(2)
                            mock_user32.LockSetForegroundWindow.assert_called_with(2)

                            # 2. Kiểm tra chuỗi Alt keybd_event: Alt down (0) và Alt up (2)
                            keybd_calls = mock_user32.keybd_event.call_args_list
                            assert len(keybd_calls) >= 2
                            # Alt down: vk=0x12, flags=0
                            assert keybd_calls[0][0][0] == 0x12
                            assert keybd_calls[0][0][2] == 0
                            # Alt up: vk=0x12, flags=2 (KEYEVENTF_KEYUP)
                            assert keybd_calls[1][0][0] == 0x12
                            assert keybd_calls[1][0][2] == 2

                            # 3. Kiểm tra gọi BringWindowToTop và SetForegroundWindow
                            mock_user32.BringWindowToTop.assert_called_with(fake_hwnd)
                            mock_user32.SetForegroundWindow.assert_called_with(fake_hwnd)

    def test_ensure_window_focus_fails_safely_on_invalid_hwnd(self):
        """ensure_window_focus trả về False an toàn khi HWND = 0 hoặc không phải cửa sổ hợp lệ."""
        assert ensure_window_focus(0) is False

        mock_user32 = MagicMock()
        mock_user32.IsWindow.return_value = False
        with patch("ctypes.windll.user32", mock_user32):
            assert ensure_window_focus(0x9999) is False


# =============================================================================
# 4. TEST SUITE: MASTER INVARIANTS SENTINEL INTEGRATION FOR INV-DX12-HARDWARE-RAYCAST
# =============================================================================

class TestMasterSentinelInvDx12HardwareRaycast:
    """Kiểm tra tích hợp bất biến INV-DX12-HARDWARE-RAYCAST vào MasterInvariantsSentinel."""

    def test_sentinel_audit_inv_dx12_hardware_raycast_unfocused_protection(self):
        """Khi game unfocused: Nếu con trỏ bị dịch chuyển > 0px -> BLOCKED ngay lập tức."""
        sentinel = MasterInvariantsSentinel()

        # Bị dịch chuyển 15px ra desktop khi unfocused -> BLOCKED
        res_leak = sentinel.audit_inv_dx12_hardware_raycast(
            is_focused=False,
            cursor_displacement_on_unfocused=15,
        )
        assert res_leak.status == InvariantStatus.BLOCKED
        assert "Dịch chuyển con trỏ" in res_leak.evidence

        # 0px displacement khi unfocused -> PASSED
        res_safe = sentinel.audit_inv_dx12_hardware_raycast(
            is_focused=False,
            cursor_displacement_on_unfocused=0,
            mouse_event_sent=False,
        )
        assert res_safe.status == InvariantStatus.PASSED
        assert "0 pixel dịch chuyển con trỏ phần cứng" in res_safe.evidence

    def test_sentinel_audit_inv_dx12_hardware_raycast_focused_compliance(self):
        """Khi game focused: Tuân thủ đầy đủ tiêu chuẩn đồng bộ con trỏ và mouse_event -> PASSED."""
        sentinel = MasterInvariantsSentinel()

        res_ok = sentinel.audit_inv_dx12_hardware_raycast(
            is_focused=True,
            hardware_cursor_synced=True,
            mouse_event_sent=True,
            postmessage_only_for_3d_rejected=True,
        )
        assert res_ok.status == InvariantStatus.PASSED
        assert res_ok.code == "INV-DX12-HARDWARE-RAYCAST"
