"""AutoPOE2 - Unit Tests for BossArenaDetector (Doc 69 §3.5).
============================================================
Kiểm thử toàn diện:
1. Khởi tạo và xử lý an toàn khung hình rỗng / dị thường.
2. Nhận diện cổng sương mù đấu trường Boss (Fog Door / Arena Gate) và đề xuất PRE_BUFF.
3. Nhận diện thanh máu Boss khi đang combat trực tiếp (ENGAGE_BOSS).
4. Nhận diện thanh máu Boss ở trạng thái chuyển pha / khiên bất tử (PHASE_KITE).
5. Kháng nhận diện sai (Zero False Positives) trên cảnh chơi bình thường / artifact thật.
6. Benchmark hiệu năng thời gian thực: Tốc độ xử lý < 5.0 ms.

Tuân thủ nghiêm ngặt:
- Rule 4: Module hóa độc lập.
- Rule 5: Empirical DoD.
"""

from __future__ import annotations

import os
import time
import unittest
import numpy as np

try:
    from PIL import Image
except ImportError:
    Image = None  # type: ignore[assignment]

from src.assistant_tool.vision.boss_arena_detector import (
    ArenaGateDetection,
    BossArenaDetector,
    BossArenaState,
    BossHealthBarDetection,
)


class TestBossArenaDetector(unittest.TestCase):
    """Test suite kiểm thử bộ nhận diện cổng sương mù và đấu trường Boss."""

    def setUp(self) -> None:
        self.detector = BossArenaDetector(ref_resolution=(2560, 1440))
        self.captures_dir = os.path.join(os.path.dirname(__file__), "..", "captures")

    def test_empty_and_invalid_frame_safety(self) -> None:
        """Kiểm chứng tính an toàn với đầu vào None hoặc kích thước không hợp lệ."""
        state = self.detector.process_frame(None)
        self.assertFalse(state.gate_detected)
        self.assertFalse(state.boss_bar_active)
        self.assertEqual(state.recommended_action, "NORMAL_CLEAR")

        tiny = np.zeros((10, 10, 3), dtype=np.uint8)
        state_tiny = self.detector.process_frame(tiny)
        self.assertFalse(state_tiny.gate_detected)
        self.assertFalse(state_tiny.boss_bar_active)

    def test_fog_gate_detection_and_prebuff_action(self) -> None:
        """Kiểm chứng nhận diện cổng sương mù và kích hoạt trạng thái PRE_BUFF."""
        # Tạo khung hình 1920x1080 giả lập cảnh hầm ngục tối
        frame = np.full((1080, 1920, 3), 25, dtype=np.uint8)

        # Vẽ dải sương mù xám bạc đặc trưng tại y=420..490, x=700..1220
        # Lum ~130..150, color_std < 10
        fog_patch = np.full((70, 520, 3), [135, 140, 142], dtype=np.uint8)
        # Thêm chút noise mờ đục tự nhiên
        noise = np.random.randint(-5, 6, (70, 520, 3), dtype=np.int16)
        fog_patch = np.clip(fog_patch.astype(np.int16) + noise, 0, 255).astype(np.uint8)

        frame[420:490, 700:1220] = fog_patch

        # Đặt 2 trụ đá sẫm màu ở 2 bên mép cổng
        frame[400:520, 670:700] = [15, 12, 10]
        frame[400:520, 1220:1250] = [15, 12, 10]

        state = self.detector.process_frame(frame)

        self.assertTrue(state.gate_detected, "Phải nhận diện được cổng sương mù")
        self.assertEqual(state.gate_info.gate_type, "fog_door")
        self.assertGreaterEqual(state.gate_info.confidence, 0.45)
        self.assertIsNotNone(state.gate_info.gate_screen_pos)
        self.assertEqual(state.recommended_action, "PRE_BUFF", "Trước cổng phải đề xuất PRE_BUFF")

    def test_boss_health_bar_active_combat(self) -> None:
        """Kiểm chứng nhận diện thanh máu Boss màu đỏ viền vàng và kích hoạt ENGAGE_BOSS."""
        frame = np.full((1080, 1920, 3), 30, dtype=np.uint8)

        # Vẽ thanh máu Boss trên đỉnh màn hình tại y=35..65, x=650..1270
        # Viền vàng kim
        frame[35:38, 650:1270] = [185, 130, 25]
        frame[62:65, 650:1270] = [185, 130, 25]
        frame[35:65, 650:653] = [185, 130, 25]
        frame[35:65, 1267:1270] = [185, 130, 25]

        # Ruột máu đỏ (còn 75% máu: từ x=653 đến x=1115)
        frame[38:62, 653:1115] = [180, 30, 30]
        # Phần mất máu màu đen
        frame[38:62, 1115:1267] = [20, 15, 15]

        state = self.detector.process_frame(frame)

        self.assertTrue(state.boss_bar_active, "Phải phát hiện thanh máu Boss")
        self.assertTrue(state.in_arena, "Có thanh máu Boss nghĩa là đang trong đấu trường")
        self.assertFalse(state.boss_bar_info.is_phased, "Boss đang ở trạng thái thường, không bất tử")
        self.assertGreater(state.boss_bar_info.health_ratio, 0.5)
        self.assertEqual(state.recommended_action, "ENGAGE_BOSS")

    def test_boss_health_bar_phased_invulnerability(self) -> None:
        """Kiểm chứng nhận diện thanh máu Boss ở trạng thái chuyển pha (PHASE_KITE)."""
        frame = np.full((1080, 1920, 3), 30, dtype=np.uint8)

        # Viền vàng kim
        frame[35:38, 650:1270] = [185, 130, 25]
        frame[62:65, 650:1270] = [185, 130, 25]
        frame[35:65, 650:653] = [185, 130, 25]
        frame[35:65, 1267:1270] = [185, 130, 25]

        # Ruột màu xám bạc / khiên chuyển pha (Invulnerable)
        frame[38:62, 653:1267] = [180, 185, 200]

        state = self.detector.process_frame(frame)

        self.assertTrue(state.boss_bar_active)
        self.assertTrue(state.boss_bar_info.is_phased, "Phải nhận diện được Boss đang chuyển pha")
        self.assertEqual(state.recommended_action, "PHASE_KITE", "Boss chuyển pha bất tử -> Đề xuất PHASE_KITE")

    def test_real_capture_no_false_positive(self) -> None:
        """Kiểm chứng không nhận diện sai trên ảnh chụp thực tế trong Hideout / Map thường."""
        sample_img = os.path.join(self.captures_dir, "test_hideout_now.png")
        if not os.path.exists(sample_img):
            sample_img = os.path.join(self.captures_dir, "game_window_focused.png")

        if os.path.exists(sample_img) and Image is not None:
            img = Image.open(sample_img)
            state = self.detector.process_frame(img)
            # Ở Hideout / cảnh thường không có Boss thanh máu
            self.assertFalse(state.boss_bar_active, "Không được báo Boss Bar sai trong Hideout")
            self.assertEqual(state.recommended_action, "NORMAL_CLEAR")

    def test_sub_5ms_performance_benchmark(self) -> None:
        """Kiểm chứng hiệu năng thời gian thực: Tốc độ xử lý phải đạt chuẩn < 5.0 ms."""
        # Kiểm thử trên ảnh độ phân giải 2560x1440
        frame_1440p = np.full((1440, 2560, 3), 40, dtype=np.uint8)
        # Thêm dải sương mù để detector chạy đầy đủ cả 2 pipeline
        frame_1440p[600:680, 1000:1600] = [135, 140, 142]

        # Chạy warm-up
        _ = self.detector.process_frame(frame_1440p)

        iterations = 15
        latencies = []
        for _ in range(iterations):
            t0 = time.perf_counter()
            st = self.detector.process_frame(frame_1440p)
            t1 = time.perf_counter()
            latencies.append((t1 - t0) * 1000.0)

        avg_latency = float(np.mean(latencies))
        p95_latency = float(np.percentile(latencies, 95))

        print(f"\n[Benchmark BossArenaDetector 1440p] Avg: {avg_latency:.2f}ms | P95: {p95_latency:.2f}ms")
        self.assertLess(avg_latency, 5.0, f"Độ trễ trung bình {avg_latency:.2f}ms phải < 5.0ms")


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