"""
Unit tests for OriginService - character origin loading and validation.

Tests verify that origins load correctly from YAML, contain all required
data, and provide proper narrative hooks for the AI DM.
"""

import pytest
from app.services.origin_service import OriginService, get_origin_service
from app.models.origins import Origin, StartingLocation, StartingBonus


class TestOriginService:
    """Test suite for OriginService functionality."""

    @pytest.fixture
    def origin_service(self):
        """Create a fresh OriginService instance for each test."""
        service = OriginService()
        service.clear_cache()
        return service

    def test_service_initializes(self, origin_service):
        """Test that OriginService initializes correctly."""
        assert origin_service is not None
        assert origin_service.data_file.exists()

    def test_singleton_pattern(self):
        """Test that get_origin_service returns a singleton."""
        service1 = get_origin_service()
        service2 = get_origin_service()
        assert service1 is service2

    def test_load_all_origins(self, origin_service):
        """Test loading all origins from YAML file."""
        origins = origin_service.load_all_origins()

        assert len(origins) == 4
        origin_ids = [origin.id for origin in origins]
        assert "soul_revenant" in origin_ids
        assert "memory_thief" in origin_ids
        assert "shadow_apprentice" in origin_ids
        assert "escaped_captive" in origin_ids

    def test_load_origin_by_id(self, origin_service):
        """Test loading a specific origin by ID."""
        origin = origin_service.load_origin("soul_revenant")

        assert origin is not None
        assert origin.id == "soul_revenant"
        assert origin.name == "Soul Revenant"
        assert isinstance(origin.description, str)
        assert len(origin.description) > 0

    def test_origin_not_found(self, origin_service):
        """Test that loading non-existent origin returns None."""
        origin = origin_service.load_origin("nonexistent_origin")
        assert origin is None

    def test_origin_has_starting_location(self, origin_service):
        """Test that all origins have valid starting locations."""
        origins = origin_service.load_all_origins()

        for origin in origins:
            assert origin.starting_location is not None
            assert isinstance(origin.starting_location, StartingLocation)
            assert origin.starting_location.id
            assert origin.starting_location.name
            assert origin.starting_location.region
            assert origin.starting_location.description

    def test_origin_has_narrative_hooks(self, origin_service):
        """Test that all origins have narrative hooks for AI DM."""
        origins = origin_service.load_all_origins()

        for origin in origins:
            assert origin.narrative_hooks is not None
            assert isinstance(origin.narrative_hooks, list)
            assert len(origin.narrative_hooks) > 0, f"{origin.id} has no narrative hooks"

    def test_origin_has_starting_bonus(self, origin_service):
        """Test that all origins have starting bonuses."""
        origins = origin_service.load_all_origins()

        for origin in origins:
            assert origin.starting_bonus is not None
            assert isinstance(origin.starting_bonus, StartingBonus)
            assert origin.starting_bonus.trait
            assert origin.starting_bonus.description
            assert origin.starting_bonus.effect

    def test_soul_revenant_details(self, origin_service):
        """Test Soul Revenant origin has correct details."""
        origin = origin_service.load_origin("soul_revenant")

        assert origin.name == "Soul Revenant"
        assert "centuries" in origin.description.lower()
        assert origin.starting_location.id == "forgotten_crypt"
        assert "Deathless Resolve" in origin.starting_bonus.trait
        assert any("past" in hook.lower() for hook in origin.narrative_hooks)

    def test_memory_thief_details(self, origin_service):
        """Test Memory Thief origin has correct details."""
        origin = origin_service.load_origin("memory_thief")

        assert origin.name == "Memory Thief"
        assert "memory" in origin.description.lower()
        assert origin.starting_location.id == "thornfield_plains"
        assert "Blank Slate" in origin.starting_bonus.trait
        assert any("memory" in hook.lower() for hook in origin.narrative_hooks)

    def test_shadow_apprentice_details(self, origin_service):
        """Test Shadow Apprentice origin has correct details."""
        origin = origin_service.load_origin("shadow_apprentice")

        assert origin.name == "Shadow Apprentice"
        assert "master" in origin.description.lower()
        assert origin.starting_location.id == "shadowfen"
        assert "Trained in Shadows" in origin.starting_bonus.trait
        assert any("master" in hook.lower() for hook in origin.narrative_hooks)

    def test_escaped_captive_details(self, origin_service):
        """Test Escaped Captive origin has correct details."""
        origin = origin_service.load_origin("escaped_captive")

        assert origin.name == "The Escaped Captive"
        assert "prison" in origin.description.lower() or "ironpeak" in origin.description.lower()
        assert origin.starting_location.id == "ironpeak_pass"
        assert "Hardened Survivor" in origin.starting_bonus.trait
        assert any("prison" in hook.lower() or "past" in hook.lower() for hook in origin.narrative_hooks)

    def test_origin_serialization(self, origin_service):
        """Test that origins can be serialized to dict and back."""
        original = origin_service.load_origin("soul_revenant")

        # Serialize to dict
        origin_dict = original.to_dict()
        assert isinstance(origin_dict, dict)
        assert origin_dict["id"] == "soul_revenant"
        assert origin_dict["name"] == "Soul Revenant"

        # Deserialize from dict
        restored = Origin.from_dict(origin_dict)
        assert restored.id == original.id
        assert restored.name == original.name
        assert restored.description == original.description
        assert restored.starting_location.id == original.starting_location.id
        assert restored.starting_bonus.trait == original.starting_bonus.trait

    def test_caching_works(self, origin_service):
        """Test that caching improves performance on repeated loads."""
        # First load
        origin1 = origin_service.load_origin("soul_revenant")

        # Second load should come from cache
        origin2 = origin_service.load_origin("soul_revenant")

        # Should be the exact same instance from cache
        assert origin1 is origin2

    def test_cache_clear(self, origin_service):
        """Test that cache clearing works correctly."""
        # Load origins to populate cache
        origin_service.load_all_origins()
        assert len(origin_service._origins_cache) > 0

        # Clear cache
        origin_service.clear_cache()
        assert len(origin_service._origins_cache) == 0
        assert origin_service._all_origins_loaded is False

    def test_reload_origins(self, origin_service):
        """Test that reloading clears cache and reloads from file."""
        # Load origins
        origins1 = origin_service.load_all_origins()

        # Reload
        origins2 = origin_service.reload_origins()

        # Should have same content but be fresh instances
        assert len(origins1) == len(origins2)
        assert all(o1.id == o2.id for o1, o2 in zip(origins1, origins2))

    def test_get_all_origin_ids(self, origin_service):
        """Test getting list of all origin IDs."""
        origin_ids = origin_service.get_all_origin_ids()

        assert isinstance(origin_ids, list)
        assert len(origin_ids) == 4
        assert "soul_revenant" in origin_ids
        assert "memory_thief" in origin_ids
        assert "shadow_apprentice" in origin_ids
        assert "escaped_captive" in origin_ids

    def test_get_origin_by_id_alias(self, origin_service):
        """Test that get_origin_by_id is an alias for load_origin."""
        origin1 = origin_service.load_origin("soul_revenant")
        origin2 = origin_service.get_origin_by_id("soul_revenant")

        assert origin1 is origin2
