"""
Tests for LootEntry model with hybrid loot support.

Tests the extended LootEntry dataclass that supports both static
and procedural loot types with backward compatibility.
"""

import pytest

from app.models.enemy import LootEntry, LootType


class TestLootEntryBackwardCompatibility:
    """Test that existing YAML format still works."""

    def test_from_dict_defaults_to_static(self):
        """Old-style entries without loot_type should default to STATIC."""
        entry_data = {
            "item_id": "rusty_dagger",
            "drop_chance": 0.15,
        }
        entry = LootEntry.from_dict(entry_data)

        assert entry.loot_type == LootType.STATIC
        assert entry.item_id == "rusty_dagger"
        assert entry.drop_chance == 0.15
        assert entry.quantity_min == 1
        assert entry.quantity_max == 1

    def test_from_dict_with_all_old_fields(self):
        """Test entry with all old-style fields."""
        entry_data = {
            "item_id": "gold_coin",
            "drop_chance": 0.50,
            "quantity_min": 1,
            "quantity_max": 3,
        }
        entry = LootEntry.from_dict(entry_data)

        assert entry.loot_type == LootType.STATIC
        assert entry.item_id == "gold_coin"
        assert entry.drop_chance == 0.50
        assert entry.quantity_min == 1
        assert entry.quantity_max == 3

    def test_to_dict_includes_loot_type(self):
        """Serialization should include loot_type."""
        entry = LootEntry(
            loot_type=LootType.STATIC,
            item_id="health_potion",
            drop_chance=0.2
        )
        data = entry.to_dict()

        assert data["loot_type"] == "static"
        assert data["item_id"] == "health_potion"
        assert data["drop_chance"] == 0.2


class TestLootEntryStaticType:
    """Test static loot entries."""

    def test_static_entry_creation(self):
        """Test creating a static loot entry."""
        entry = LootEntry(
            loot_type=LootType.STATIC,
            item_id="goblin_ear",
            drop_chance=0.60,
            quantity_min=1,
            quantity_max=2
        )

        assert entry.loot_type == LootType.STATIC
        assert entry.item_id == "goblin_ear"
        assert entry.item_type is None
        assert entry.rarity_bonus == 0.0

    def test_static_from_dict_explicit(self):
        """Test parsing explicit static entry."""
        entry_data = {
            "loot_type": "static",
            "item_id": "health_potion_small",
            "drop_chance": 0.10,
        }
        entry = LootEntry.from_dict(entry_data)

        assert entry.loot_type == LootType.STATIC
        assert entry.item_id == "health_potion_small"

    def test_static_to_dict_omits_procedural_fields(self):
        """Static entries should omit procedural-only fields."""
        entry = LootEntry(
            loot_type=LootType.STATIC,
            item_id="gold_coin",
            drop_chance=0.5
        )
        data = entry.to_dict()

        assert "item_id" in data
        assert "item_type" not in data
        assert "rarity_bonus" not in data


class TestLootEntryProceduralType:
    """Test procedural loot entries."""

    def test_procedural_entry_creation(self):
        """Test creating a procedural loot entry."""
        entry = LootEntry(
            loot_type=LootType.PROCEDURAL,
            item_type="weapon",
            drop_chance=0.10,
            rarity_bonus=0.15
        )

        assert entry.loot_type == LootType.PROCEDURAL
        assert entry.item_type == "weapon"
        assert entry.rarity_bonus == 0.15
        assert entry.item_id is None

    def test_procedural_from_dict(self):
        """Test parsing procedural entry from dict."""
        entry_data = {
            "loot_type": "procedural",
            "item_type": "armor",
            "drop_chance": 0.08,
            "rarity_bonus": 0.05,
        }
        entry = LootEntry.from_dict(entry_data)

        assert entry.loot_type == LootType.PROCEDURAL
        assert entry.item_type == "armor"
        assert entry.drop_chance == 0.08
        assert entry.rarity_bonus == 0.05

    def test_procedural_to_dict_includes_item_type(self):
        """Procedural entries should include item_type and rarity_bonus."""
        entry = LootEntry(
            loot_type=LootType.PROCEDURAL,
            item_type="weapon",
            drop_chance=0.15,
            rarity_bonus=0.10
        )
        data = entry.to_dict()

        assert data["loot_type"] == "procedural"
        assert data["item_type"] == "weapon"
        assert data["rarity_bonus"] == 0.10
        assert "item_id" not in data

    def test_procedural_default_rarity_bonus(self):
        """Procedural entries default to 0.0 rarity bonus."""
        entry_data = {
            "loot_type": "procedural",
            "item_type": "weapon",
            "drop_chance": 0.10,
        }
        entry = LootEntry.from_dict(entry_data)

        assert entry.rarity_bonus == 0.0


class TestLootTypeEnum:
    """Test LootType enum values."""

    def test_static_value(self):
        """Test STATIC enum value."""
        assert LootType.STATIC.value == "static"

    def test_procedural_value(self):
        """Test PROCEDURAL enum value."""
        assert LootType.PROCEDURAL.value == "procedural"

    def test_from_string(self):
        """Test creating enum from string."""
        assert LootType("static") == LootType.STATIC
        assert LootType("procedural") == LootType.PROCEDURAL

    def test_invalid_string_raises(self):
        """Test that invalid string raises ValueError."""
        with pytest.raises(ValueError):
            LootType("invalid")


class TestLootEntryRoundTrip:
    """Test serialization/deserialization round trips."""

    def test_static_round_trip(self):
        """Static entry should survive round trip."""
        original = LootEntry(
            loot_type=LootType.STATIC,
            item_id="health_potion_small",
            drop_chance=0.15,
            quantity_min=1,
            quantity_max=2
        )

        data = original.to_dict()
        restored = LootEntry.from_dict(data)

        assert restored.loot_type == original.loot_type
        assert restored.item_id == original.item_id
        assert restored.drop_chance == original.drop_chance
        assert restored.quantity_min == original.quantity_min
        assert restored.quantity_max == original.quantity_max

    def test_procedural_round_trip(self):
        """Procedural entry should survive round trip."""
        original = LootEntry(
            loot_type=LootType.PROCEDURAL,
            item_type="weapon",
            drop_chance=0.25,
            rarity_bonus=0.15,
            quantity_min=1,
            quantity_max=1
        )

        data = original.to_dict()
        restored = LootEntry.from_dict(data)

        assert restored.loot_type == original.loot_type
        assert restored.item_type == original.item_type
        assert restored.drop_chance == original.drop_chance
        assert restored.rarity_bonus == original.rarity_bonus
