"""
Tests for ActionPromptLoader service

Tests loading from YAML, filtering by tier and location,
and error handling.
"""

import pytest
import tempfile
import os

from app.services.action_prompt_loader import (
    ActionPromptLoader,
    ActionPromptLoaderError,
    ActionPromptNotFoundError,
)
from app.models.action_prompt import LocationType
from app.ai.model_selector import UserTier


class TestActionPromptLoader:
    """Tests for ActionPromptLoader service."""

    @pytest.fixture(autouse=True)
    def reset_singleton(self):
        """Reset singleton before each test."""
        ActionPromptLoader.reset_instance()
        yield
        ActionPromptLoader.reset_instance()

    @pytest.fixture
    def sample_yaml(self):
        """Create a sample YAML file for testing."""
        content = """
action_prompts:
  - prompt_id: test_free
    category: explore
    display_text: Free Action
    description: Available to all
    tier_required: free
    context_filter: [town, tavern]
    dm_prompt_template: Test template

  - prompt_id: test_premium
    category: gather_info
    display_text: Premium Action
    description: Premium only
    tier_required: premium
    context_filter: [any]
    dm_prompt_template: Premium template

  - prompt_id: test_elite
    category: special
    display_text: Elite Action
    description: Elite only
    tier_required: elite
    context_filter: [library]
    dm_prompt_template: Elite template
"""
        with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
            f.write(content)
            filepath = f.name

        yield filepath
        os.unlink(filepath)

    @pytest.fixture
    def loader(self, sample_yaml):
        """Create a loader with sample data."""
        loader = ActionPromptLoader()
        loader.load_from_yaml(sample_yaml)
        return loader

    # Loading tests

    def test_load_from_yaml(self, sample_yaml):
        """Test loading prompts from YAML file."""
        loader = ActionPromptLoader()
        count = loader.load_from_yaml(sample_yaml)

        assert count == 3
        assert loader.is_loaded()

    def test_load_file_not_found(self):
        """Test error when file doesn't exist."""
        loader = ActionPromptLoader()

        with pytest.raises(ActionPromptLoaderError) as exc_info:
            loader.load_from_yaml("/nonexistent/path.yaml")

        assert "not found" in str(exc_info.value)

    def test_load_invalid_yaml(self):
        """Test error when YAML is malformed."""
        with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
            f.write("invalid: yaml: content: [")
            filepath = f.name

        try:
            loader = ActionPromptLoader()
            with pytest.raises(ActionPromptLoaderError) as exc_info:
                loader.load_from_yaml(filepath)

            assert "Invalid YAML" in str(exc_info.value)
        finally:
            os.unlink(filepath)

    def test_load_missing_key(self):
        """Test error when action_prompts key is missing."""
        with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f:
            f.write("other_key: value")
            filepath = f.name

        try:
            loader = ActionPromptLoader()
            with pytest.raises(ActionPromptLoaderError) as exc_info:
                loader.load_from_yaml(filepath)

            assert "Missing 'action_prompts'" in str(exc_info.value)
        finally:
            os.unlink(filepath)

    # Get methods tests

    def test_get_all_actions(self, loader):
        """Test getting all loaded actions."""
        actions = loader.get_all_actions()

        assert len(actions) == 3
        prompt_ids = [a.prompt_id for a in actions]
        assert "test_free" in prompt_ids
        assert "test_premium" in prompt_ids
        assert "test_elite" in prompt_ids

    def test_get_action_by_id(self, loader):
        """Test getting action by ID."""
        action = loader.get_action_by_id("test_free")

        assert action.prompt_id == "test_free"
        assert action.display_text == "Free Action"

    def test_get_action_by_id_not_found(self, loader):
        """Test error when action ID not found."""
        with pytest.raises(ActionPromptNotFoundError):
            loader.get_action_by_id("nonexistent")

    # Filtering tests

    def test_get_available_actions_free_tier(self, loader):
        """Test filtering for free tier user."""
        actions = loader.get_available_actions(UserTier.FREE, LocationType.TOWN)

        assert len(actions) == 1
        assert actions[0].prompt_id == "test_free"

    def test_get_available_actions_premium_tier(self, loader):
        """Test filtering for premium tier user."""
        actions = loader.get_available_actions(UserTier.PREMIUM, LocationType.TOWN)

        # Premium gets: test_free (town) + test_premium (any)
        assert len(actions) == 2
        prompt_ids = [a.prompt_id for a in actions]
        assert "test_free" in prompt_ids
        assert "test_premium" in prompt_ids

    def test_get_available_actions_elite_tier(self, loader):
        """Test filtering for elite tier user."""
        actions = loader.get_available_actions(UserTier.ELITE, LocationType.LIBRARY)

        # Elite in library gets: test_premium (any) + test_elite (library)
        assert len(actions) == 2
        prompt_ids = [a.prompt_id for a in actions]
        assert "test_premium" in prompt_ids
        assert "test_elite" in prompt_ids

    def test_get_available_actions_location_filter(self, loader):
        """Test that location filtering works correctly."""
        # In town, no elite actions available
        actions = loader.get_available_actions(UserTier.ELITE, LocationType.TOWN)
        prompt_ids = [a.prompt_id for a in actions]

        assert "test_elite" not in prompt_ids  # Only in library

    def test_get_actions_by_tier(self, loader):
        """Test getting actions by tier without location filter."""
        free_actions = loader.get_actions_by_tier(UserTier.FREE)
        premium_actions = loader.get_actions_by_tier(UserTier.PREMIUM)
        elite_actions = loader.get_actions_by_tier(UserTier.ELITE)

        assert len(free_actions) == 1
        assert len(premium_actions) == 2
        assert len(elite_actions) == 3

    def test_get_actions_by_category(self, loader):
        """Test getting actions by category."""
        explore_actions = loader.get_actions_by_category("explore")
        special_actions = loader.get_actions_by_category("special")

        assert len(explore_actions) == 1
        assert explore_actions[0].prompt_id == "test_free"

        assert len(special_actions) == 1
        assert special_actions[0].prompt_id == "test_elite"

    def test_get_locked_actions(self, loader):
        """Test getting locked actions for upgrade prompts."""
        # Free user in library sees elite action as locked
        locked = loader.get_locked_actions(UserTier.FREE, LocationType.LIBRARY)

        # test_premium (any) and test_elite (library) are locked for free
        assert len(locked) == 2
        prompt_ids = [a.prompt_id for a in locked]
        assert "test_premium" in prompt_ids
        assert "test_elite" in prompt_ids

    # Singleton and reload tests

    def test_singleton_pattern(self, sample_yaml):
        """Test that loader is singleton."""
        loader1 = ActionPromptLoader()
        loader1.load_from_yaml(sample_yaml)

        loader2 = ActionPromptLoader()

        assert loader1 is loader2
        assert loader2.is_loaded()

    def test_reload(self, sample_yaml):
        """Test reloading prompts."""
        loader = ActionPromptLoader()
        loader.load_from_yaml(sample_yaml)

        # Modify and reload
        count = loader.reload(sample_yaml)
        assert count == 3

    def test_get_prompt_count(self, loader):
        """Test getting prompt count."""
        assert loader.get_prompt_count() == 3


class TestActionPromptLoaderIntegration:
    """Integration tests with actual YAML file."""

    @pytest.fixture(autouse=True)
    def reset_singleton(self):
        """Reset singleton before each test."""
        ActionPromptLoader.reset_instance()
        yield
        ActionPromptLoader.reset_instance()

    def test_load_actual_yaml(self):
        """Test loading the actual action_prompts.yaml file."""
        loader = ActionPromptLoader()
        filepath = os.path.join(
            os.path.dirname(__file__),
            '..', 'app', 'data', 'action_prompts.yaml'
        )

        if os.path.exists(filepath):
            count = loader.load_from_yaml(filepath)

            # Should have 10 actions
            assert count == 10

            # Verify tier distribution
            free_actions = loader.get_actions_by_tier(UserTier.FREE)
            premium_actions = loader.get_actions_by_tier(UserTier.PREMIUM)
            elite_actions = loader.get_actions_by_tier(UserTier.ELITE)

            assert len(free_actions) == 4  # Only free tier
            assert len(premium_actions) == 7  # Free + premium
            assert len(elite_actions) == 10  # All

    def test_free_tier_town_actions(self):
        """Test free tier actions in town location."""
        loader = ActionPromptLoader()
        filepath = os.path.join(
            os.path.dirname(__file__),
            '..', 'app', 'data', 'action_prompts.yaml'
        )

        if os.path.exists(filepath):
            loader.load_from_yaml(filepath)

            actions = loader.get_available_actions(UserTier.FREE, LocationType.TOWN)

            # Free user in town should have:
            # - ask_locals (town/tavern)
            # - search_supplies (any)
            # - rest_recover (town/tavern/safe_area)
            prompt_ids = [a.prompt_id for a in actions]
            assert "ask_locals" in prompt_ids
            assert "search_supplies" in prompt_ids
            assert "rest_recover" in prompt_ids

    def test_premium_tier_wilderness_actions(self):
        """Test premium tier actions in wilderness location."""
        loader = ActionPromptLoader()
        filepath = os.path.join(
            os.path.dirname(__file__),
            '..', 'app', 'data', 'action_prompts.yaml'
        )

        if os.path.exists(filepath):
            loader.load_from_yaml(filepath)

            actions = loader.get_available_actions(UserTier.PREMIUM, LocationType.WILDERNESS)

            prompt_ids = [a.prompt_id for a in actions]
            # Should include wilderness actions
            assert "explore_area" in prompt_ids
            assert "make_camp" in prompt_ids
            assert "search_supplies" in prompt_ids
