"""AutoPOE2 - Unit Tests for Zero-Day Memory Calibrator & Invariants.
========================================================================
Kiểm thử toàn diện cho ZeroDayMemoryCalibrator:
1. INV-BUILD-01: Trích xuất PE Fingerprint, TimeDateStamp và SectionLayoutHash.
2. INV-SIG-UNIQUE: Pattern SigScan AOB với wildcard, RIP displacement resolution.
3. INV-MEM-DYNAMIC-XYZ: Loại bỏ triệt để neo tĩnh 0x290 / 0x540 / 0x120.
4. INV-VIT-01: Nhận diện CI (HP 1/1), kháng UI Marker 778/779, tách rời ES, Ward, Mana, Spirit.
5. INV-OFFSET-01: Định dạng TOML hợp lệ, từ chối tuyệt đối địa chỉ heap tuyệt đối.
6. Ngân sách thời gian < 180s (chạy < 3s).

Tuân thủ nghiêm ngặt:
- Rule 5: 100% dữ liệu thực nghiệm / artifact.
- Rule 14: Quality Gate, 100% tests PASS.
"""

from __future__ import annotations

import struct
import tempfile
import unittest
from pathlib import Path

from src.assistant_tool.zero_day_mem_calibrator import (
    FORBIDDEN_ABSOLUTE_KEYS,
    CalibrationResult,
    ZeroDayMemoryCalibrator,
)


class TestZeroDayMemoryCalibrator(unittest.TestCase):
    """Test suite kiểm thử bộ hiệu chuẩn bộ nhớ Zero-Day."""

    def setUp(self) -> None:
        self.calibrator = ZeroDayMemoryCalibrator()

    def test_aob_pattern_parsing_and_matching(self) -> None:
        """Kiểm chứng thuật toán quét chữ ký AOB với wildcard '?'."""
        # Haystack mô phỏng đoạn code assembly .text
        # MOV RAX, [RIP + 0x10] -> 48 8B 05 10 00 00 00
        # NOP NOP NOP
        code = bytes([
            0x90, 0x90,
            0x48, 0x8B, 0x05, 0x20, 0x00, 0x00, 0x00,  # match tại offset 2
            0x48, 0x85, 0xC0, 0x74, 0x05, 0x48, 0x8B, 0x48,
            0xCC, 0xCC,
        ])
        pattern_str = "48 8B 05 ? ? ? ? 48 85 C0 74 ? 48 8B 48"
        pat = self.calibrator.parse_pattern(pattern_str)

        self.assertEqual(len(pat), 15)
        self.assertIsNone(pat[3])
        self.assertIsNone(pat[4])
        self.assertIsNone(pat[5])
        self.assertIsNone(pat[6])
        self.assertIsNone(pat[11])
        self.assertEqual(pat[0], 0x48)
        self.assertEqual(pat[1], 0x8B)

        matches = self.calibrator.scan_pattern_bytes(code, pat)
        self.assertEqual(len(matches), 1)
        self.assertEqual(matches[0], 2)

        # Kiểm chứng giải mã RIP displacement: disp = 0x20
        # next_ip = text_va + match_offset(2) + rip_offset(3) + 4 = text_va + 9
        # target_rva = (text_va + 9) + 0x20 = text_va + 0x29
        text_va = 0x1000
        rva = self.calibrator.resolve_rip_rva(matches[0], 3, code, text_va)
        self.assertEqual(rva, text_va + 9 + 0x20)

    def test_inv_mem_dynamic_xyz_stripping(self) -> None:
        """Kiểm chứng INV-MEM-DYNAMIC-XYZ: Từ chối neo tĩnh 0x290, 0x540, 0x120."""
        # 0x290: Hideout spawn point anchor
        pos, stripped = self.calibrator.sanitize_pos_offset(0x290)
        self.assertEqual(pos, 0x0)
        self.assertTrue(stripped)

        # 0x540: Creek spawn point anchor
        pos, stripped = self.calibrator.sanitize_pos_offset(0x540)
        self.assertEqual(pos, 0x0)
        self.assertTrue(stripped)

        # 0x120: Instance anchor
        pos, stripped = self.calibrator.sanitize_pos_offset(0x120)
        self.assertEqual(pos, 0x0)
        self.assertTrue(stripped)

        # 0x0: Chưa khởi tạo -> cũng phải ép PlayerFinder
        pos, stripped = self.calibrator.sanitize_pos_offset(0x0)
        self.assertEqual(pos, 0x0)
        self.assertTrue(stripped)

        # Offset hợp lệ (nếu có con trỏ động khác)
        pos, stripped = self.calibrator.sanitize_pos_offset(0x400)
        self.assertEqual(pos, 0x400)
        self.assertFalse(stripped)

    def test_vitals_fingerprint_ci_detection(self) -> None:
        """Kiểm chứng nhận diện chính xác Chaos Inoculation (HP 1/1)."""
        # Tạo buffer LifeComponent 0x454 byte
        buf = bytearray(0x454)
        # HP: 1/1 (+0x00, +0x04)
        struct.pack_into("<II", buf, 0x0, 1, 1)
        # ES: 4713/4713 (+0x0C, +0x10)
        struct.pack_into("<II", buf, 0xC, 4713, 4713)
        # Ward: 240/240 (+0x20, +0x24)
        struct.pack_into("<II", buf, 0x20, 240, 240)
        # Mana: 876/876 (+0x420, +0x424)
        struct.pack_into("<II", buf, 0x420, 876, 876)
        # Spirit: 7/147 (+0x44C, +0x450)
        struct.pack_into("<II", buf, 0x44C, 7, 147)

        vitals = self.calibrator.evaluate_vitals_buffer(bytes(buf))
        self.assertTrue(vitals.is_ci)
        self.assertFalse(vitals.is_marker_778)
        self.assertEqual(vitals.cur_hp, 1)
        self.assertEqual(vitals.max_hp, 1)
        self.assertEqual(vitals.cur_es, 4713)
        self.assertEqual(vitals.max_es, 4713)
        self.assertEqual(vitals.cur_ward, 240)
        self.assertEqual(vitals.cur_mana, 876)
        self.assertEqual(vitals.cur_spirit, 7)

    def test_vitals_fingerprint_anti_778_marker_rejection(self) -> None:
        """Kiểm chứng phát hiện và chặn ô nhiễm UI Marker 778/779."""
        buf = bytearray(0x454)
        # Ô nhiễm UI Marker: HP 778/778
        struct.pack_into("<II", buf, 0x0, 778, 778)
        struct.pack_into("<II", buf, 0xC, 1000, 1000)

        vitals = self.calibrator.evaluate_vitals_buffer(bytes(buf))
        self.assertTrue(vitals.is_marker_778)
        self.assertFalse(vitals.is_ci)
        self.assertIn("778", vitals.diagnostic)

    def test_generate_toml_invariants(self) -> None:
        """Kiểm chứng TOML sinh ra tuân thủ INV-BUILD-01 và INV-OFFSET-01."""
        calib = CalibrationResult(
            success=True,
            build_id="6AA2213C",
            time_date_stamp=0x6AA2213C,
            section_layout_hash=0x3C619311AE16D3D6,
            file_sha256="dc3b3f1bedbf664a21fb71ff6fd178db5e55f588efc53ead026a87d963468714",
            static_roots=[0x45CCFE0, 0x45CCFD8],
            aob_patterns=[{
                "pattern": "F0 48 01 1D ? ? ? ?",
                "rip_offset": 4,
            }],
            offsets={
                "player_offset": 0xBE0,
                "stat_offset": 0x3D8,
                "pos_offset": 0x0,
                "energy_shield_offset": 0xC,
            },
            static_anchor_stripped=True,
            gate2_passed=True,
            elapsed_sec=0.15,
        )

        toml_text = self.calibrator.generate_toml(calib)

        # INV-BUILD-01
        self.assertIn("build_time_date_stamp = 0x6AA2213C", toml_text)
        self.assertIn("build_section_layout_hash = 0x3C619311AE16D3D6", toml_text)
        self.assertIn("static_root_1 = 0x45CCFE0", toml_text)
        self.assertIn("aob_pattern_1 = F0 48 01 1D ? ? ? ?", toml_text)
        self.assertIn("pos_offset = 0x0", toml_text)

        # INV-OFFSET-01: Cấm tuyệt đối khóa heap
        for forbidden in FORBIDDEN_ABSOLUTE_KEYS:
            self.assertNotIn(f"{forbidden} =", toml_text)

    def test_full_calibration_pipeline_execution_time(self) -> None:
        """Kiểm chứng toàn bộ quy trình hiệu chuẩn chạy hoàn tất trong < 180s."""
        res = self.calibrator.run_calibration(dry_run=True)
        self.assertTrue(res.success)
        self.assertTrue(res.gate2_passed)
        self.assertTrue(res.static_anchor_stripped)
        self.assertEqual(res.offsets["pos_offset"], 0x0)
        self.assertLess(res.elapsed_sec, 180.0)
        self.assertLess(res.elapsed_sec, 5.0)  # Thường chỉ tốn < 1 giây


if __name__ == "__main__":
    unittest.main()
