"""
AutoPOE2 - Unit Tests for Invariants & Safety Sentinel Fixes (Audit 28/09/2026).
"""

import json
import os
import shutil
import tempfile
import time
import pytest

from src.agentic_engine.invariant_sentinel import (
    ERR_BOGUS_VITAL_STRUCT_778,
    ERR_XYZ_FROZEN_AT_ORIGIN,
    InvariantSentinel,
)
from src.agentic_engine.flight_recorder import MultimodalFlightRecorder
from src.agentic_engine.realtime_watcher import RealtimeWatcher
from src.agentic_engine.master_invariants_sentinel import MasterInvariantsSentinel
from src.assistant_tool.zero_day_gate import ZeroDayInvariantGate
from src.assistant_tool.core_shm_bridge import CoreTelemetrySnapshot, PlayerTelemetry


def test_dict_nested_player_detection():
    """Bắt buộc phát hiện struct rác 778/778 khi telemetry là dictionary lồng 'player'."""
    raw_dict = {
        "player": {
            "current_hp": 778,
            "max_hp": 778,
            "pos_x": 100.0,
            "pos_y": 200.0,
            "pos_z": 0.0,
        },
        "area_name": "Sandswept Marsh",
        "monsters_count": 5,
    }
    vitals_anomalies = InvariantSentinel.check_vitals_invariants(raw_dict)
    assert ERR_BOGUS_VITAL_STRUCT_778 in vitals_anomalies

    all_anomalies = InvariantSentinel.check_all(raw_dict)
    assert ERR_BOGUS_VITAL_STRUCT_778 in all_anomalies
    assert ERR_XYZ_FROZEN_AT_ORIGIN not in all_anomalies


def test_flight_recorder_incident_rotation():
    """Kiểm tra cơ chế dọn dẹp thư mục incident khi vượt max_retained_incidents."""
    temp_dir = tempfile.mkdtemp(prefix="test_rotation_")
    try:
        recorder = MultimodalFlightRecorder(output_dir=temp_dir, max_retained_incidents=3)
        for i in range(5):
            recorder.package_incident(
                incident_type=f"TEST_INCIDENT_{i}",
                violated_invariants=["INV-TEST-01"],
                telemetry={"player": {"hp": 100, "max_hp": 100}},
            )
            time.sleep(0.02)

        dirs = [d for d in os.listdir(temp_dir) if d.startswith("INCIDENT_") and os.path.isdir(os.path.join(temp_dir, d))]
        assert len(dirs) <= 3
    finally:
        shutil.rmtree(temp_dir, ignore_errors=True)


def test_master_sentinel_zero_mock_telemetry():
    """Kiểm tra package_invariant_incident không bịa đặt số liệu Monk CI khi telemetry is None."""
    temp_dir = tempfile.mkdtemp(prefix="test_master_")
    ptr_file = os.path.join(temp_dir, "LATEST_INCIDENT.json")
    try:
        sentinel = MasterInvariantsSentinel(incidents_dir=temp_dir, latest_pointer_file=ptr_file)
        dossier_path = sentinel.package_invariant_incident(
            invariant_code="INV-TEST-01",
            message="Test message",
            telemetry=None,
        )
        inc_dir = os.path.dirname(dossier_path)
        ram_file = os.path.join(inc_dir, "ram_snapshot.json")
        with open(ram_file, "r", encoding="utf-8") as f:
            ram = json.load(f)

        assert ram["player"]["hp"] == 0
        assert ram["player"]["es"] == 0
        assert ram["provenance"]["rule_14_compliant"] is True
    finally:
        shutil.rmtree(temp_dir, ignore_errors=True)


def test_realtime_watcher_debounce():
    """Kiểm tra RealtimeWatcher không flood xuất file incident liên tục mỗi tick."""
    temp_dir = tempfile.mkdtemp(prefix="test_watcher_debounce_")
    try:
        recorder = MultimodalFlightRecorder(output_dir=temp_dir)
        watcher = RealtimeWatcher(flight_recorder=recorder)
        watcher.incident_debounce_s = 2.0

        p = PlayerTelemetry(current_hp=778, max_hp=778, pos_x=0.0, pos_y=0.0, pos_z=0.0)
        snap = CoreTelemetrySnapshot(is_online=True, snapshot_id=1, timestamp_ms=1000, player=p, area_name="Sandswept Marsh")

        # Tick 1: trigger export
        watcher.process_telemetry(snapshot=snap, trigger_incident_export=True)
        # Tick 2: within debounce window, should NOT create another incident
        watcher.process_telemetry(snapshot=snap, trigger_incident_export=True)

        dirs = [d for d in os.listdir(temp_dir) if d.startswith("INCIDENT_") and os.path.isdir(os.path.join(temp_dir, d))]
        assert len(dirs) == 1
    finally:
        shutil.rmtree(temp_dir, ignore_errors=True)


def test_zero_day_gate_all_20_invariants_count():
    """Xác nhận ZeroDayInvariantGate bao phủ đầy đủ 20 Bất biến."""
    codes = ZeroDayInvariantGate.MASTER_INVARIANT_CODES
    assert "INV-WAYSTONE-ROW1-ONLY" in codes
    assert "INV-MOTION-INTEGRITY" in codes
    assert len(codes) >= 20
