"""
AutoPOE2 Item Inspector & Hover Watcher Unit Tests
100% Authentic Real Data POE2 (Rule 14), Zero Mock Data, Rule 8, Rule 9, Rule 10.
Kiểm thử bóc tách, đánh giá và phản hồi của hệ thống Item Inspector, Hover Watcher và Tooltip Overlay.
"""

import os
import sys
from unittest.mock import patch
import pytest

sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))

from src.assistant_tool.item_inspector import (
    AffixType,
    InspectionSeverity,
    ItemCategory,
    ItemInspectionReport,
    ItemRarity,
    UniversalItemInspector,
    UniversalItemParser,
    inspect_item,
)
from src.assistant_tool.item_hover_watcher import ItemHoverWatcher
from src.assistant_tool.item_tooltip_overlay import ItemTooltipOverlay


# ==============================================================================
# DỮ LIỆU THẬT 100% TỪ CLIENT PATH OF EXILE 2 (CLIPBOARD DUMP)
# ==============================================================================

REAL_POE2_WAYSTONE_BRICK = """Item Class: Waystones
Rarity: Rare
Torment Pillar
Waystone (Tier 15)
--------
Waystone Tier: 15
Item Level: 82
--------
Monsters reflect 18% of Physical Damage
Players cannot Regenerate Life, Mana or Energy Shield
Monsters deal 45% extra Damage as Fire
--------
Corrupted"""

REAL_POE2_WAYSTONE_REWARD = """Item Class: Waystones
Rarity: Rare
Bramble Shrine
Waystone (Tier 16)
--------
Waystone Tier: 16
Item Level: 83
--------
85% increased Quantity of Items found in this Area
50% increased Rarity of Items found in this Area
32% increased Monster Pack Size"""

REAL_POE2_WAYSTONE_T14 = """Item Class: Waystones
Rarity: Rare
Forgotten Course
Waystone (Tier 14)
--------
Revives Available: 2
Pack Size: +26%
Monster Rarity: +18%
Waystone Drop Chance: +70%
--------
Item Level: 80
--------
{ Prefix Modifier "Venomous" (Tier: 1) }
Monsters have 30(27-33)% chance to Poison on Hit
{ Prefix Modifier "Fleeting" (Tier: 1) }
Monsters have 15(10-15)% increased Attack, Cast and Movement Speed
{ Suffix Modifier "of Exposure" (Tier: 1) }
-7% maximum Player Resistances
{ Suffix Modifier "of Obstruction" (Tier: 1) }
Monsters take 28(26-30)% reduced Extra Damage from Critical Hits
--------
Can be used in a Map Device, allowing you to enter a Map. Waystones can only be used once.
"""

REAL_POE2_BODY_ARMOUR = """Item Class: Body Armours
Rarity: Rare
Morbid Carapace
Expert Iron Cuirass
--------
Armour: 540 (augmented)
--------
Requirements:
Level: 65
Str: 120
--------
Sockets: S S S
--------
Item Level: 82
--------
+115 to maximum Life
+350 to Armour
+44% to Fire Resistance
+42% to Lightning Resistance"""

REAL_POE2_BOOTS = """Item Class: Boots
Rarity: Rare
Storm Stride
Expert Wrapped Boots
--------
Evasion Rating: 280
--------
Requirements:
Level: 65
Dex: 110
--------
Item Level: 80
--------
+35% increased Movement Speed
+14% chance to Suppress Spell Damage
+38% to Cold Resistance"""

REAL_POE2_RING = """Item Class: Rings
Rarity: Rare
Doom Spiral
Diamond Ring
--------
Requirements:
Level: 60
--------
Item Level: 78
--------
+24% to Global Critical Strike Multiplier
+22% to Chaos Resistance
+75 to maximum Life"""

REAL_POE2_FLASK = """Item Class: Life Flasks
Rarity: Magic
Surgeon's Ultimate Life Flask of Staunching
--------
Recovers 1800 Life over 4.00 Seconds
--------
Requirements:
Level: 60
--------
Item Level: 75
--------
Grants Immunity to Bleeding for 4 seconds
Grants Immunity to Shock during Effect"""

REAL_POE2_ADVANCED_TAGS = """Item Class: Body Armours
Rarity: Rare
Morbid Carapace
Expert Iron Cuirass
--------
Armour: 540
--------
Requirements:
Level: 65
Str: 120
--------
Item Level: 82
--------
{ Prefix Modifier "Vigorous" (Tier: 1) — Life }
+115 to maximum Life
{ Prefix Modifier "Reinforced" (Tier: 2) — Defences }
+350 to Armour
{ Suffix Modifier "of the Drake" (Tier: 1) — Fire Resistance }
+44% to Fire Resistance
{ Suffix Modifier "of the Tempest" (Tier: 2) — Lightning Resistance }
+42% to Lightning Resistance"""


# ==============================================================================
# TEST CASES CHÍNH XÁC THEO YÊU CẦU ĐẶC TẢ
# ==============================================================================

def test_1_waystone_brick_reflect_and_no_regen():
    """Test 1: Waystone với Reflect Physical + No Regen (báo Brick Đỏ, safety score thấp)."""
    report = inspect_item(REAL_POE2_WAYSTONE_BRICK)
    assert report is not None
    assert report.category == ItemCategory.WAYSTONE
    assert report.rarity == ItemRarity.RARE
    assert report.is_brick is True
    assert report.safety_score <= 50
    assert len(report.red_mods) >= 2
    assert any("reflect" in m.lower() for m in report.red_mods)
    assert any("cannot Regenerate" in m or "No Regen" in m for m in report.red_mods)
    assert "BRICK" in report.summary

    # Kiểm tra ordered_modifiers
    assert len(report.ordered_modifiers) == 3
    assert report.ordered_modifiers[0].severity == InspectionSeverity.BRICK
    assert report.ordered_modifiers[1].severity == InspectionSeverity.BRICK
    assert report.ordered_modifiers[2].severity == InspectionSeverity.DANGEROUS
    assert not any(m.text.startswith("[") for m in report.ordered_modifiers)


def test_waystone_t14_ordered_modifiers_and_in_place_clean_styling():
    """
    Test kiểm tra Waystone T14 (thực tế từ client PoE2):
    1. Kiểm tra report.ordered_modifiers chứa đúng thứ tự các dòng của Waystone T14.
    2. Kiểm tra dòng -7% maximum Player Resistances được đánh dấu DANGEROUS (Đỏ) và giữ nguyên text sạch sẽ không có tag [Ailment on Hit].
    3. Kiểm tra dòng Pack Size: +26% được đánh dấu REWARD (Xanh).
    """
    report = inspect_item(REAL_POE2_WAYSTONE_T14)
    assert report is not None
    assert report.category == ItemCategory.WAYSTONE
    assert report.rarity == ItemRarity.RARE

    # 1. Kiểm tra report.ordered_modifiers chứa đúng thứ tự các dòng của Waystone T14
    expected_lines = [
        "Revives Available: 2",
        "Pack Size: +26%",
        "Monster Rarity: +18%",
        "Waystone Drop Chance: +70%",
        "Monsters have 30(27-33)% chance to Poison on Hit",
        "Monsters have 15(10-15)% increased Attack, Cast and Movement Speed",
        "-7% maximum Player Resistances",
        "Monsters take 28(26-30)% reduced Extra Damage from Critical Hits",
    ]
    actual_lines = [m.text for m in report.ordered_modifiers]
    assert actual_lines == expected_lines

    # Kiểm tra thứ tự original_order
    assert [m.original_order for m in report.ordered_modifiers] == list(range(len(expected_lines)))

    # 2. Kiểm tra dòng -7% maximum Player Resistances được đánh dấu DANGEROUS (Đỏ) và giữ nguyên text sạch sẽ
    res_mods = [m for m in report.ordered_modifiers if "maximum Player Resistances" in m.text]
    assert len(res_mods) == 1
    res_mod = res_mods[0]
    assert res_mod.severity == InspectionSeverity.DANGEROUS
    assert res_mod.text == "-7% maximum Player Resistances"
    assert not res_mod.text.startswith("[")
    assert "[Ailment on Hit]" not in res_mod.text
    assert "[-Max Res]" not in res_mod.text
    assert res_mod.color_code == "#FF4D4D"

    # 3. Kiểm tra dòng Pack Size: +26% được đánh dấu REWARD (Xanh)
    pack_mods = [m for m in report.ordered_modifiers if "Pack Size: +26%" in m.text]
    assert len(pack_mods) == 1
    pack_mod = pack_mods[0]
    assert pack_mod.severity == InspectionSeverity.REWARD
    assert pack_mod.text == "Pack Size: +26%"
    assert not pack_mod.text.startswith("[")
    assert "[Pack Size]" not in pack_mod.text
    assert pack_mod.color_code == "#2ECC71"

    # Toàn bộ danh sách mod không chứa tiền tố thô thiển
    assert not any(m.text.startswith("[") for m in report.ordered_modifiers)
    assert any("Monsters have 30(27-33)% chance to Poison on Hit" == m.text and m.severity == InspectionSeverity.DANGEROUS for m in report.ordered_modifiers)


def test_2_waystone_high_quantity_and_pack_size():
    """Test 2: Waystone với High Quantity (85%) + Pack Size (32%) (báo Xanh Lá, an toàn 100)."""
    report = inspect_item(REAL_POE2_WAYSTONE_REWARD)
    assert report is not None
    assert report.category == ItemCategory.WAYSTONE
    assert report.is_brick is False
    assert report.safety_score == 100
    assert len(report.green_mods) >= 2
    assert any("Quantity" in m for m in report.green_mods)
    assert any("Pack Size" in m for m in report.green_mods)
    assert "AN TOÀN" in report.summary


def test_3_body_armour_t1_life_res_and_open_affixes():
    """
    Test 3: Body Armour với T1 Máu (+115) + Kháng Lửa (+44%) + Kháng Sét (+42%)
    + 1 Open Suffix + 1 Open Prefix (báo Xanh Lá cho Máu/Kháng và Vàng cho 1 Open Suffix).
    """
    report = inspect_item(REAL_POE2_BODY_ARMOUR)
    assert report is not None
    assert report.category == ItemCategory.BODY_ARMOUR
    assert report.rarity == ItemRarity.RARE
    assert report.item_level == 82
    assert report.open_prefixes == 1
    assert report.open_suffixes == 1

    # Báo Xanh lá cho Máu và Kháng
    assert any("115 to maximum Life" in m for m in report.green_mods)
    assert any("44% to Fire Resistance" in m for m in report.green_mods)
    assert any("42% to Lightning Resistance" in m for m in report.green_mods)

    # Báo Vàng cho Open Suffix và Open Prefix
    assert any("1 Open Suffix" in m for m in report.yellow_mods)
    assert any("1 Open Prefix" in m for m in report.yellow_mods)


def test_4_boots_movement_speed_and_spell_suppression():
    """Test 4: Giày với 35% Movement Speed + Spell Suppression (báo God Roll Xanh)."""
    report = inspect_item(REAL_POE2_BOOTS)
    assert report is not None
    assert report.category == ItemCategory.BOOTS
    assert report.rarity == ItemRarity.RARE
    # 35% Movement Speed đạt chuẩn God Roll (>= 30%)
    assert any("35% increased Movement Speed" in m for m in report.green_mods)
    assert any("Suppress Spell Damage" in m for m in report.green_mods)
    assert any("Cold Resistance" in m for m in report.green_mods)
    assert report.quality_score >= 80
    assert "GOD ROLL" in report.summary


def test_5_ring_crit_multi_chaos_res_and_life():
    """Test 5: Nhẫn với Crit Multiplier + Chaos Resistance + Life."""
    report = inspect_item(REAL_POE2_RING)
    assert report is not None
    assert report.category == ItemCategory.RING
    assert report.rarity == ItemRarity.RARE
    assert any("Critical Strike Multiplier" in m for m in report.green_mods)
    assert any("Chaos Resistance" in m for m in report.green_mods)
    assert any("75 to maximum Life" in m for m in report.green_mods)
    assert len(report.green_mods) >= 3


def test_6_flask_shock_and_bleed_immunity():
    """Test 6: Bình Flask với Shock & Bleed Immunity."""
    report = inspect_item(REAL_POE2_FLASK)
    assert report is not None
    assert report.category == ItemCategory.FLASK
    assert report.rarity == ItemRarity.MAGIC
    assert any("Bleeding" in m for m in report.green_mods)
    assert any("Shock" in m for m in report.green_mods)


def test_7_open_affixes_calculation_rare_and_magic():
    """Test 7: Kiểm tra tính toán Open Affixes cho đồ Rare (3P/3S) và đồ Magic (1P/1S)."""
    # 1. Rare với 2 Prefix và 1 Suffix -> 1 Open Prefix, 2 Open Suffixes
    rare_2p1s = """Item Class: Helmets
Rarity: Rare
Gloom Crown
Soldier Helmet
--------
Item Level: 75
--------
+80 to maximum Life
+100 to Armour
+36% to Fire Resistance"""
    r1 = inspect_item(rare_2p1s)
    assert r1 is not None
    assert r1.max_prefixes == 3
    assert r1.max_suffixes == 3
    assert r1.open_prefixes == 1
    assert r1.open_suffixes == 2

    # 2. Rare đầy đủ 3 Prefix và 3 Suffix -> 0 Open Prefix, 0 Open Suffix (Full)
    rare_3p3s = """Item Class: Helmets
Rarity: Rare
Gloom Crown
Soldier Helmet
--------
Item Level: 80
--------
+90 to maximum Life
+150 to Armour
+50 to maximum Mana
+40% to Fire Resistance
+40% to Cold Resistance
+40% to Lightning Resistance"""
    r2 = inspect_item(rare_3p3s)
    assert r2 is not None
    assert r2.open_prefixes == 0
    assert r2.open_suffixes == 0

    # 3. Magic với 1 Prefix và 0 Suffix -> 0 Open Prefix, 1 Open Suffix
    magic_1p0s = """Item Class: Body Armours
Rarity: Magic
Vigorous Iron Cuirass
--------
Item Level: 50
--------
+85 to maximum Life"""
    r3 = inspect_item(magic_1p0s)
    assert r3 is not None
    assert r3.max_prefixes == 1
    assert r3.max_suffixes == 1
    assert r3.open_prefixes == 0
    assert r3.open_suffixes == 1

    # 4. Magic với 0 Prefix và 1 Suffix -> 1 Open Prefix, 0 Open Suffix
    magic_0p1s = """Item Class: Body Armours
Rarity: Magic
Iron Cuirass of the Drake
--------
Item Level: 50
--------
+38% to Fire Resistance"""
    r4 = inspect_item(magic_0p1s)
    assert r4 is not None
    assert r4.open_prefixes == 1
    assert r4.open_suffixes == 0

    # 5. Magic với 1 Prefix và 1 Suffix -> 0 Open Prefix, 0 Open Suffix
    magic_1p1s = """Item Class: Body Armours
Rarity: Magic
Vigorous Iron Cuirass of the Drake
--------
Item Level: 50
--------
+85 to maximum Life
+38% to Fire Resistance"""
    r5 = inspect_item(magic_1p1s)
    assert r5 is not None
    assert r5.open_prefixes == 0
    assert r5.open_suffixes == 0


def test_advanced_mod_tags_parsing():
    """Kiểm tra nhận diện chính xác các thẻ Advanced Mod tag { Prefix/Suffix Modifier (Tier: X) }."""
    report = inspect_item(REAL_POE2_ADVANCED_TAGS)
    assert report is not None
    assert report.open_prefixes == 1
    assert report.open_suffixes == 1
    assert len(report.raw_mods) == 4

    # Kiểm tra mod Life Tier 1
    life_mod = [m for m in report.raw_mods if m.category == "Life"][0]
    assert life_mod.affix_type == AffixType.PREFIX
    assert life_mod.tier == 1
    assert life_mod.severity == InspectionSeverity.GOD_ROLL

    # Kiểm tra mod Fire Resistance Tier 1
    fire_mod = [m for m in report.raw_mods if "Fire" in m.text][0]
    assert fire_mod.affix_type == AffixType.SUFFIX
    assert fire_mod.tier == 1


def test_item_hover_watcher_logic():
    """Kiểm tra Hover Dwell Tracker, Focus Interlock và Callback dispatch của ItemHoverWatcher."""
    inspected_events = []
    cursor_moved_events = []

    watcher = ItemHoverWatcher(
        on_item_inspected=lambda r, x, y: inspected_events.append((r, x, y)),
        on_cursor_moved=lambda: cursor_moved_events.append(True),
        dwell_time_ms=100.0,
        poll_interval_s=0.025,
        enable_focus_interlock=True,
        simulate_keys=True,
    )

    # 1. Giả lập game không focus -> không trigger
    watcher._mock_is_focused = False
    watcher._mock_clipboard_text = REAL_POE2_BODY_ARMOUR

    state = {"last_pos": None, "anchor_pos": None, "dwell_accum_ms": 0.0, "has_triggered": False}

    # Đứng yên 150ms tại (500, 500)
    for _ in range(6):
        watcher.process_tick(500, 500, state)

    assert len(inspected_events) == 0  # Bị chặn bởi Focus Interlock!

    # 2. Giả lập game được focus -> trigger inspect
    watcher._mock_is_focused = True
    state = {"last_pos": None, "anchor_pos": None, "dwell_accum_ms": 0.0, "has_triggered": False}

    for _ in range(6):  # 6 * 25ms = 150ms >= 100ms
        watcher.process_tick(500, 500, state)

    assert len(inspected_events) == 1
    rep, cx, cy = inspected_events[0]
    assert rep.category == ItemCategory.BODY_ARMOUR
    assert cx == 500 and cy == 500
    assert watcher.tooltip_active is True

    # 3. Chuột di chuyển > 8px -> on_cursor_moved được gọi tức thì (0ms)
    watcher.process_tick(515, 515, state)
    assert len(cursor_moved_events) == 1
    assert watcher.tooltip_active is False


def test_item_tooltip_overlay_lifecycle():
    """Kiểm tra khởi tạo và chu kỳ hiển thị show/hide của ItemTooltipOverlay."""
    overlay = ItemTooltipOverlay()
    if overlay.window is None:
        pytest.skip("Bỏ qua test GUI trong môi trường không có DISPLAY")

    report = inspect_item(REAL_POE2_BODY_ARMOUR)
    assert report is not None

    overlay.show(report, 200, 200)
    assert overlay.is_visible is True

    overlay.hide()
    assert overlay.is_visible is False


def test_focus_interlock_allows_own_process():
    """Test xác nhận is_game_window_focused() trả về True khi pid == os.getpid() (Focus Interlock không tự chặn)."""
    watcher = ItemHoverWatcher(enable_focus_interlock=True)
    watcher._mock_is_focused = None  # Đảm bảo logic kiểm tra Win32 Foreground Window thật được gọi

    # 1. Giả lập foreground window thuộc tiến trình hiện tại (pid == os.getpid())
    # Tiêu đề và ClassName hoàn toàn không liên quan tới POE (ví dụ: Terminal hoặc Tkinter Overlay)
    with patch("win32gui.GetForegroundWindow", return_value=88888), \
         patch("win32gui.GetWindowText", return_value="Item Inspector Tkinter Overlay"), \
         patch("win32gui.GetClassName", return_value="TkTopLevel"), \
         patch("win32process.GetWindowThreadProcessId", return_value=(0, os.getpid())):
        assert watcher.is_game_window_focused() is True

    # 2. Đối chiếu ngược: Khi pid là tiến trình khác không nằm trong allowed_processes (ví dụ: trình duyệt Chrome)
    with patch("win32gui.GetForegroundWindow", return_value=99999), \
         patch("win32gui.GetWindowText", return_value="Google Chrome"), \
         patch("win32gui.GetClassName", return_value="Chrome_WidgetWin_1"), \
         patch("win32process.GetWindowThreadProcessId", return_value=(0, 999999)), \
         patch("psutil.Process") as mock_proc:
        mock_proc.return_value.name.return_value = "chrome.exe"
        assert watcher.is_game_window_focused() is False


def test_item_tooltip_overlay_noactivate_and_click_through():
    """Kiểm tra ItemTooltipOverlay áp dụng cờ WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOACTIVATE và SetWindowPos."""
    overlay = ItemTooltipOverlay()
    if overlay.window is None or overlay.target_hwnd is None:
        pytest.skip("Bỏ qua test Win32 GUI trong môi trường không có DISPLAY")

    import win32con
    import win32gui

    style = win32gui.GetWindowLong(overlay.target_hwnd, win32con.GWL_EXSTYLE)
    WS_EX_LAYERED = 0x00080000
    WS_EX_TRANSPARENT = 0x00000020
    WS_EX_NOACTIVATE = 0x08000000
    expected_mask = WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOACTIVATE
    assert (style & expected_mask) == expected_mask

    report = inspect_item(REAL_POE2_BODY_ARMOUR)
    overlay.show(report, 200, 200)
    assert overlay.is_visible is True

    overlay.hide()
    assert overlay.is_visible is False

