"""
Unit tests for ClassLoader service.

Tests loading player class definitions from YAML files.
"""

import pytest
from pathlib import Path

from app.services.class_loader import ClassLoader
from app.models.skills import PlayerClass, SkillTree, SkillNode
from app.models.stats import Stats


class TestClassLoader:
    """Test ClassLoader functionality."""

    @pytest.fixture
    def loader(self):
        """Create a ClassLoader instance for testing."""
        return ClassLoader()

    def test_load_vanguard_class(self, loader):
        """Test loading the Vanguard class."""
        vanguard = loader.load_class("vanguard")

        # Verify class loaded successfully
        assert vanguard is not None
        assert isinstance(vanguard, PlayerClass)

        # Check basic properties
        assert vanguard.class_id == "vanguard"
        assert vanguard.name == "Vanguard"
        assert "melee combat" in vanguard.description.lower()

    def test_vanguard_base_stats(self, loader):
        """Test Vanguard base stats are correct."""
        vanguard = loader.load_class("vanguard")

        assert isinstance(vanguard.base_stats, Stats)
        assert vanguard.base_stats.strength == 14
        assert vanguard.base_stats.dexterity == 10
        assert vanguard.base_stats.constitution == 14
        assert vanguard.base_stats.intelligence == 8
        assert vanguard.base_stats.wisdom == 10
        assert vanguard.base_stats.charisma == 9

    def test_vanguard_skill_trees(self, loader):
        """Test Vanguard has 2 skill trees."""
        vanguard = loader.load_class("vanguard")

        # Should have exactly 2 skill trees
        assert len(vanguard.skill_trees) == 2

        # Get trees by ID
        shield_bearer = vanguard.get_skill_tree("shield_bearer")
        weapon_master = vanguard.get_skill_tree("weapon_master")

        assert shield_bearer is not None
        assert weapon_master is not None

        # Check tree names
        assert shield_bearer.name == "Shield Bearer"
        assert weapon_master.name == "Weapon Master"

    def test_shield_bearer_tree_structure(self, loader):
        """Test Shield Bearer tree has correct structure."""
        vanguard = loader.load_class("vanguard")
        shield_bearer = vanguard.get_skill_tree("shield_bearer")

        # Should have 10 nodes (5 tiers × 2 nodes)
        assert len(shield_bearer.nodes) == 10

        # Check tier distribution
        tier_counts = {}
        for node in shield_bearer.nodes:
            tier_counts[node.tier] = tier_counts.get(node.tier, 0) + 1

        # Should have 2 nodes per tier for tiers 1-5
        assert tier_counts == {1: 2, 2: 2, 3: 2, 4: 2, 5: 2}

    def test_weapon_master_tree_structure(self, loader):
        """Test Weapon Master tree has correct structure."""
        vanguard = loader.load_class("vanguard")
        weapon_master = vanguard.get_skill_tree("weapon_master")

        # Should have 10 nodes (5 tiers × 2 nodes)
        assert len(weapon_master.nodes) == 10

        # Check tier distribution
        tier_counts = {}
        for node in weapon_master.nodes:
            tier_counts[node.tier] = tier_counts.get(node.tier, 0) + 1

        # Should have 2 nodes per tier for tiers 1-5
        assert tier_counts == {1: 2, 2: 2, 3: 2, 4: 2, 5: 2}

    def test_skill_node_prerequisites(self, loader):
        """Test skill nodes have correct prerequisites."""
        vanguard = loader.load_class("vanguard")
        shield_bearer = vanguard.get_skill_tree("shield_bearer")

        # Find tier 1 and tier 2 nodes
        tier1_nodes = [n for n in shield_bearer.nodes if n.tier == 1]
        tier2_nodes = [n for n in shield_bearer.nodes if n.tier == 2]

        # Tier 1 nodes should have no prerequisites
        for node in tier1_nodes:
            assert len(node.prerequisites) == 0

        # Tier 2 nodes should have prerequisites
        for node in tier2_nodes:
            assert len(node.prerequisites) > 0
            # Prerequisites should reference tier 1 skills
            for prereq_id in node.prerequisites:
                prereq_found = any(n.skill_id == prereq_id for n in tier1_nodes)
                assert prereq_found, f"Prerequisite {prereq_id} not found in tier 1"

    def test_skill_node_effects(self, loader):
        """Test skill nodes have proper effects defined."""
        vanguard = loader.load_class("vanguard")
        shield_bearer = vanguard.get_skill_tree("shield_bearer")

        # Find the "fortify" skill (passive defense bonus)
        fortify = next((n for n in shield_bearer.nodes if n.skill_id == "fortify"), None)
        assert fortify is not None

        # Should have stat bonuses in effects
        assert "stat_bonuses" in fortify.effects
        assert "defense" in fortify.effects["stat_bonuses"]
        assert fortify.effects["stat_bonuses"]["defense"] == 5

    def test_skill_node_abilities(self, loader):
        """Test skill nodes with ability unlocks."""
        vanguard = loader.load_class("vanguard")
        shield_bearer = vanguard.get_skill_tree("shield_bearer")

        # Find the "shield_bash" skill (active ability)
        shield_bash = next((n for n in shield_bearer.nodes if n.skill_id == "shield_bash"), None)
        assert shield_bash is not None

        # Should have abilities in effects
        assert "abilities" in shield_bash.effects
        assert "shield_bash" in shield_bash.effects["abilities"]

    def test_starting_equipment(self, loader):
        """Test Vanguard starting equipment."""
        vanguard = loader.load_class("vanguard")

        # Should have starting equipment
        assert len(vanguard.starting_equipment) > 0
        assert "rusty_sword" in vanguard.starting_equipment
        assert "cloth_armor" in vanguard.starting_equipment
        assert "rusty_knife" in vanguard.starting_equipment

    def test_starting_abilities(self, loader):
        """Test Vanguard starting abilities."""
        vanguard = loader.load_class("vanguard")

        # Should have basic_attack
        assert len(vanguard.starting_abilities) > 0
        assert "basic_attack" in vanguard.starting_abilities

    def test_cache_functionality(self, loader):
        """Test that classes are cached after first load."""
        # Load class twice
        vanguard1 = loader.load_class("vanguard")
        vanguard2 = loader.load_class("vanguard")

        # Should be the same object (cached)
        assert vanguard1 is vanguard2

    def test_reload_class(self, loader):
        """Test reload_class forces a fresh load."""
        # Load class
        vanguard1 = loader.load_class("vanguard")

        # Reload class
        vanguard2 = loader.reload_class("vanguard")

        # Should still be equal but different objects
        assert vanguard1.class_id == vanguard2.class_id
        # Note: May or may not be same object depending on implementation

    def test_load_nonexistent_class(self, loader):
        """Test loading a class that doesn't exist."""
        result = loader.load_class("nonexistent_class")
        assert result is None

    def test_get_all_class_ids(self, loader):
        """Test getting list of all class IDs."""
        class_ids = loader.get_all_class_ids()

        # Should include vanguard
        assert "vanguard" in class_ids

        # Should be a list of strings
        assert all(isinstance(cid, str) for cid in class_ids)

    def test_load_all_classes(self, loader):
        """Test loading all classes at once."""
        classes = loader.load_all_classes()

        # Should have at least 1 class (vanguard)
        assert len(classes) >= 1

        # All should be PlayerClass instances
        assert all(isinstance(c, PlayerClass) for c in classes)

        # Vanguard should be in the list
        vanguard_found = any(c.class_id == "vanguard" for c in classes)
        assert vanguard_found

    def test_get_all_skills_method(self, loader):
        """Test PlayerClass.get_all_skills() method."""
        vanguard = loader.load_class("vanguard")

        all_skills = vanguard.get_all_skills()

        # Should have 20 skills (2 trees × 10 nodes)
        assert len(all_skills) == 20

        # All should be SkillNode instances
        assert all(isinstance(skill, SkillNode) for skill in all_skills)

        # Should include skills from both trees
        skill_ids = [s.skill_id for s in all_skills]
        assert "shield_bash" in skill_ids  # Shield Bearer tree
        assert "power_strike" in skill_ids  # Weapon Master tree

    def test_tier_5_ultimate_skills(self, loader):
        """Test that tier 5 skills exist and have powerful effects."""
        vanguard = loader.load_class("vanguard")
        all_skills = vanguard.get_all_skills()

        # Get tier 5 skills
        tier5_skills = [s for s in all_skills if s.tier == 5]

        # Should have 4 tier 5 skills (2 per tree)
        assert len(tier5_skills) == 4

        # Each tier 5 skill should have prerequisites
        for skill in tier5_skills:
            assert len(skill.prerequisites) > 0

        # At least one tier 5 skill should have significant stat bonuses
        has_major_bonuses = any(
            "stat_bonuses" in s.effects and
            any(v >= 10 for v in s.effects.get("stat_bonuses", {}).values())
            for s in tier5_skills
        )
        assert has_major_bonuses


if __name__ == "__main__":
    pytest.main([__file__, "-v"])
