"""AutoPOE2 - Master Invariants Sentinel & Headless Mock Harness Tests (SSoT 2026).
================================================================================
Domain: Sư đoàn 4 / Trung đoàn 2: MASTER INVARIANTS AUDITOR (lead_qa_governance).
Kiểm thử toàn diện Ma trận 19 Bất biến Kiến trúc (AGENTS.md Mục 8):
- 100% User Mode Headless Mock Harness (< 1.0s).
- Autonomy Readiness Score (ARS %) đạt 100.0% khi đủ 19 bất biến.
- Khóa chặn Fail-Closed kích hoạt ARS = 0.0% khi bất kỳ bất biến nào bị BLOCKED.
- Quy trình đóng gói Incident Dossier 6 thành phần vào debug_harness/incidents/.
- 69/69 checks kiểm định định lượng toàn diện.
"""

from __future__ import annotations

import json
import os
import shutil
import tempfile
import pytest

from src.agentic_engine.master_invariants_sentinel import (
    MASTER_19_INVARIANTS_SPECS,
    MASTER_20_INVARIANTS_SPECS,
    InvariantStatus,
    MasterInvariantsSentinel,
)

ALL_20_INVARIANT_CODES = [
    "INV-INPUT-ABS-MOUSE",
    "INV-WASD-NO-BLIND-LMB",
    "INV-KEY-NO-F-FOR-INTERACT",
    "INV-KEY-NO-U-FOR-MAP-DEVICE",
    "INV-WASD-MIN-DWELL",
    "INV-F8-MUTES-BRAIN",
    "INV-MAPDEVICE-VERIFY-UI",
    "INV-MAPDEVICE-RETRY-POLL",
    "INV-BRAIN-CONFLICT-GATEKEEPER",
    "INV-TERRAIN-ORIGIN",
    "INV-FSM-SINGLE-OWNER",
    "INV-CI-01",
    "INV-MEM-DYNAMIC-XYZ",
    "INV-KEY-PORTAL",
    "INV-KEY-E-PULSE",
    "INV-OCR-MOCK-NO-CALL",
    "INV-FOCUS-INTERLOCK-HARDWARE-SAFETY",
    "INV-ATLAS-FARM-NODE",
    "INV-WAYSTONE-ROW1-ONLY",
    "INV-MOTION-INTEGRITY",
]
ALL_19_INVARIANT_CODES = ALL_20_INVARIANT_CODES


@pytest.fixture
def sentinel_env():
    """Tạo môi trường cô lập cho Master Invariants Sentinel."""
    temp_dir = tempfile.mkdtemp(prefix="sentinel_test_")
    incidents_dir = os.path.join(temp_dir, "incidents")
    pointer_file = os.path.join(temp_dir, "LATEST_INCIDENT.json")

    sentinel = MasterInvariantsSentinel(
        incidents_dir=incidents_dir,
        latest_pointer_file=pointer_file,
    )

    yield {
        "temp_dir": temp_dir,
        "incidents_dir": incidents_dir,
        "pointer_file": pointer_file,
        "sentinel": sentinel,
    }

    shutil.rmtree(temp_dir, ignore_errors=True)


class TestMaster19InvariantsSpecs:
    """Kiểm tra đặc tả 20 Bất biến Kiến trúc (Master Invariant Matrix)."""

    def test_spec_count_is_exactly_20(self):
        """Xác nhận đủ 20 Bất biến Kiến trúc theo đúng AGENTS.md Mục 8."""
        assert len(MASTER_20_INVARIANTS_SPECS) == 20
        codes = [s.code for s in MASTER_20_INVARIANTS_SPECS]
        assert codes == ALL_20_INVARIANT_CODES

    def test_total_weights_sum_to_100(self):
        """Tổng trọng số định lượng của 20 bất biến xấp xỉ 100%."""
        total_w = sum(s.weight for s in MASTER_20_INVARIANTS_SPECS)
        assert abs(total_w - 100.0) < 0.1


class TestMasterInvariantsAuditHarness:
    """Kiểm định Headless Mock Harness và Autonomy Readiness Score (ARS)."""

    def test_full_audit_100_percent_verified_and_ars_100(self, sentinel_env):
        """Khi 20 bất biến đều thỏa mãn -> 100% VERIFIED, ARS = 100.0%."""
        sentinel: MasterInvariantsSentinel = sentinel_env["sentinel"]
        report = sentinel.audit_all_invariants()

        assert report.is_cleared_for_autonomous is True
        assert report.total_invariants == 20
        assert report.passed_count == 20
        assert report.warning_count == 0
        assert report.blocked_count == 0
        assert report.autonomy_readiness_score == 100.0
        assert "100% VERIFIED" in report.summary
        assert report.total_duration_ms < 1000.0  # Chạy < 1.0s theo Rule 5

    def test_fail_closed_triggers_ars_zero_on_blocked_invariant(self, sentinel_env):
        """Chỉ cần 1 bất biến bị BLOCKED -> ARS lập tức về 0.0% (Fail-Closed)."""
        sentinel: MasterInvariantsSentinel = sentinel_env["sentinel"]

        overrides = {"INV-KEY-NO-F-FOR-INTERACT": False}
        report = sentinel.audit_all_19_invariants(custom_overrides=overrides)

        assert report.is_cleared_for_autonomous is False
        assert report.blocked_count == 1
        assert report.autonomy_readiness_score == 0.0
        assert "BLOCKED" in report.summary

        f_result = [r for r in report.results if r.code == "INV-KEY-NO-F-FOR-INTERACT"][0]
        assert f_result.status == InvariantStatus.BLOCKED
        assert "CẢNH BÁO" in f_result.evidence


class TestIndividualInvariantAuditors:
    """Kiểm tra chi tiết từng phương thức kiểm định của các bất biến trọng yếu."""

    def test_inv_input_abs_mouse(self, sentinel_env):
        sentinel: MasterInvariantsSentinel = sentinel_env["sentinel"]
        res_ok = sentinel.audit_inv_input_abs_mouse(is_absolute=True, delta_only_for_bezier=True)
        assert res_ok.status == InvariantStatus.PASSED

        res_fail = sentinel.audit_inv_input_abs_mouse(is_absolute=False)
        assert res_fail.status == InvariantStatus.BLOCKED

    def test_inv_wasd_no_blind_lmb(self, sentinel_env):
        sentinel: MasterInvariantsSentinel = sentinel_env["sentinel"]
        res_ok = sentinel.audit_inv_wasd_no_blind_lmb(approach_dist=15.0)
        assert res_ok.status == InvariantStatus.PASSED

        res_fail = sentinel.audit_inv_wasd_no_blind_lmb(approach_dist=25.0)
        assert res_fail.status == InvariantStatus.BLOCKED

    def test_inv_wasd_min_dwell(self, sentinel_env):
        sentinel: MasterInvariantsSentinel = sentinel_env["sentinel"]
        res_ok = sentinel.audit_inv_wasd_min_dwell(dwell_ms=300.0)
        assert res_ok.status == InvariantStatus.PASSED

        res_fail = sentinel.audit_inv_wasd_min_dwell(dwell_ms=100.0)
        assert res_fail.status == InvariantStatus.BLOCKED

    def test_inv_ci_01(self, sentinel_env):
        sentinel: MasterInvariantsSentinel = sentinel_env["sentinel"]
        res_ok = sentinel.audit_inv_ci_01(current_hp=1, max_hp=1)
        assert res_ok.status == InvariantStatus.PASSED

        res_fail = sentinel.audit_inv_ci_01(current_hp=100, max_hp=100)
        assert res_fail.status == InvariantStatus.BLOCKED

    def test_inv_focus_interlock(self, sentinel_env):
        sentinel: MasterInvariantsSentinel = sentinel_env["sentinel"]
        res_ok = sentinel.audit_inv_focus_interlock_hardware_safety(
            keydown_blocked_unfocused=True,
            keyup_allowed_unfocused=True,
            release_all_on_unfocus=True,
            two_factor_window_check=True,
        )
        assert res_ok.status == InvariantStatus.PASSED

        res_fail = sentinel.audit_inv_focus_interlock_hardware_safety(
            keyup_allowed_unfocused=False,
        )
        assert res_fail.status == InvariantStatus.BLOCKED

    def test_inv_atlas_farm_node(self, sentinel_env):
        sentinel: MasterInvariantsSentinel = sentinel_env["sentinel"]
        res_ok = sentinel.audit_inv_atlas_farm_node(burning_monolith_blocked=True)
        assert res_ok.status == InvariantStatus.PASSED

        res_fail = sentinel.audit_inv_atlas_farm_node(burning_monolith_blocked=False)
        assert res_fail.status == InvariantStatus.BLOCKED

    def test_inv_waystone_row1_only(self, sentinel_env):
        sentinel: MasterInvariantsSentinel = sentinel_env["sentinel"]
        res_ok = sentinel.audit_inv_waystone_row1_only(slot_row_index=0)
        assert res_ok.status == InvariantStatus.PASSED

        res_fail = sentinel.audit_inv_waystone_row1_only(slot_row_index=3)
        assert res_fail.status == InvariantStatus.BLOCKED


class TestIncidentDossierPackaging:
    """Kiểm tra quy trình đóng gói Incident Dossier 6 thành phần (Rule 6)."""

    def test_package_incident_creates_all_6_artifacts_and_pointer(self, sentinel_env):
        sentinel: MasterInvariantsSentinel = sentinel_env["sentinel"]

        dossier_path = sentinel.package_invariant_incident(
            invariant_code="INV-KEY-NO-F-FOR-INTERACT",
            message="Phát hiện phím F được gửi trong lúc tương tác Map Device",
            rca_notes={
                "step1_trace": "Call graph từ MapDeviceHandler::Interact -> SendKey(0x46)",
                "step2_invariant": "INV-KEY-NO-F-FOR-INTERACT bị vi phạm",
                "step3_refactor": "Chuyển toàn bộ tương tác sang Click LMB có chủ đích",
                "step4_lock": "Khóa chặn bằng assertion trong InputManager",
            },
        )

        assert os.path.exists(dossier_path)
        incident_dir = os.path.dirname(dossier_path)

        assert os.path.exists(os.path.join(incident_dir, "incident_dossier.json"))
        assert os.path.exists(os.path.join(incident_dir, "screenshot.png"))
        assert os.path.exists(os.path.join(incident_dir, "ram_snapshot.json"))
        assert os.path.exists(os.path.join(incident_dir, "core_log_slice.txt"))
        assert os.path.exists(os.path.join(incident_dir, "companion_log_slice.txt"))
        assert os.path.exists(os.path.join(incident_dir, "RCA_4_STEPS_ANALYSIS.md"))

        pointer_path = sentinel_env["pointer_file"]
        assert os.path.exists(pointer_path)
        with open(pointer_path, "r", encoding="utf-8") as f:
            pointer_data = json.load(f)

        assert pointer_data["invariant_code"] == "INV-KEY-NO-F-FOR-INTERACT"
        assert pointer_data["incident_dir"] == os.path.abspath(incident_dir)


class TestAll19InvariantsMatrixParametrized:
    """Kiểm thử định lượng độc lập từng bất biến trong Ma trận 19 Bất biến (57 tests)."""

    @pytest.mark.parametrize("inv_code", ALL_19_INVARIANT_CODES)
    def test_invariant_positive_compliance(self, sentinel_env, inv_code):
        """[1/3] Kiểm chứng tuân thủ hợp lệ: 19/19 bất biến đều PASSED."""
        sentinel: MasterInvariantsSentinel = sentinel_env["sentinel"]
        report = sentinel.audit_all_19_invariants(custom_overrides={inv_code: True})
        target_res = [r for r in report.results if r.code == inv_code][0]

        assert target_res.status == InvariantStatus.PASSED
        assert len(target_res.evidence) > 0
        assert len(target_res.metric_value) > 0
        assert target_res.duration_ms >= 0.0

    @pytest.mark.parametrize("inv_code", ALL_19_INVARIANT_CODES)
    def test_invariant_fail_closed_detection(self, sentinel_env, inv_code):
        """[2/3] Kiểm chứng Fail-Closed: Bất kỳ bất biến nào vi phạm đều khiến ARS = 0.0%."""
        sentinel: MasterInvariantsSentinel = sentinel_env["sentinel"]
        report = sentinel.audit_all_19_invariants(custom_overrides={inv_code: False})
        target_res = [r for r in report.results if r.code == inv_code][0]

        assert target_res.status == InvariantStatus.BLOCKED
        assert report.blocked_count >= 1
        assert report.autonomy_readiness_score == 0.0
        assert report.is_cleared_for_autonomous is False

    @pytest.mark.parametrize("inv_code", ALL_19_INVARIANT_CODES)
    def test_invariant_spec_properties_and_audit(self, inv_code):
        """[3/3] Thẩm định tính toàn vẹn đặc tả SSoT của từng bất biến trong 19 bất biến."""
        spec = [s for s in MASTER_19_INVARIANTS_SPECS if s.code == inv_code][0]
        assert spec.code == inv_code
        assert len(spec.name) > 0
        assert len(spec.monitored_module) > 0
        assert len(spec.rule_summary) > 0
        assert spec.weight > 0.0
        assert spec.is_critical is True
