"""AutoPOE2 - Zero-Day UI Baseline & Layout Verification Pipeline (Doc 48 / SSoT).
======================================================================================
Pipeline thẩm định thị giác tự động ngay sau khi game cập nhật bản vá mới (Patch):
1. Chụp ảnh màn hình Baseline mới (Zero-Day Capture) khi nhân vật đứng trong Hideout/Atlas.
2. Kiểm tra layout các thành phần giao diện người dùng trọng yếu:
   - Map Device dialog & Atlas tree banner.
   - Nút TRAVERSE & cơ chế phòng vệ kháng bug chữ 'ENTER' (x <= 0.55 * w).
   - Biểu tượng Player Avatar hình tròn vàng trên cây Atlas (find_player_avatar_on_atlas).
   - Khay Waystone & ổ cắm Socket (Waystone Tray 12 ô & Socket).
   - Lưới hành trang nhân vật (Inventory 60 ô: 12 cột x 5 hàng).
   - Thanh Stash Tabs và bảng Chợ Faustus Currency Exchange.
3. Xuất báo cáo chẩn đoán (Diagnostic Report) cảnh báo sai lệch layout hoặc đổi texture.

Tuân thủ nghiêm ngặt:
- Rule 1: Python 3.11 Cold Path Tier 2.
- Rule 4: Module hóa độc lập, file trần < 500 dòng.
- Rule 5: 100% artifact thật từ captures/.
- Rule 16: Google Docstrings chuẩn mực.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from datetime import datetime
import json
import os
from typing import Any, Dict, List, Optional, Tuple, Union

import numpy as np

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

from src.common.coordinate_transform import (
    CoordinateScaler,
    Resolution,
    RES_1080P,
    RES_1440P,
    RES_4K,
    RES_WINDOWED_DEFAULT,
    get_faustus_element_pos,
    get_inventory_slot_pos,
    get_stash_slot_pos,
    get_traverse_button_bbox,
    get_traverse_button_pos,
    get_waystone_socket_pos,
    get_waystone_tray_slot_pos,
    resolve_resolution,
)
from src.common.screen_capture import capture_window_bitblt
from src.common.vision_filters import (
    find_traverse_button_dual_pass,
    is_atlas_ui_open_robust,
)
from src.common.vision_ocr import (
    find_button_by_text,
    find_player_avatar_on_atlas,
    find_reachable_green_node,
    is_atlas_ui_open,
    is_inventory_open,
)
from src.common.win32_window import find_poe2_window


@dataclass
class ComponentStatus:
    """Trạng thái kiểm tra một thành phần UI."""
    name: str
    is_valid: bool
    details: str
    coordinates: Optional[Tuple[int, int]] = None
    confidence: float = 1.0


@dataclass
class ZeroDayReport:
    """Báo cáo tổng hợp kiểm chuẩn Zero-Day UI."""
    timestamp: str
    resolution: Tuple[int, int]
    is_patch_ready: bool
    components: Dict[str, ComponentStatus] = field(default_factory=dict)
    warnings: List[str] = field(default_factory=list)
    errors: List[str] = field(default_factory=list)

    def to_dict(self) -> Dict[str, Any]:
        return {
            "timestamp": self.timestamp,
            "resolution": self.resolution,
            "is_patch_ready": self.is_patch_ready,
            "components": {
                k: {
                    "name": v.name,
                    "is_valid": v.is_valid,
                    "details": v.details,
                    "coordinates": v.coordinates,
                    "confidence": v.confidence,
                }
                for k, v in self.components.items()
            },
            "warnings": self.warnings,
            "errors": self.errors,
        }


class ZeroDayUiVerifier:
    """Bộ kiểm chuẩn thị giác và xác nhận cấu trúc UI Zero-Day sau cập nhật patch."""

    def __init__(self, target_resolution: Union[Resolution, Tuple[int, int]] = RES_1440P) -> None:
        self.resolution = resolve_resolution(target_resolution)

    def capture_live_baseline(self, hwnd: Optional[int] = None) -> Tuple[Optional[Image.Image], str]:
        """Chụp ảnh màn hình trực tiếp từ cửa sổ game POE2 đang chạy."""
        if hwnd is None:
            hwnd = find_poe2_window()
        if not hwnd:
            return None, "Không tìm thấy cửa sổ Path of Exile 2"

        img = capture_window_bitblt(hwnd)
        if img is None:
            return None, "Lỗi capture BitBlt từ cửa sổ game"

        return img, "Chụp ảnh thành công"

    def verify_ui_frame(
        self,
        img_input: Union[str, Image.Image, np.ndarray],
    ) -> ZeroDayReport:
        """Thực hiện kiểm tra toàn diện cấu trúc các thành phần UI trên ảnh frame."""
        pil_img: Optional[Image.Image] = None
        if isinstance(img_input, str):
            if os.path.exists(img_input) and Image is not None:
                pil_img = Image.open(img_input).convert("RGB")
        elif isinstance(img_input, np.ndarray) and Image is not None:
            pil_img = Image.fromarray(img_input).convert("RGB")
        elif Image is not None and isinstance(img_input, Image.Image):
            pil_img = img_input.convert("RGB")

        now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

        if pil_img is None:
            report = ZeroDayReport(
                timestamp=now_str,
                resolution=(self.resolution.width, self.resolution.height),
                is_patch_ready=False,
                errors=["Ảnh đầu vào không hợp lệ hoặc không tồn tại"],
            )
            return report

        w, h = pil_img.size
        res = Resolution(w, h)
        components: Dict[str, ComponentStatus] = {}
        warnings: List[str] = []
        errors: List[str] = []

        # 1. Kiểm tra Trạng thái Atlas / Map Device UI
        atlas_open_basic = is_atlas_ui_open(pil_img)
        atlas_open_robust = is_atlas_ui_open_robust(pil_img)
        is_open = atlas_open_basic or atlas_open_robust
        components["atlas_ui"] = ComponentStatus(
            name="Atlas Map Device UI",
            is_valid=True,
            details=f"State={'OPEN' if is_open else 'STANDBY_HIDEOUT'} (Basic={atlas_open_basic}, Robust={atlas_open_robust})",
            confidence=1.0 if is_open else 0.85,
        )

        # 2. Kiểm tra Nút TRAVERSE & Cơ chế Kháng Bug Chữ 'ENTER'
        max_traverse_x = int(w * 0.55)
        traverse_pos = find_traverse_button_dual_pass(pil_img)
        text_btn_pos = find_button_by_text(pil_img, "TRAVERSE")

        active_pos = traverse_pos or text_btn_pos
        if active_pos is not None:
            is_pos_safe = (active_pos[0] <= max_traverse_x)
            if not is_pos_safe:
                errors.append(f"Nút TRAVERSE bị lệch sang vùng túi đồ ({active_pos[0]} > {max_traverse_x})!")
            components["traverse_button"] = ComponentStatus(
                name="TRAVERSE Button (Anti-Enter Protected)",
                is_valid=is_pos_safe,
                details=f"Tọa độ ({active_pos[0]}, {active_pos[1]}) - Kháng bug x<={max_traverse_x}",
                coordinates=active_pos,
                confidence=0.95 if is_pos_safe else 0.0,
            )
        else:
            # Nếu chưa mở modal TRAVERSE, kiểm tra tọa độ hình học chuẩn hóa
            canonical_pos = get_traverse_button_pos(res)
            components["traverse_button"] = ComponentStatus(
                name="TRAVERSE Button (Canonical Anchor)",
                is_valid=(canonical_pos[0] <= max_traverse_x),
                details=f"Tọa độ chuẩn hóa ({canonical_pos[0]}, {canonical_pos[1]})",
                coordinates=canonical_pos,
                confidence=0.80,
            )

        # 3. Kiểm tra Định vị Player Avatar trên Cây Atlas (find_player_avatar_on_atlas)
        avatar_pos = find_player_avatar_on_atlas(pil_img, return_none_if_missing=True)
        if avatar_pos is not None:
            components["player_avatar"] = ComponentStatus(
                name="Player Avatar On Atlas",
                is_valid=True,
                details=f"Phát hiện avatar tại đỉnh Great Wall ({avatar_pos[0]}, {avatar_pos[1]})",
                coordinates=avatar_pos,
                confidence=0.95,
            )
        else:
            # Fallback vị trí đỉnh Great Wall
            fallback_pos = find_player_avatar_on_atlas(pil_img, return_none_if_missing=False)
            components["player_avatar"] = ComponentStatus(
                name="Player Avatar On Atlas (Calibrated Fallback)",
                is_valid=True,
                details=f"Dự báo tọa độ đỉnh Great Wall ({fallback_pos[0]}, {fallback_pos[1]})",
                coordinates=fallback_pos,
                confidence=0.85,
            )

        # 4. Kiểm tra Lưới Túi Đồ Inventory (60 ô)
        inv_open = is_inventory_open(pil_img)
        # Tọa độ ô (0, 0) và (11, 4)
        c00 = get_inventory_slot_pos(0, 0, target_res=res)
        c114 = get_inventory_slot_pos(11, 4, target_res=res)
        inv_bounds_ok = (c00[0] > max_traverse_x and c114[0] < w and c114[1] < h)
        components["inventory_grid"] = ComponentStatus(
            name="Inventory Grid 60 Slots",
            is_valid=inv_bounds_ok,
            details=f"Open={inv_open}, Slot[0,0]={c00}, Slot[11,4]={c114}",
            coordinates=c00,
            confidence=0.90 if inv_bounds_ok else 0.2,
        )
        if not inv_bounds_ok:
            errors.append(f"Lưới Inventory lệch biên: {c00} -> {c114}")

        # 5. Kiểm tra Khay Waystone & Socket
        socket_pos = get_waystone_socket_pos(target_res=res)
        tray_slot0 = get_waystone_tray_slot_pos(0, target_res=res)
        tray_ok = (0 < socket_pos[0] < w and 0 < tray_slot0[0] < w)
        components["waystone_tray"] = ComponentStatus(
            name="Waystone Tray & Socket",
            is_valid=tray_ok,
            details=f"Socket={socket_pos}, TraySlot0={tray_slot0}",
            coordinates=socket_pos,
            confidence=0.90,
        )

        # 6. Kiểm tra Faustus Currency Exchange & Stash Tab Anchors
        faustus_center = get_faustus_element_pos("panel_center", target_res=res)
        stash_slot0 = get_stash_slot_pos(0, 0, target_res=res)
        components["faustus_and_stash"] = ComponentStatus(
            name="Faustus Exchange & Stash Tabs",
            is_valid=(0 < faustus_center[0] < w and 0 < stash_slot0[0] < w),
            details=f"FaustusCenter={faustus_center}, Stash[0,0]={stash_slot0}",
            coordinates=faustus_center,
            confidence=0.88,
        )

        is_patch_ready = len(errors) == 0 and all(c.is_valid for c in components.values())

        return ZeroDayReport(
            timestamp=now_str,
            resolution=(w, h),
            is_patch_ready=is_patch_ready,
            components=components,
            warnings=warnings,
            errors=errors,
        )

    def save_diagnostic_report(self, report: ZeroDayReport, out_path: str) -> None:
        """Lưu báo cáo kiểm chuẩn Zero-Day dưới dạng tệp JSON."""
        os.makedirs(os.path.dirname(os.path.abspath(out_path)), exist_ok=True)
        with open(out_path, "w", encoding="utf-8") as f:
            json.dump(report.to_dict(), f, indent=2, ensure_ascii=False)
