"""
Unit Tests for Atlas Node Selector & Precursor Tower Orchestrator (Doc 56)
==========================================================================
Kiểm thử các bất biến kiến trúc:
- INV-ATLAS-01: Safe Hideout Execution
- INV-ATLAS-02: Verified Node Target & Hover Confirmation
- INV-ATLAS-03: Zero Hot-Path Blocking
- INV-ATLAS-04: Zero Wishful Thinking
"""

import os
import pytest
from src.assistant_tool.atlas_node_selector import (
    AtlasNode,
    PrecursorTower,
    AtlasPerceptionEngine,
    AtlasStrategyPlanner,
    AtlasStrategyConfig,
    AtlasNodeSelector,
    ATLAS_FLAG_REQUIRE_HOVER_VERIFY,
    ATLAS_FLAG_SOCKET_PRECURSOR_TOWER,
    ATLAS_FLAG_AUTO_PROCEED_TO_WAYSTONE,
)


def test_atlas_perception_roi_scaling():
    engine = AtlasPerceptionEngine(ref_width=1920.0, ref_height=1080.0)
    # Trung tâm màn hình 1080p phải nằm trong Viewport ROI
    assert engine.is_in_viewport(960.0, 540.0, 1920.0, 1080.0) is True

    # Điểm ngoài rìa màn hình phải nằm ngoài Viewport
    assert engine.is_in_viewport(50.0, 50.0, 1920.0, 1080.0) is False

    # Scale sang 2560x1440
    sx, sy = engine.scale_point(960.0, 540.0, 2560.0, 1440.0)
    assert sx == 1280.0
    assert sy == 720.0


def test_atlas_strategy_whitelist_selection():
    cfg = AtlasStrategyConfig(
        mode="farm_whitelist",
        min_tier=14,
        max_tier=16,
        preferred_maps=["Deserted", "Fortress", "Creek"],
        ignored_maps=["Vaal Pyramid", "Maze"],
    )
    planner = AtlasStrategyPlanner(config=cfg)

    candidates = [
        AtlasNode(name="Creek Map", tier=14, screen_x=600.0, screen_y=400.0, bonus_completed=True),
        AtlasNode(name="Deserted Map", tier=15, screen_x=700.0, screen_y=500.0, bonus_completed=False),
        AtlasNode(name="Vaal Pyramid Map", tier=16, screen_x=800.0, screen_y=600.0, bonus_completed=False),
        AtlasNode(name="Random Normal Map", tier=14, screen_x=900.0, screen_y=700.0, bonus_completed=True),
    ]

    best = planner.select_best_node(candidates)
    assert best is not None
    # Deserted đứng đầu preferred_maps và hợp lệ -> phải được chọn
    assert "Deserted" in best.name
    assert best.tier == 15


def test_atlas_strategy_blacklist_rejection():
    cfg = AtlasStrategyConfig(
        mode="farm_whitelist",
        min_tier=14,
        max_tier=16,
        preferred_maps=["Deserted"],
        ignored_maps=["Vaal Pyramid"],
    )
    planner = AtlasStrategyPlanner(config=cfg)

    # Chỉ có map trong blacklist
    candidates = [
        AtlasNode(name="Vaal Pyramid Map", tier=15, screen_x=800.0, screen_y=600.0),
    ]

    best = planner.select_best_node(candidates)
    assert best is None, "Map trong danh sách đen bắt buộc phải bị loại bỏ!"


def test_atlas_strategy_bonus_rush():
    cfg = AtlasStrategyConfig(
        mode="bonus_rush",
        min_tier=14,
        max_tier=16,
        prioritize_uncompleted_bonus=True,
        preferred_maps=["Deserted", "Fortress"],
    )
    planner = AtlasStrategyPlanner(config=cfg)

    candidates = [
        AtlasNode(name="Deserted Map", tier=15, screen_x=700.0, screen_y=500.0, bonus_completed=True),
        AtlasNode(name="Fortress Map", tier=15, screen_x=750.0, screen_y=550.0, bonus_completed=False),
    ]

    best = planner.select_best_node(candidates)
    assert best is not None
    # Trong bonus rush, Fortress chưa lấy bonus phải được ưu tiên hơn Deserted đã lấy bonus
    assert best.name == "Fortress Map"
    assert best.bonus_completed is False


def test_inv_atlas_01_safe_hideout_execution():
    selector = AtlasNodeSelector()

    # Thử chạy tại chiến trường có quái vật -> Phải từ chối ngay lập tức
    candidates = [
        AtlasNode(name="Deserted Map", tier=15, screen_x=700.0, screen_y=500.0)
    ]
    res_danger = selector.plan_and_execute(
        candidate_nodes=candidates,
        area_name="Creek",
        monster_count=5,
    )
    assert res_danger["success"] is False
    assert "INV-ATLAS-01" in res_danger["error"]

    # Chạy tại Shoreline Hideout với 0 quái vật -> Thành công
    res_safe = selector.plan_and_execute(
        candidate_nodes=candidates,
        area_name="Shoreline Hideout",
        monster_count=0,
    )
    assert res_safe["success"] is True
    assert res_safe["selected_node"].name == "Deserted Map"


def test_precursor_tower_socket_flag():
    selector = AtlasNodeSelector()
    candidates = [
        AtlasNode(name="Deserted Map", tier=15, screen_x=700.0, screen_y=500.0)
    ]
    tower = PrecursorTower(screen_x=960.0, screen_y=380.0, empty_sockets=2)

    res = selector.plan_and_execute(
        candidate_nodes=candidates,
        area_name="Shoreline Hideout",
        monster_count=0,
        available_tower=tower,
    )
    assert res["success"] is True
    assert res["tower_socketed"] is True


def test_inv_atlas_02_out_of_viewport_fail_closed():
    selector = AtlasNodeSelector()
    # Tọa độ ngoài Viewport ROI (ví dụ góc trên màn hình x=100, y=50)
    invalid_candidates = [
        AtlasNode(name="Deserted Map", tier=15, screen_x=100.0, screen_y=50.0)
    ]

    res = selector.plan_and_execute(
        candidate_nodes=invalid_candidates,
        area_name="Shoreline Hideout",
        monster_count=0,
    )
    assert res["success"] is False
    assert "INV-ATLAS-02" in res["error"]
    assert res["selected_node"] is None


def test_inv_atlas_02_occlusion_fail_closed():
    selector = AtlasNodeSelector()
    # Tọa độ rơi vào vùng chatbox bên trái khi chat_open=True (x=400, y=700)
    occluded_candidates = [
        AtlasNode(name="Deserted Map", tier=15, screen_x=400.0, screen_y=700.0)
    ]

    # Khi chat mở -> Phải fail-closed từ chối
    res_chat = selector.plan_and_execute(
        candidate_nodes=occluded_candidates,
        area_name="Shoreline Hideout",
        monster_count=0,
        chat_open=True,
    )
    assert res_chat["success"] is False
    assert "INV-ATLAS-02" in res_chat["error"]

    # Khi chat đóng -> Nằm trong viewport hợp lệ
    res_normal = selector.plan_and_execute(
        candidate_nodes=occluded_candidates,
        area_name="Shoreline Hideout",
        monster_count=0,
        chat_open=False,
    )
    assert res_normal["success"] is True
    assert res_normal["selected_node"].name == "Deserted Map"


def test_precursor_tower_outside_viewport_rejection():
    selector = AtlasNodeSelector()
    candidates = [
        AtlasNode(name="Deserted Map", tier=15, screen_x=700.0, screen_y=500.0)
    ]
    # Tháp nằm ngoài màn hình (x=50, y=50)
    out_tower = PrecursorTower(screen_x=50.0, screen_y=50.0, empty_sockets=1)

    res = selector.plan_and_execute(
        candidate_nodes=candidates,
        area_name="Shoreline Hideout",
        monster_count=0,
        available_tower=out_tower,
    )
    assert res["success"] is True
    # Tháp ngoài viewport không được socket
    assert res["tower_socketed"] is False


def test_atlas_selector_select_node_direct():
    selector = AtlasNodeSelector()
    node = AtlasNode(name="Deserted Map", tier=15, screen_x=1275.0, screen_y=660.0)
    res = selector.select_node(node, area_name="Shoreline Hideout", monster_count=0, screen_w=2560.0, screen_h=1440.0)
    assert res["success"] is True
    assert res["selected_node"].name == "Deserted Map"

