"""
Unit tests for the InventoryService.

Tests cover:
- Adding and removing items
- Equipment slot validation
- Level and class requirement checks
- Consumable usage and effect application
- Bulk operations
- Error handling
"""

import pytest
from unittest.mock import Mock, patch, MagicMock
from typing import List

from app.models.character import Character
from app.models.items import Item
from app.models.effects import Effect
from app.models.stats import Stats
from app.models.enums import ItemType, ItemRarity, EffectType, StatType, DamageType
from app.models.skills import PlayerClass
from app.models.origins import Origin
from app.services.inventory_service import (
    InventoryService,
    ItemNotFoundError,
    CannotEquipError,
    InvalidSlotError,
    CannotUseItemError,
    InventoryFullError,
    ConsumableResult,
    VALID_SLOTS,
    ITEM_TYPE_SLOTS,
    MAX_INVENTORY_SIZE,
    get_inventory_service,
)


# =============================================================================
# Test Fixtures
# =============================================================================

@pytest.fixture
def mock_character_service():
    """Create a mock CharacterService."""
    service = Mock()
    service.update_character = Mock()
    return service


@pytest.fixture
def inventory_service(mock_character_service):
    """Create InventoryService with mocked dependencies."""
    return InventoryService(character_service=mock_character_service)


@pytest.fixture
def mock_origin():
    """Create a minimal Origin for testing."""
    from app.models.origins import StartingLocation, StartingBonus

    starting_location = StartingLocation(
        id="test_location",
        name="Test Village",
        region="Test Region",
        description="A test location"
    )

    return Origin(
        id="test_origin",
        name="Test Origin",
        description="A test origin for testing purposes",
        starting_location=starting_location,
        narrative_hooks=["test hook"],
        starting_bonus=None,
    )


@pytest.fixture
def mock_player_class():
    """Create a minimal PlayerClass for testing."""
    return PlayerClass(
        class_id="warrior",
        name="Warrior",
        description="A mighty warrior",
        base_stats=Stats(
            strength=14,
            dexterity=10,
            constitution=12,
            intelligence=8,
            wisdom=10,
            charisma=8,
            luck=8,
        ),
        skill_trees=[],
        starting_abilities=["basic_attack"],
    )


@pytest.fixture
def test_character(mock_player_class, mock_origin):
    """Create a test character."""
    return Character(
        character_id="char_test_123",
        user_id="user_test_456",
        name="Test Hero",
        player_class=mock_player_class,
        origin=mock_origin,
        level=5,
        experience=0,
        base_stats=mock_player_class.base_stats.copy(),
        inventory=[],
        equipped={},
        gold=100,
    )


@pytest.fixture
def test_weapon():
    """Create a test weapon item."""
    return Item(
        item_id="iron_sword",
        name="Iron Sword",
        item_type=ItemType.WEAPON,
        rarity=ItemRarity.COMMON,
        description="A sturdy iron sword",
        value=50,
        damage=10,
        damage_type=DamageType.PHYSICAL,
        crit_chance=0.05,
        crit_multiplier=2.0,
        required_level=1,
    )


@pytest.fixture
def test_armor():
    """Create a test armor item."""
    return Item(
        item_id="leather_chest",
        name="Leather Chestpiece",
        item_type=ItemType.ARMOR,
        rarity=ItemRarity.COMMON,
        description="Simple leather armor",
        value=40,
        defense=5,
        resistance=2,
        required_level=1,
    )


@pytest.fixture
def test_helmet():
    """Create a test helmet item."""
    return Item(
        item_id="iron_helm",
        name="Iron Helmet",
        item_type=ItemType.ARMOR,
        rarity=ItemRarity.UNCOMMON,
        description="A protective iron helmet",
        value=30,
        defense=3,
        resistance=1,
        required_level=3,
    )


@pytest.fixture
def test_consumable():
    """Create a test consumable item (health potion)."""
    return Item(
        item_id="health_potion_small",
        name="Small Health Potion",
        item_type=ItemType.CONSUMABLE,
        rarity=ItemRarity.COMMON,
        description="Restores 25 HP",
        value=10,
        effects_on_use=[
            Effect(
                effect_id="heal_25",
                name="Minor Healing",
                effect_type=EffectType.HOT,
                duration=1,
                power=25,
                stacks=1,
            )
        ],
    )


@pytest.fixture
def test_buff_potion():
    """Create a test buff potion."""
    return Item(
        item_id="strength_potion",
        name="Potion of Strength",
        item_type=ItemType.CONSUMABLE,
        rarity=ItemRarity.UNCOMMON,
        description="Increases strength temporarily",
        value=25,
        effects_on_use=[
            Effect(
                effect_id="str_buff",
                name="Strength Boost",
                effect_type=EffectType.BUFF,
                duration=3,
                power=5,
                stat_affected=StatType.STRENGTH,
                stacks=1,
            )
        ],
    )


@pytest.fixture
def test_quest_item():
    """Create a test quest item."""
    return Item(
        item_id="ancient_key",
        name="Ancient Key",
        item_type=ItemType.QUEST_ITEM,
        rarity=ItemRarity.RARE,
        description="An ornate key to the ancient tomb",
        value=0,
        is_tradeable=False,
    )


@pytest.fixture
def high_level_weapon():
    """Create a weapon with high level requirement."""
    return Item(
        item_id="legendary_blade",
        name="Blade of Ages",
        item_type=ItemType.WEAPON,
        rarity=ItemRarity.LEGENDARY,
        description="A blade forged in ancient times",
        value=5000,
        damage=50,
        damage_type=DamageType.PHYSICAL,
        required_level=20,  # Higher than test character's level 5
    )


# =============================================================================
# Read Operation Tests
# =============================================================================

class TestGetInventory:
    """Tests for get_inventory() and related read operations."""

    def test_get_empty_inventory(self, inventory_service, test_character):
        """Test getting inventory when it's empty."""
        items = inventory_service.get_inventory(test_character)
        assert items == []

    def test_get_inventory_with_items(self, inventory_service, test_character, test_weapon, test_armor):
        """Test getting inventory with items."""
        test_character.inventory = [test_weapon, test_armor]

        items = inventory_service.get_inventory(test_character)

        assert len(items) == 2
        assert test_weapon in items
        assert test_armor in items

    def test_get_inventory_returns_copy(self, inventory_service, test_character, test_weapon):
        """Test that get_inventory returns a new list (not the original)."""
        test_character.inventory = [test_weapon]

        items = inventory_service.get_inventory(test_character)
        items.append(test_weapon)  # Modify returned list

        # Original inventory should be unchanged
        assert len(test_character.inventory) == 1

    def test_get_item_by_id_found(self, inventory_service, test_character, test_weapon):
        """Test finding an item by ID."""
        test_character.inventory = [test_weapon]

        item = inventory_service.get_item_by_id(test_character, "iron_sword")

        assert item is test_weapon

    def test_get_item_by_id_not_found(self, inventory_service, test_character, test_weapon):
        """Test item not found returns None."""
        test_character.inventory = [test_weapon]

        item = inventory_service.get_item_by_id(test_character, "nonexistent_item")

        assert item is None

    def test_get_equipped_items_empty(self, inventory_service, test_character):
        """Test getting equipped items when nothing equipped."""
        equipped = inventory_service.get_equipped_items(test_character)
        assert equipped == {}

    def test_get_equipped_items_with_equipment(self, inventory_service, test_character, test_weapon):
        """Test getting equipped items."""
        test_character.equipped = {"weapon": test_weapon}

        equipped = inventory_service.get_equipped_items(test_character)

        assert "weapon" in equipped
        assert equipped["weapon"] is test_weapon

    def test_get_equipped_item_specific_slot(self, inventory_service, test_character, test_weapon):
        """Test getting item from a specific slot."""
        test_character.equipped = {"weapon": test_weapon}

        item = inventory_service.get_equipped_item(test_character, "weapon")

        assert item is test_weapon

    def test_get_equipped_item_empty_slot(self, inventory_service, test_character):
        """Test getting item from empty slot returns None."""
        item = inventory_service.get_equipped_item(test_character, "weapon")
        assert item is None

    def test_get_inventory_count(self, inventory_service, test_character, test_weapon, test_armor):
        """Test counting inventory items."""
        test_character.inventory = [test_weapon, test_armor]

        count = inventory_service.get_inventory_count(test_character)

        assert count == 2


# =============================================================================
# Add/Remove Item Tests
# =============================================================================

class TestAddItem:
    """Tests for add_item()."""

    def test_add_item_success(self, inventory_service, test_character, test_weapon, mock_character_service):
        """Test successfully adding an item."""
        inventory_service.add_item(test_character, test_weapon, "user_test_456")

        assert test_weapon in test_character.inventory
        mock_character_service.update_character.assert_called_once()

    def test_add_item_without_save(self, inventory_service, test_character, test_weapon, mock_character_service):
        """Test adding item without persistence."""
        inventory_service.add_item(test_character, test_weapon, "user_test_456", save=False)

        assert test_weapon in test_character.inventory
        mock_character_service.update_character.assert_not_called()

    def test_add_multiple_items(self, inventory_service, test_character, test_weapon, test_armor, mock_character_service):
        """Test adding multiple items."""
        inventory_service.add_item(test_character, test_weapon, "user_test_456")
        inventory_service.add_item(test_character, test_armor, "user_test_456")

        assert len(test_character.inventory) == 2


class TestRemoveItem:
    """Tests for remove_item()."""

    def test_remove_item_success(self, inventory_service, test_character, test_weapon, mock_character_service):
        """Test successfully removing an item."""
        test_character.inventory = [test_weapon]

        removed = inventory_service.remove_item(test_character, "iron_sword", "user_test_456")

        assert removed is test_weapon
        assert test_weapon not in test_character.inventory
        mock_character_service.update_character.assert_called_once()

    def test_remove_item_not_found(self, inventory_service, test_character):
        """Test removing non-existent item raises error."""
        with pytest.raises(ItemNotFoundError) as exc:
            inventory_service.remove_item(test_character, "nonexistent", "user_test_456")

        assert "nonexistent" in str(exc.value)

    def test_remove_item_from_multiple(self, inventory_service, test_character, test_weapon, test_armor):
        """Test removing one item from multiple."""
        test_character.inventory = [test_weapon, test_armor]

        inventory_service.remove_item(test_character, "iron_sword", "user_test_456")

        assert test_weapon not in test_character.inventory
        assert test_armor in test_character.inventory

    def test_drop_item_alias(self, inventory_service, test_character, test_weapon, mock_character_service):
        """Test drop_item is an alias for remove_item."""
        test_character.inventory = [test_weapon]

        dropped = inventory_service.drop_item(test_character, "iron_sword", "user_test_456")

        assert dropped is test_weapon
        assert test_weapon not in test_character.inventory


# =============================================================================
# Equipment Tests
# =============================================================================

class TestEquipItem:
    """Tests for equip_item()."""

    def test_equip_weapon_to_weapon_slot(self, inventory_service, test_character, test_weapon, mock_character_service):
        """Test equipping a weapon to weapon slot."""
        test_character.inventory = [test_weapon]

        previous = inventory_service.equip_item(
            test_character, "iron_sword", "weapon", "user_test_456"
        )

        assert previous is None
        assert test_character.equipped.get("weapon") is test_weapon
        assert test_weapon not in test_character.inventory
        mock_character_service.update_character.assert_called_once()

    def test_equip_armor_to_chest_slot(self, inventory_service, test_character, test_armor, mock_character_service):
        """Test equipping armor to chest slot."""
        test_character.inventory = [test_armor]

        inventory_service.equip_item(test_character, "leather_chest", "chest", "user_test_456")

        assert test_character.equipped.get("chest") is test_armor

    def test_equip_returns_previous_item(self, inventory_service, test_character, test_weapon, mock_character_service):
        """Test that equipping returns the previously equipped item."""
        old_weapon = Item(
            item_id="old_sword",
            name="Old Sword",
            item_type=ItemType.WEAPON,
            damage=5,
        )
        test_character.inventory = [test_weapon]
        test_character.equipped = {"weapon": old_weapon}

        previous = inventory_service.equip_item(
            test_character, "iron_sword", "weapon", "user_test_456"
        )

        assert previous is old_weapon
        assert old_weapon in test_character.inventory  # Returned to inventory

    def test_equip_to_invalid_slot_raises_error(self, inventory_service, test_character, test_weapon):
        """Test equipping to invalid slot raises InvalidSlotError."""
        test_character.inventory = [test_weapon]

        with pytest.raises(InvalidSlotError) as exc:
            inventory_service.equip_item(
                test_character, "iron_sword", "invalid_slot", "user_test_456"
            )

        assert "invalid_slot" in str(exc.value)
        assert "Valid slots" in str(exc.value)

    def test_equip_weapon_to_armor_slot_raises_error(self, inventory_service, test_character, test_weapon):
        """Test equipping weapon to armor slot raises CannotEquipError."""
        test_character.inventory = [test_weapon]

        with pytest.raises(CannotEquipError) as exc:
            inventory_service.equip_item(
                test_character, "iron_sword", "chest", "user_test_456"
            )

        assert "weapon" in str(exc.value).lower()

    def test_equip_armor_to_weapon_slot_raises_error(self, inventory_service, test_character, test_armor):
        """Test equipping armor to weapon slot raises CannotEquipError."""
        test_character.inventory = [test_armor]

        with pytest.raises(CannotEquipError) as exc:
            inventory_service.equip_item(
                test_character, "leather_chest", "weapon", "user_test_456"
            )

        assert "armor" in str(exc.value).lower()

    def test_equip_item_not_in_inventory(self, inventory_service, test_character):
        """Test equipping item not in inventory raises ItemNotFoundError."""
        with pytest.raises(ItemNotFoundError):
            inventory_service.equip_item(
                test_character, "nonexistent", "weapon", "user_test_456"
            )

    def test_equip_item_level_requirement_not_met(self, inventory_service, test_character, high_level_weapon):
        """Test equipping item with unmet level requirement raises error."""
        test_character.inventory = [high_level_weapon]
        test_character.level = 5  # Item requires level 20

        with pytest.raises(CannotEquipError) as exc:
            inventory_service.equip_item(
                test_character, "legendary_blade", "weapon", "user_test_456"
            )

        assert "level 20" in str(exc.value)
        assert "level 5" in str(exc.value)

    def test_equip_consumable_raises_error(self, inventory_service, test_character, test_consumable):
        """Test equipping consumable raises CannotEquipError."""
        test_character.inventory = [test_consumable]

        with pytest.raises(CannotEquipError) as exc:
            inventory_service.equip_item(
                test_character, "health_potion_small", "weapon", "user_test_456"
            )

        assert "consumable" in str(exc.value).lower()

    def test_equip_quest_item_raises_error(self, inventory_service, test_character, test_quest_item):
        """Test equipping quest item raises CannotEquipError."""
        test_character.inventory = [test_quest_item]

        with pytest.raises(CannotEquipError) as exc:
            inventory_service.equip_item(
                test_character, "ancient_key", "weapon", "user_test_456"
            )

    def test_equip_weapon_to_off_hand(self, inventory_service, test_character, test_weapon, mock_character_service):
        """Test equipping weapon to off_hand slot."""
        test_character.inventory = [test_weapon]

        inventory_service.equip_item(
            test_character, "iron_sword", "off_hand", "user_test_456"
        )

        assert test_character.equipped.get("off_hand") is test_weapon


class TestUnequipItem:
    """Tests for unequip_item()."""

    def test_unequip_item_success(self, inventory_service, test_character, test_weapon, mock_character_service):
        """Test successfully unequipping an item."""
        test_character.equipped = {"weapon": test_weapon}

        unequipped = inventory_service.unequip_item(test_character, "weapon", "user_test_456")

        assert unequipped is test_weapon
        assert "weapon" not in test_character.equipped
        assert test_weapon in test_character.inventory
        mock_character_service.update_character.assert_called_once()

    def test_unequip_empty_slot_returns_none(self, inventory_service, test_character, mock_character_service):
        """Test unequipping from empty slot returns None."""
        unequipped = inventory_service.unequip_item(test_character, "weapon", "user_test_456")

        assert unequipped is None

    def test_unequip_invalid_slot_raises_error(self, inventory_service, test_character):
        """Test unequipping from invalid slot raises InvalidSlotError."""
        with pytest.raises(InvalidSlotError):
            inventory_service.unequip_item(test_character, "invalid_slot", "user_test_456")


# =============================================================================
# Consumable Tests
# =============================================================================

class TestUseConsumable:
    """Tests for use_consumable()."""

    def test_use_health_potion(self, inventory_service, test_character, test_consumable, mock_character_service):
        """Test using a health potion restores HP."""
        test_character.inventory = [test_consumable]

        result = inventory_service.use_consumable(
            test_character, "health_potion_small", "user_test_456",
            current_hp=50, max_hp=100
        )

        assert isinstance(result, ConsumableResult)
        assert result.hp_restored == 25  # Potion restores 25, capped at missing HP
        assert result.item_name == "Small Health Potion"
        assert test_consumable not in test_character.inventory
        mock_character_service.update_character.assert_called_once()

    def test_use_health_potion_capped_at_max(self, inventory_service, test_character, test_consumable):
        """Test HP restoration is capped at max HP."""
        test_character.inventory = [test_consumable]

        result = inventory_service.use_consumable(
            test_character, "health_potion_small", "user_test_456",
            current_hp=90, max_hp=100  # Only missing 10 HP
        )

        assert result.hp_restored == 10  # Only restores missing amount

    def test_use_consumable_at_full_hp(self, inventory_service, test_character, test_consumable):
        """Test using potion at full HP restores 0."""
        test_character.inventory = [test_consumable]

        result = inventory_service.use_consumable(
            test_character, "health_potion_small", "user_test_456",
            current_hp=100, max_hp=100
        )

        assert result.hp_restored == 0

    def test_use_buff_potion(self, inventory_service, test_character, test_buff_potion, mock_character_service):
        """Test using a buff potion."""
        test_character.inventory = [test_buff_potion]

        result = inventory_service.use_consumable(
            test_character, "strength_potion", "user_test_456"
        )

        assert result.item_name == "Potion of Strength"
        assert len(result.effects_applied) == 1
        assert result.effects_applied[0]["effect_type"] == "buff"
        assert result.effects_applied[0]["stat_affected"] == "strength"

    def test_use_non_consumable_raises_error(self, inventory_service, test_character, test_weapon):
        """Test using non-consumable item raises CannotUseItemError."""
        test_character.inventory = [test_weapon]

        with pytest.raises(CannotUseItemError) as exc:
            inventory_service.use_consumable(
                test_character, "iron_sword", "user_test_456"
            )

        assert "not a consumable" in str(exc.value)

    def test_use_item_not_in_inventory_raises_error(self, inventory_service, test_character):
        """Test using item not in inventory raises ItemNotFoundError."""
        with pytest.raises(ItemNotFoundError):
            inventory_service.use_consumable(
                test_character, "nonexistent", "user_test_456"
            )

    def test_consumable_result_to_dict(self, inventory_service, test_character, test_consumable):
        """Test ConsumableResult serialization."""
        test_character.inventory = [test_consumable]

        result = inventory_service.use_consumable(
            test_character, "health_potion_small", "user_test_456",
            current_hp=50, max_hp=100
        )

        result_dict = result.to_dict()

        assert "item_name" in result_dict
        assert "hp_restored" in result_dict
        assert "effects_applied" in result_dict
        assert "message" in result_dict


class TestUseConsumableInCombat:
    """Tests for use_consumable_in_combat()."""

    def test_combat_consumable_returns_effects(self, inventory_service, test_character, test_buff_potion):
        """Test combat consumable returns duration effects."""
        test_character.inventory = [test_buff_potion]

        result, effects = inventory_service.use_consumable_in_combat(
            test_character, "strength_potion", "user_test_456",
            current_hp=50, max_hp=100
        )

        assert isinstance(result, ConsumableResult)
        assert len(effects) == 1
        assert effects[0].effect_type == EffectType.BUFF
        assert effects[0].duration == 3

    def test_combat_instant_heal_potion(self, inventory_service, test_character, test_consumable):
        """Test instant heal in combat."""
        test_character.inventory = [test_consumable]

        result, effects = inventory_service.use_consumable_in_combat(
            test_character, "health_potion_small", "user_test_456",
            current_hp=50, max_hp=100
        )

        # HOT with duration 1 should be returned as duration effect for combat tracking
        assert len(effects) >= 0  # Implementation may vary


# =============================================================================
# Bulk Operation Tests
# =============================================================================

class TestBulkOperations:
    """Tests for bulk inventory operations."""

    def test_add_items_bulk(self, inventory_service, test_character, test_weapon, test_armor, mock_character_service):
        """Test adding multiple items at once."""
        items = [test_weapon, test_armor]

        count = inventory_service.add_items(test_character, items, "user_test_456")

        assert count == 2
        assert len(test_character.inventory) == 2
        mock_character_service.update_character.assert_called_once()

    def test_get_items_by_type(self, inventory_service, test_character, test_weapon, test_armor, test_consumable):
        """Test filtering items by type."""
        test_character.inventory = [test_weapon, test_armor, test_consumable]

        weapons = inventory_service.get_items_by_type(test_character, ItemType.WEAPON)
        armor = inventory_service.get_items_by_type(test_character, ItemType.ARMOR)
        consumables = inventory_service.get_items_by_type(test_character, ItemType.CONSUMABLE)

        assert len(weapons) == 1
        assert test_weapon in weapons
        assert len(armor) == 1
        assert test_armor in armor
        assert len(consumables) == 1

    def test_get_equippable_items(self, inventory_service, test_character, test_weapon, test_armor, test_consumable):
        """Test getting only equippable items."""
        test_character.inventory = [test_weapon, test_armor, test_consumable]

        equippable = inventory_service.get_equippable_items(test_character)

        assert test_weapon in equippable
        assert test_armor in equippable
        assert test_consumable not in equippable

    def test_get_equippable_items_for_slot(self, inventory_service, test_character, test_weapon, test_armor):
        """Test getting equippable items for a specific slot."""
        test_character.inventory = [test_weapon, test_armor]

        for_weapon = inventory_service.get_equippable_items(test_character, slot="weapon")
        for_chest = inventory_service.get_equippable_items(test_character, slot="chest")

        assert test_weapon in for_weapon
        assert test_armor not in for_weapon
        assert test_armor in for_chest
        assert test_weapon not in for_chest

    def test_get_equippable_items_excludes_high_level(self, inventory_service, test_character, test_weapon, high_level_weapon):
        """Test that items above character level are excluded."""
        test_character.inventory = [test_weapon, high_level_weapon]
        test_character.level = 5

        equippable = inventory_service.get_equippable_items(test_character)

        assert test_weapon in equippable
        assert high_level_weapon not in equippable


# =============================================================================
# Edge Cases and Error Handling
# =============================================================================

class TestEdgeCases:
    """Tests for edge cases and error handling."""

    def test_valid_slots_constant(self):
        """Test VALID_SLOTS contains expected slots."""
        expected = {"weapon", "off_hand", "helmet", "chest", "gloves", "boots", "accessory_1", "accessory_2"}
        assert VALID_SLOTS == expected

    def test_item_type_slots_mapping(self):
        """Test ITEM_TYPE_SLOTS mapping is correct."""
        assert ItemType.WEAPON in ITEM_TYPE_SLOTS
        assert "weapon" in ITEM_TYPE_SLOTS[ItemType.WEAPON]
        assert "off_hand" in ITEM_TYPE_SLOTS[ItemType.WEAPON]
        assert ItemType.ARMOR in ITEM_TYPE_SLOTS
        assert "chest" in ITEM_TYPE_SLOTS[ItemType.ARMOR]
        assert "helmet" in ITEM_TYPE_SLOTS[ItemType.ARMOR]

    def test_generated_item_with_unique_id(self, inventory_service, test_character, mock_character_service):
        """Test handling of generated items with unique IDs."""
        generated_item = Item(
            item_id="gen_abc123",  # Generated item ID format
            name="Dagger",
            item_type=ItemType.WEAPON,
            rarity=ItemRarity.RARE,
            damage=15,
            is_generated=True,
            generated_name="Flaming Dagger of Strength",
            base_template_id="dagger",
            applied_affixes=["flaming", "of_strength"],
        )

        inventory_service.add_item(test_character, generated_item, "user_test_456")

        assert generated_item in test_character.inventory
        assert generated_item.get_display_name() == "Flaming Dagger of Strength"

    def test_equip_generated_item(self, inventory_service, test_character, mock_character_service):
        """Test equipping a generated item."""
        generated_item = Item(
            item_id="gen_xyz789",
            name="Sword",
            item_type=ItemType.WEAPON,
            rarity=ItemRarity.EPIC,
            damage=25,
            is_generated=True,
            generated_name="Blazing Sword of Power",
            required_level=1,
        )
        test_character.inventory = [generated_item]

        inventory_service.equip_item(test_character, "gen_xyz789", "weapon", "user_test_456")

        assert test_character.equipped.get("weapon") is generated_item


# =============================================================================
# Global Instance Tests
# =============================================================================

class TestGlobalInstance:
    """Tests for the global singleton pattern."""

    def test_get_inventory_service_returns_instance(self):
        """Test get_inventory_service returns InventoryService."""
        with patch('app.services.inventory_service._service_instance', None):
            with patch('app.services.inventory_service.get_character_service'):
                service = get_inventory_service()
                assert isinstance(service, InventoryService)

    def test_get_inventory_service_returns_same_instance(self):
        """Test get_inventory_service returns singleton."""
        with patch('app.services.inventory_service._service_instance', None):
            with patch('app.services.inventory_service.get_character_service'):
                service1 = get_inventory_service()
                service2 = get_inventory_service()
                assert service1 is service2
