"""
Unit tests for FreeExile Monster Loot Drop Generation, Ground Pickup, and Intelligent Filter.
Follows Autonomous TDD Closed-Loop standards.
"""

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

import pytest
import time
from inventory.inventory_types import ItemType, ItemRarity, InventoryItem
from inventory.inventory_service import InventoryService
from inventory.loot_filter_types import FilterPreset, LootFilterConfig, GroundLootDrop
from inventory.loot_drop_service import LootDropService


@pytest.fixture
def inventory_service() -> InventoryService:
    return InventoryService()


@pytest.fixture
def loot_service(inventory_service: InventoryService) -> LootDropService:
    return LootDropService(inventory_service)


def test_loot_filter_presets_evaluation():
    common_item = InventoryItem(
        item_uuid="it_1", item_id="wpn_sword_common", name="Thô Thiết Kiếm",
        item_type=ItemType.WEAPON, rarity=ItemRarity.PHAM_PHAM, item_level=10,
        width=1, height=3
    )
    magic_item = InventoryItem(
        item_uuid="it_2", item_id="wpn_sword_magic", name="Thanh Phong Kiếm",
        item_type=ItemType.WEAPON, rarity=ItemRarity.LINH_PHAM, item_level=50,
        width=1, height=3
    )
    rare_item = InventoryItem(
        item_uuid="it_3", item_id="wpn_sword_rare", name="Xích Diễm Cự Đao",
        item_type=ItemType.WEAPON, rarity=ItemRarity.CUC_PHAM, item_level=75,
        width=2, height=4
    )
    unique_item = InventoryItem(
        item_uuid="it_4", item_id="wpn_sword_unique", name="Thần Kiếm Độc Bản",
        item_type=ItemType.WEAPON, rarity=ItemRarity.THAN_THOAI, item_level=90,
        width=2, height=4
    )
    currency_item = InventoryItem(
        item_uuid="it_5", item_id="curr_chaos_stone", name="Hỗn Nguyên Thạch",
        item_type=ItemType.CURRENCY, rarity=ItemRarity.CUC_PHAM, item_level=1,
        width=1, height=1, stack_count=5
    )

    # Preset: ALL
    cfg_all = LootFilterConfig(preset=FilterPreset.ALL)
    assert cfg_all.evaluate_item(common_item) is True
    assert cfg_all.evaluate_item(magic_item) is True
    assert cfg_all.evaluate_item(rare_item) is True
    assert cfg_all.evaluate_item(unique_item) is True
    assert cfg_all.evaluate_item(currency_item) is True

    # Preset: SEMI_STRICT (Hides Phàm Phẩm)
    cfg_semi = LootFilterConfig(preset=FilterPreset.SEMI_STRICT)
    assert cfg_semi.evaluate_item(common_item) is False
    assert cfg_semi.evaluate_item(magic_item) is True
    assert cfg_semi.evaluate_item(rare_item) is True
    assert cfg_semi.evaluate_item(unique_item) is True
    assert cfg_semi.evaluate_item(currency_item) is True

    # Preset: STRICT (Hides Phàm & Linh, requires Cực Phẩm iLvl >= 70)
    cfg_strict = LootFilterConfig(preset=FilterPreset.STRICT)
    assert cfg_strict.evaluate_item(common_item) is False
    assert cfg_strict.evaluate_item(magic_item) is False
    assert cfg_strict.evaluate_item(rare_item) is True
    low_rare = InventoryItem(
        item_uuid="it_rare_low", item_id="wpn_low", name="Rìu Thô",
        item_type=ItemType.WEAPON, rarity=ItemRarity.CUC_PHAM, item_level=40,
        width=2, height=3
    )
    assert cfg_strict.evaluate_item(low_rare) is False
    assert cfg_strict.evaluate_item(unique_item) is True

    # Preset: UBER_STRICT (Cực Phẩm iLvl >= 85 or Thần Thoại)
    cfg_uber = LootFilterConfig(preset=FilterPreset.UBER_STRICT)
    assert cfg_uber.evaluate_item(rare_item) is False  # iLvl 75 < 85
    pinnacle_rare = InventoryItem(
        item_uuid="it_rare_high", item_id="wpn_high", name="Long Ngạc Cự Phủ",
        item_type=ItemType.WEAPON, rarity=ItemRarity.CUC_PHAM, item_level=86,
        width=2, height=4
    )
    assert cfg_uber.evaluate_item(pinnacle_rare) is True
    assert cfg_uber.evaluate_item(unique_item) is True


def test_monster_loot_generation(loot_service: LootDropService):
    drops = loot_service.generate_monster_drops(
        monster_id="boss_abyssal_tyrant",
        monster_level=85,
        is_boss=True,
        drop_pos=(1.5, 2.5)
    )
    assert len(drops) >= 2
    for drop in drops:
        assert drop.world_x == pytest.approx(1.5, abs=1.5)
        assert drop.world_y == pytest.approx(2.5, abs=1.5)
        assert drop.item.width >= 1
        assert drop.item.height >= 1
        assert drop.is_collected is False


def test_pickup_ground_drop_into_inventory(loot_service: LootDropService, inventory_service: InventoryService):
    acc = "acc_test_pickup"
    char = "char_test_pickup"

    # Generate a dropped item on the ground
    item = InventoryItem(
        item_uuid="drop_item_1",
        item_id="wpn_infernal_blade",
        name="Xích Diễm Trảm Mã Đao",
        item_type=ItemType.WEAPON,
        rarity=ItemRarity.CUC_PHAM,
        item_level=80,
        width=2,
        height=4
    )
    ground_drop = GroundLootDrop(
        drop_id="gd_001",
        item=item,
        world_x=1.0,
        world_y=1.0,
        beam_color="#fbbf24",
        beam_height=140
    )

    loot_service.register_ground_drop(ground_drop)

    # Pickup drop
    success, msg, slot = loot_service.pickup_ground_drop(acc, char, ground_drop.drop_id)
    assert success is True
    assert ground_drop.is_collected is True
    assert slot is not None

    # Check inventory
    inv = inventory_service.get_character_inventory(acc, char)
    assert slot in inv.slots
    assert inv.slots[slot].name == "Xích Diễm Trảm Mã Đao"
    assert inv.slots[slot].width == 2
    assert inv.slots[slot].height == 4


def test_pickup_fails_when_inventory_full(loot_service: LootDropService, inventory_service: InventoryService):
    acc = "acc_full"
    char = "char_full"
    inv = inventory_service.get_character_inventory(acc, char)

    # Fill all 60 slots
    for i in range(inv.capacity):
        inv.slots[i] = InventoryItem(
            item_uuid=f"filler_{i}",
            item_id="filler",
            name="Rác Hoang Vực",
            item_type=ItemType.CONSUMABLE,
            rarity=ItemRarity.PHAM_PHAM,
            item_level=1,
            width=1,
            height=1
        )

    drop_item = InventoryItem(
        item_uuid="drop_overflow",
        item_id="wpn_sword",
        name="Thần Kiếm",
        item_type=ItemType.WEAPON,
        rarity=ItemRarity.THAN_THOAI,
        item_level=90,
        width=2,
        height=4
    )
    ground_drop = GroundLootDrop(
        drop_id="gd_overflow",
        item=drop_item,
        world_x=0.0,
        world_y=0.0,
        beam_color="#818cf8",
        beam_height=160
    )
    loot_service.register_ground_drop(ground_drop)

    success, msg, slot = loot_service.pickup_ground_drop(acc, char, ground_drop.drop_id)
    assert success is False
    assert "full" in msg.lower()
    assert ground_drop.is_collected is False


def test_currency_auto_stacking_on_pickup(loot_service: LootDropService, inventory_service: InventoryService):
    acc = "acc_stack"
    char = "char_stack"

    drop_1 = GroundLootDrop(
        drop_id="gd_curr_1",
        item=InventoryItem(
            item_uuid="c_1", item_id="curr_chaos_stone", name="Hỗn Nguyên Thạch",
            item_type=ItemType.CURRENCY, rarity=ItemRarity.CUC_PHAM, item_level=1,
            width=1, height=1, stack_count=10, max_stack=5000
        ),
        world_x=0.0, world_y=0.0, beam_color="#fbbf24", beam_height=100
    )
    drop_2 = GroundLootDrop(
        drop_id="gd_curr_2",
        item=InventoryItem(
            item_uuid="c_2", item_id="curr_chaos_stone", name="Hỗn Nguyên Thạch",
            item_type=ItemType.CURRENCY, rarity=ItemRarity.CUC_PHAM, item_level=1,
            width=1, height=1, stack_count=15, max_stack=5000
        ),
        world_x=0.5, world_y=0.5, beam_color="#fbbf24", beam_height=100
    )

    loot_service.register_ground_drop(drop_1)
    loot_service.register_ground_drop(drop_2)

    s1, _, slot1 = loot_service.pickup_ground_drop(acc, char, drop_1.drop_id)
    assert s1 is True
    s2, _, slot2 = loot_service.pickup_ground_drop(acc, char, drop_2.drop_id)
    assert s2 is True
    assert slot1 == slot2  # Stacked into the exact same slot

    inv = inventory_service.get_character_inventory(acc, char)
    assert inv.slots[slot1].stack_count == 25
