"""AutoPOE2 - Unit & Integration Tests cho Reconnect Invariants & Anti-False-Positive.
=====================================================================================
Domain: QA & Governance Sentinel (Sư đoàn 4)
Tuân thủ nghiêm ngặt Master Invariant Matrix (Rule 8) & Rule 4C/5:
- INV-RECONNECT-PROCESS-ALIVE: Client PID phải tồn tại và đang chạy trên Windows.
- INV-RECONNECT-HWND-VALID: HWND phải hợp lệ (win32gui.IsWindow).
- INV-RECONNECT-NO-HARDCODED-VITALS: Cấm gán cứng chỉ số sinh tồn (es=4284, hp=1, mana=897)
  khi bộ nhớ chưa gắn kết (memory_attached=False) hoặc player.address == 0x0.
- INV-CAPTURE-CLIENT-ONLY: ScreenCapturer cấm fallback chụp ảnh toàn màn hình Desktop khi không có window.
- Khép kín vòng lặp tự sửa lỗi (Rule 6): Incident Dossier INCIDENT_20260918_RECONNECT_FALSE_POSITIVE.
"""

from __future__ import annotations

import json
import os
import shutil
import tempfile
from unittest.mock import MagicMock, patch

import pytest
from PIL import Image

from src.assistant_tool.reconnect_validator import (
    HARDCODED_PROHIBITED_VITALS,
    ReconnectValidationResult,
    validate_reconnect_result,
)
from src.assistant_tool.screen_capturer import ScreenCapturer


@pytest.fixture
def temp_output_dir():
    """Tạo thư mục tạm cô lập cho ScreenCapturer tests."""
    temp_dir = tempfile.mkdtemp(prefix="capturer_inv_test_")
    yield temp_dir
    shutil.rmtree(temp_dir, ignore_errors=True)


class TestReconnectResultAudit:
    """Kiểm toán đối soát chéo artifact hiện trường sự cố và file kết quả live."""

    def test_audit_current_reconnect_result_rejects_false_positive(self):
        """[MÔ PHỎNG KIỂM TOÁN THỰC NGHIỆM TỪ INCIDENT DOSSIER]
        Đọc artifact reconnect_result_false_positive.json và artifact RAM tại captures/reconnect/.
        Xác nhận hệ thống phát hiện chính xác vi phạm False Positive (Rule 4C & Rule 5).
        """
        result_file = os.path.join(
            "debug_harness", "incidents", "INCIDENT_20260918_RECONNECT_FALSE_POSITIVE",
            "reconnect_result_false_positive.json"
        )
        assert os.path.exists(result_file), f"Thiếu file {result_file} trong incident dossier"

        with open(result_file, "r", encoding="utf-8") as f:
            res_data = json.load(f)

        ram_artifact = os.path.join(
            "captures", "reconnect", "20260918_070449_424_RECONNECT_STEP4_IN_GAME_RAM.json"
        )
        assert os.path.exists(ram_artifact), f"Thiếu artifact RAM {ram_artifact}"

        with open(ram_artifact, "r", encoding="utf-8") as f:
            ram_data = json.load(f)

        # Chạy kiểm định validator trên artifact sự cố
        val_result = validate_reconnect_result(
            result_data=res_data,
            ram_snapshot=ram_data,
            check_live_process=True,
            check_live_hwnd=False,
        )

        # Kết quả bắt buộc phải bị REJECT (is_valid = False)
        assert not val_result.is_valid, "Validator phải REJECT file reconnect_result.json giả mạo!"
        assert len(val_result.errors) >= 1

        # Xác nhận bắt đúng lỗi hardcoded vitals vi phạm Rule 4C / Rule 5
        errors_str = " ".join(val_result.errors)
        assert "INV-RECONNECT-NO-HARDCODED-VITALS" in errors_str
        assert "4284" in errors_str

    def test_audit_live_reconnect_result_file_integrity(self):
        """[KIỂM TOÁN DỮ LIỆU THẬT 100% TRÊN WORKSPACE]
        Kiểm tra file reconnect_result.json hiện tại trên workspace:
        Nếu client thật đang chạy (PID 22512), xác nhận không còn gán cứng ES 4284 ở root.
        """
        result_file = "reconnect_result.json"
        if not os.path.exists(result_file):
            pytest.skip("reconnect_result.json chưa được tạo")

        with open(result_file, "r", encoding="utf-8") as f:
            res_data = json.load(f)

        # Xác nhận đã loại bỏ gán cứng root template (es=4284, hp=1, mana=897)
        assert not (res_data.get("es") == 4284 and res_data.get("hp") == 1 and res_data.get("mana") == 897), \
            "reconnect_result.json vẫn còn chứa hằng số gán cứng vi phạm Rule 4C!"


class TestReconnectValidatorUnit:
    """Kiểm thử đơn vị chi tiết từng quy tắc bất biến trong reconnect_validator."""

    def test_validator_rejects_missing_schema_fields(self):
        """Result thiếu các trường cốt lõi (status, pid, hwnd) bị từ chối ngay lập tức."""
        incomplete_data = {"status": "SUCCESS", "zone": "Shoreline Hideout"}
        res = validate_reconnect_result(incomplete_data, check_live_process=False, check_live_hwnd=False)
        assert not res.is_valid
        assert any("Missing required field" in err for err in res.errors)

    def test_validator_rejects_dead_process_id(self):
        """INV-RECONNECT-PROCESS-ALIVE: PID không tồn tại trong Windows process table bị từ chối."""
        fake_data = {
            "status": "SUCCESS",
            "pid": 99999999,  # PID siêu lớn chắc chắn không tồn tại
            "hwnd": "0x12345",
            "es": 100,
            "hp": 100,
            "mana": 100,
        }
        res = validate_reconnect_result(fake_data, check_live_process=True, check_live_hwnd=False)
        assert not res.is_valid
        assert any("INV-RECONNECT-PROCESS-ALIVE" in err for err in res.errors)
        assert any("does not exist in Windows process table" in err for err in res.errors)

    def test_validator_rejects_hardcoded_vitals_when_ram_unattached(self):
        """INV-RECONNECT-NO-HARDCODED-VITALS: Cấm gán cứng template CI khi RAM unattached hoặc address 0x0."""
        # Dùng PID của tiến trình python hiện tại để vượt qua kiểm tra PID sống
        curr_pid = os.getpid()

        fake_result = {
            "status": "SUCCESS",
            "pid": curr_pid,
            "hwnd": "0x1000",
            "es": 4284,
            "hp": 1,
            "mana": 897,
        }

        dead_ram = {
            "memory_attached": False,
            "player": {
                "address": "0x0",
                "hp": 0,
                "es": 0,
                "mana": 0,
            },
        }

        res = validate_reconnect_result(
            result_data=fake_result,
            ram_snapshot=dead_ram,
            check_live_process=True,
            check_live_hwnd=False,
        )

        assert not res.is_valid
        assert any("INV-RECONNECT-NO-HARDCODED-VITALS" in err for err in res.errors)
        assert any("Detected hardcoded CI template vitals" in err for err in res.errors)

    def test_validator_accepts_authentic_telemetry(self):
        """Dữ liệu thực nghiệm đọc từ RAM thật, PID sống, HWND hợp lệ -> PASSED."""
        curr_pid = os.getpid()

        authentic_result = {
            "status": "SUCCESS",
            "pid": curr_pid,
            "hwnd": "0x1000",
            "es": 3500,
            "hp": 1200,
            "mana": 450,
        }

        authentic_ram = {
            "memory_attached": True,
            "player": {
                "address": "0x7FF712345678",
                "hp": 1200,
                "es": 3500,
                "mana": 450,
            },
        }

        res = validate_reconnect_result(
            result_data=authentic_result,
            ram_snapshot=authentic_ram,
            check_live_process=True,
            check_live_hwnd=False,
        )

        assert res.is_valid
        assert len(res.errors) == 0


class TestScreenCapturerInvariants:
    """Kiểm thử bất biến INV-CAPTURE-CLIENT-ONLY trên ScreenCapturer."""

    def test_screen_capturer_never_captures_desktop_when_hwnd_none(self, temp_output_dir):
        """INV-CAPTURE-CLIENT-ONLY: Khi không tìm thấy cửa sổ POE2 (hwnd=None, bbox=None),
        ScreenCapturer TUYỆT ĐỐI KHÔNG được gọi ImageGrab.grab() chụp toàn Desktop,
        mà phải tạo ảnh chuẩn đoán nền tối chuyên dụng.
        """
        capturer = ScreenCapturer(output_dir=temp_output_dir)

        # Mock find_poe2_window trả về None (game đóng hoặc chưa tìm thấy)
        with patch.object(capturer, "find_poe2_window", return_value=None):
            # Patch ImageGrab.grab để theo dõi xem có bị gọi không tham số không
            with patch("PIL.ImageGrab.grab") as mock_grab:
                saved_path = capturer.capture_screenshot(
                    event_name="TEST_INVARIANT_WINDOW_NOT_FOUND",
                    capture_ram=False,
                )

                # Bắt buộc ImageGrab.grab() KHÔNG BAO GIỜ được gọi
                mock_grab.assert_not_called()

        assert saved_path is not None
        assert os.path.exists(saved_path)

        # Đọc ảnh đã tạo: phải là ảnh chẩn đoán 1280x720 màu nền xanh đen (15, 23, 42)
        with Image.open(saved_path) as img:
            assert img.size == (1280, 720)
            # Điểm pixel góc trong [20, 20] phải là màu nền (15, 23, 42)
            bg_pixel = img.getpixel((20, 20))
            assert bg_pixel[:3] == (15, 23, 42)

        # Đọc file metadata JSON tương ứng
        json_path = saved_path.replace(".png", ".json")
        assert os.path.exists(json_path)
        with open(json_path, "r", encoding="utf-8") as f:
            meta = json.load(f)

        assert meta["window_found"] is False
        assert meta["bbox"] is None


class TestIncidentDossierGovernance:
    """Kiểm toán hồ sơ sự cố Rule 6: INCIDENT_20260918_RECONNECT_FALSE_POSITIVE."""

    def test_incident_dossier_structure_and_rca(self):
        """Khép kín vòng lặp tự sửa lỗi: Incident Dossier phải đầy đủ 4 bước RCA và artifacts liên kết."""
        dossier_dir = os.path.join(
            "debug_harness", "incidents", "INCIDENT_20260918_RECONNECT_FALSE_POSITIVE"
        )
        assert os.path.isdir(dossier_dir), f"Thiếu thư mục dossier {dossier_dir}"

        report_json = os.path.join(dossier_dir, "incident_report.json")
        rca_md = os.path.join(dossier_dir, "RCA_4_STEPS_ANALYSIS.md")

        assert os.path.exists(report_json), "Thiếu file incident_report.json"
        assert os.path.exists(rca_md), "Thiếu file RCA_4_STEPS_ANALYSIS.md"

        with open(report_json, "r", encoding="utf-8") as f:
            data = json.load(f)

        assert data["incident_id"] == "INCIDENT_20260918_RECONNECT_FALSE_POSITIVE"
        assert data["severity"] == "CRITICAL"
        assert "INV-RECONNECT-NO-HARDCODED-VITALS" in data["violated_invariants"]
        assert "INV-CAPTURE-CLIENT-ONLY" in data["violated_invariants"]

        with open(rca_md, "r", encoding="utf-8") as f:
            rca_text = f.read()

        assert "BƯỚC 1: HIỆN TƯỢNG SỰ CỐ" in rca_text
        assert "BƯỚC 2: BẰNG CHỨNG MÂU THUẪN THỰC ĐỊA" in rca_text
        assert "BƯỚC 3: PHÂN TÍCH NGUYÊN NHÂN GỐC RỄ" in rca_text
        assert "BƯỚC 4: GIẢI PHÁP KHẮC PHỤC & CHỐT CHẶN NGĂN NGỪA" in rca_text
