"""AutoPOE2 - Frontier-Based Minimap Exploration Engine (Doc 69 §3.4 / Stage 5).
==================================================================================
Động cơ điều hướng sương mù liên tục theo đường biên Frontier (Frontier-Based Navigation):
- Thay vì đi ngẫu nhiên hoặc dừng lại sớm sau khi gặp bãi quái đầu tiên, động cơ này
  liên tục trích xuất các Frontier Nodes từ Minimap và định hướng nhân vật thâm nhập
  sâu vào các vùng tối chưa khám phá cho đến khi độ phủ đạt >= 80%.
- Tích hợp liền mạch:
  1. MinimapFogExplorer: Trích xuất và gom cụm mép sương mù, cắt tỉa ngõ cụt.
  2. BossArenaDetector: Giám sát cổng sương mù và thanh máu Boss trên HUD.
  3. MonsterDensityHeatmap: Theo dõi bãi quái vật, số lượng tiêu diệt, ngăn chặn clear sớm.
- Điều phối tự động giữa 3 chế độ vận hành:
  - `FRONTIER_EXPLORE`: Di chuyển sâu vào sương mù chưa mở khi xung quanh an toàn.
  - `COMBAT_ENGAGE`: Tạm dừng thám hiểm để dồn DPS tiêu diệt quái vật khi chạm trán.
  - `BOSS_FIGHT`: Chiến đấu với Boss bản đồ hoặc chuẩn bị tiền buff trước cổng sương mù.

Tuân thủ nghiêm ngặt:
- Rule 1: Python 3.11 Cold Path Tier 2.
- Rule 4: Module hóa độc lập trần < 500 dòng.
- Rule 16: Google Docstrings chuẩn mực.
- Bất biến: INV-MAP-LEGITIMATE-CLEAR, INV-WASD-MIN-DWELL, INV-WASD-NO-BLIND-LMB.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from enum import Enum
import math
import time
from typing import Any, Callable, Dict, List, Optional, Tuple, Union

import numpy as np

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

from src.assistant_tool.navigation.minimap_camera_calibrator import MinimapCameraCalibrator
from src.assistant_tool.navigation.minimap_fog_explorer import (
    CANONICAL_MINIMAP_BBOX_1080P,
    ExplorationTarget,
    MinimapFogExplorer,
)
from src.assistant_tool.navigation.minimap_navigation_adapter import (
    MinimapNavigationAdapter,
    ProjectionMode,
    WasdCommand,
    WasdDirection,
)
from src.assistant_tool.navigation.monster_density_heatmap import MonsterDensityHeatmap
from src.assistant_tool.vision.boss_arena_detector import BossArenaDetector, BossArenaState
from src.common.coordinate_transform import Resolution, RES_1080P, RES_1440P, resolve_resolution


class InMapOperationMode(str, Enum):
    """Chế độ tác chiến trong map của Frontier Exploration Engine."""
    FRONTIER_EXPLORE = "FRONTIER_EXPLORE"  # Thâm nhập sương mù chưa khám phá
    COMBAT_ENGAGE = "COMBAT_ENGAGE"        # Đang combat tiêu diệt bãi quái vật
    BOSS_PRE_BUFF = "BOSS_PRE_BUFF"        # Đứng trước cổng sương mù -> Tiền buff
    BOSS_COMBAT = "BOSS_COMBAT"            # Đang giao tranh trực tiếp với Boss
    BOSS_KITE = "BOSS_KITE"                # Boss chuyển pha bất tử -> Thả diều né đòn
    MAP_COMPLETED = "MAP_COMPLETED"        # Đã hoàn tất dọn dẹp map (>= 80% coverage + boss dead)


@dataclass
class ExplorationStepDecision:
    """Quyết định hành động trong một nhịp tuần tra bản đồ."""
    mode: InMapOperationMode
    wasd_cmd: Optional[WasdCommand] = None
    target_vector: Tuple[float, float] = (0.0, 0.0)
    coverage_ratio: float = 0.0
    packs_encountered: int = 0
    boss_detected: bool = False
    boss_slain: bool = False
    reason: str = ""


class FrontierExplorationEngine:
    """Động cơ điều phối thám hiểm sương mù liên tục và đánh giá hoàn tất bản đồ."""

    def __init__(
        self,
        target_resolution: Union[Resolution, Tuple[int, int]] = RES_1080P,
        target_coverage: float = 0.80,
        min_required_packs: int = 4,
        fog_explorer: Optional[MinimapFogExplorer] = None,
        boss_detector: Optional[BossArenaDetector] = None,
        density_heatmap: Optional[MonsterDensityHeatmap] = None,
        navigation_adapter: Optional[MinimapNavigationAdapter] = None,
    ) -> None:
        """Khởi tạo động cơ thám hiểm sương mù liên tục.

        Args:
            target_resolution: Độ phân giải màn hình game.
            target_coverage: Ngưỡng độ phủ sương mù cần đạt (mặc định >= 80%).
            min_required_packs: Số bãi quái tối thiểu phải chạm trán (kháng lỗi đánh 1 quái rồi dừng).
            fog_explorer: Bộ phân tích sương mù Minimap.
            boss_detector: Bộ phát hiện cổng sương mù và thanh máu Boss.
            density_heatmap: Bản đồ nhiệt mật độ quái vật.
            navigation_adapter: Bộ thích ứng điều hướng WASD.
        """
        self.resolution = resolve_resolution(target_resolution)
        self.target_coverage = target_coverage
        self.min_required_packs = min_required_packs

        self.fog_explorer = fog_explorer or MinimapFogExplorer(grid_width=64, grid_height=64)
        self.boss_detector = boss_detector or BossArenaDetector(ref_resolution=(self.resolution.width, self.resolution.height))
        self.density_heatmap = density_heatmap or MonsterDensityHeatmap()
        self.nav_adapter = navigation_adapter or MinimapNavigationAdapter(
            target_resolution=self.resolution,
            explorer=self.fog_explorer,
            target_coverage=self.target_coverage,
        )

        self.current_mode = InMapOperationMode.FRONTIER_EXPLORE
        self.map_start_time: float = time.time()
        self.boss_slain: bool = False

    def reset(self) -> None:
        """Đặt lại trạng thái cho lượt chạy map mới."""
        self.fog_explorer.reset()
        self.density_heatmap.reset()
        self.current_mode = InMapOperationMode.FRONTIER_EXPLORE
        self.map_start_time = time.time()
        self.boss_slain = False

    def process_frame_and_decide(
        self,
        frame_input: Union[str, np.ndarray, Any],
        player_x: float = 0.0,
        player_y: float = 0.0,
        monsters_near: int = 0,
        core_boss_slain_signal: bool = False,
    ) -> ExplorationStepDecision:
        """Xử lý khung hình và ra quyết định hành động tối ưu cho nhân vật.

        Args:
            frame_input: Ảnh toàn màn hình (đường dẫn, mảng NumPy, hoặc PIL Image).
            player_x: Tọa độ X thế giới của người chơi.
            player_y: Tọa độ Y thế giới của người chơi.
            monsters_near: Số quái vật sống quanh người chơi từ RAM telemetry.
            core_boss_slain_signal: Tín hiệu diệt Boss từ C++ Core SHM nếu có.

        Returns:
            ExplorationStepDecision: Quyết định hành vi tối ưu trong nhịp này.
        """
        now = time.time()

        # Zero-Copy / Single-Decode: Nạp ảnh một lần duy nhất vào bộ nhớ RAM nếu truyền path
        if isinstance(frame_input, str) or hasattr(frame_input, "__fspath__"):
            if Image is not None:
                frame_input = np.array(Image.open(frame_input))

        # 1. Cập nhật Monster Density Heatmap
        self.density_heatmap.record_telemetry_frame(
            player_x=player_x,
            player_y=player_y,
            monsters_near=monsters_near,
            now_sec=now,
        )

        # 2. Phân tích thị giác: Cổng sương mù & Thanh máu Boss trên HUD
        arena_state: BossArenaState = self.boss_detector.process_frame(frame_input)

        # Cập nhật trạng thái thanh máu Boss vào Density Heatmap
        self.density_heatmap.update_optical_boss_status(
            boss_bar_detected=arena_state.boss_bar_active,
            health_ratio=arena_state.boss_bar_info.health_ratio,
            is_phased=arena_state.boss_bar_info.is_phased,
        )

        if core_boss_slain_signal or self.density_heatmap.boss_slain_optical:
            self.boss_slain = True

        # 3. Phân tích sương mù và trích xuất Frontier WASD command
        wasd_cmd = self.nav_adapter.process_fullscreen_frame(frame_input)
        coverage_ratio = wasd_cmd.coverage_ratio if wasd_cmd else self.fog_explorer.coverage_ratio

        # 4. Kiểm tra điều kiện hoàn tất bản đồ chuẩn (>= 80% coverage + boss slain / legitimate clear)
        is_legit, legit_reason = self.density_heatmap.is_clearing_legitimate(self.min_required_packs)

        if coverage_ratio >= self.target_coverage and is_legit:
            self.current_mode = InMapOperationMode.MAP_COMPLETED
            return ExplorationStepDecision(
                mode=InMapOperationMode.MAP_COMPLETED,
                wasd_cmd=None,
                target_vector=(0.0, 0.0),
                coverage_ratio=coverage_ratio,
                packs_encountered=self.density_heatmap.total_packs_count,
                boss_detected=self.density_heatmap.boss_detected,
                boss_slain=self.boss_slain,
                reason=f"Đạt độ phủ sương mù {coverage_ratio*100:.1f}% (>= {self.target_coverage*100:.0f}%) và {legit_reason}",
            )

        # 5. Quyết định hành vi theo trạng thái chiến thuật
        # TRƯỜNG HỢP A: Đang trong đấu trường Boss (Boss Health Bar active)
        if arena_state.boss_bar_active:
            if arena_state.boss_bar_info.is_phased:
                self.current_mode = InMapOperationMode.BOSS_KITE
                return ExplorationStepDecision(
                    mode=InMapOperationMode.BOSS_KITE,
                    wasd_cmd=wasd_cmd,
                    target_vector=wasd_cmd.vector if wasd_cmd else (0.0, 0.0),
                    coverage_ratio=coverage_ratio,
                    packs_encountered=self.density_heatmap.total_packs_count,
                    boss_detected=True,
                    boss_slain=self.boss_slain,
                    reason="Boss đang chuyển pha bất tử -> Kiting thả diều né đòn",
                )
            else:
                self.current_mode = InMapOperationMode.BOSS_COMBAT
                return ExplorationStepDecision(
                    mode=InMapOperationMode.BOSS_COMBAT,
                    wasd_cmd=wasd_cmd,
                    target_vector=wasd_cmd.vector if wasd_cmd else (0.0, 0.0),
                    coverage_ratio=coverage_ratio,
                    packs_encountered=self.density_heatmap.total_packs_count,
                    boss_detected=True,
                    boss_slain=self.boss_slain,
                    reason=f"Giao tranh với Boss (Máu còn: {arena_state.boss_bar_info.health_ratio*100:.1f}%)",
                )

        # TRƯỜNG HỢP B: Đang đứng trước cổng sương mù đấu trường (Pre-buff Gate)
        if arena_state.gate_detected and not self.boss_slain:
            self.current_mode = InMapOperationMode.BOSS_PRE_BUFF
            return ExplorationStepDecision(
                mode=InMapOperationMode.BOSS_PRE_BUFF,
                wasd_cmd=wasd_cmd,
                target_vector=wasd_cmd.vector if wasd_cmd else (0.0, 0.0),
                coverage_ratio=coverage_ratio,
                packs_encountered=self.density_heatmap.total_packs_count,
                boss_detected=True,
                boss_slain=self.boss_slain,
                reason="Phát hiện cổng sương mù đấu trường -> Kích hoạt tiền buff trước khi bước qua cửa",
            )

        # TRƯỜNG HỢP C: Chạm trán bãi quái vật thông thường trong map
        if monsters_near >= 1:
            self.current_mode = InMapOperationMode.COMBAT_ENGAGE
            return ExplorationStepDecision(
                mode=InMapOperationMode.COMBAT_ENGAGE,
                wasd_cmd=wasd_cmd,
                target_vector=wasd_cmd.vector if wasd_cmd else (0.0, 0.0),
                coverage_ratio=coverage_ratio,
                packs_encountered=self.density_heatmap.total_packs_count,
                boss_detected=self.density_heatmap.boss_detected,
                boss_slain=self.boss_slain,
                reason=f"Chạm trán đàn quái vật ({monsters_near} quái) -> Ưu tiên Stutter-Step Combat dọn quái",
            )

        # TRƯỜNG HỢP D: Xung quanh an toàn -> Tiếp tục thâm nhập sương mù theo Frontier
        self.current_mode = InMapOperationMode.FRONTIER_EXPLORE
        return ExplorationStepDecision(
            mode=InMapOperationMode.FRONTIER_EXPLORE,
            wasd_cmd=wasd_cmd,
            target_vector=wasd_cmd.vector if wasd_cmd else (0.0, 0.0),
            coverage_ratio=coverage_ratio,
            packs_encountered=self.density_heatmap.total_packs_count,
            boss_detected=self.density_heatmap.boss_detected,
            boss_slain=self.boss_slain,
            reason=f"Tuần tra sương mù Frontier (Hướng: {wasd_cmd.direction.value if wasd_cmd else 'NONE'}, Độ phủ: {coverage_ratio*100:.1f}%)",
        )
