"""
Tests for Memory System Configuration.
"""

import pytest
from pydantic import ValidationError

from app.services.memory.config import (
    MemorySettings,
    get_default_settings,
    get_memory_settings,
    reset_memory_settings,
)


class TestMemorySettings:
    """Tests for MemorySettings class."""

    def test_default_settings(self) -> None:
        """Test that default settings are valid."""
        settings = MemorySettings()

        # Working memory defaults
        assert settings.working_memory_backend == "redis"
        assert settings.working_memory_default_ttl_seconds == 3600
        assert settings.working_memory_max_items_per_session == 1000

        # Redis defaults
        assert settings.redis_url == "redis://localhost:6379/0"
        assert settings.redis_prefix == "mem"

        # Episodic defaults
        assert settings.episodic_max_episodes_per_project == 10000
        assert settings.episodic_default_importance == 0.5

        # Semantic defaults
        assert settings.semantic_max_facts_per_project == 50000
        assert settings.semantic_min_confidence == 0.1

        # Procedural defaults
        assert settings.procedural_max_procedures_per_project == 1000
        assert settings.procedural_min_success_rate == 0.3

        # Embedding defaults
        assert settings.embedding_model == "text-embedding-3-small"
        assert settings.embedding_dimensions == 1536

        # Retrieval defaults
        assert settings.retrieval_default_limit == 10
        assert settings.retrieval_max_limit == 100

    def test_invalid_backend(self) -> None:
        """Test that invalid backend raises error."""
        with pytest.raises(ValidationError) as exc_info:
            MemorySettings(working_memory_backend="invalid")

        assert "backend must be one of" in str(exc_info.value)

    def test_valid_backends(self) -> None:
        """Test valid backend values."""
        redis_settings = MemorySettings(working_memory_backend="redis")
        assert redis_settings.working_memory_backend == "redis"

        memory_settings = MemorySettings(working_memory_backend="memory")
        assert memory_settings.working_memory_backend == "memory"

    def test_invalid_embedding_model(self) -> None:
        """Test that invalid embedding model raises error."""
        with pytest.raises(ValidationError) as exc_info:
            MemorySettings(embedding_model="invalid-model")

        assert "embedding_model must be one of" in str(exc_info.value)

    def test_valid_embedding_models(self) -> None:
        """Test valid embedding model values."""
        for model in [
            "text-embedding-3-small",
            "text-embedding-3-large",
            "text-embedding-ada-002",
        ]:
            settings = MemorySettings(embedding_model=model)
            assert settings.embedding_model == model

    def test_retrieval_limit_validation(self) -> None:
        """Test that default limit cannot exceed max limit."""
        with pytest.raises(ValidationError) as exc_info:
            MemorySettings(
                retrieval_default_limit=50,
                retrieval_max_limit=25,
            )

        assert "cannot exceed retrieval_max_limit" in str(exc_info.value)

    def test_valid_retrieval_limits(self) -> None:
        """Test valid retrieval limit combinations."""
        settings = MemorySettings(
            retrieval_default_limit=10,
            retrieval_max_limit=50,
        )
        assert settings.retrieval_default_limit == 10
        assert settings.retrieval_max_limit == 50

        # Equal limits should be valid
        settings = MemorySettings(
            retrieval_default_limit=25,
            retrieval_max_limit=25,
        )
        assert settings.retrieval_default_limit == 25
        assert settings.retrieval_max_limit == 25

    def test_ttl_bounds(self) -> None:
        """Test TTL setting bounds."""
        # Valid TTL
        settings = MemorySettings(working_memory_default_ttl_seconds=1800)
        assert settings.working_memory_default_ttl_seconds == 1800

        # Too low
        with pytest.raises(ValidationError):
            MemorySettings(working_memory_default_ttl_seconds=30)

        # Too high
        with pytest.raises(ValidationError):
            MemorySettings(working_memory_default_ttl_seconds=100000)

    def test_confidence_bounds(self) -> None:
        """Test confidence score bounds."""
        # Valid confidence
        settings = MemorySettings(semantic_min_confidence=0.5)
        assert settings.semantic_min_confidence == 0.5

        # Bounds
        settings = MemorySettings(semantic_min_confidence=0.0)
        assert settings.semantic_min_confidence == 0.0

        settings = MemorySettings(semantic_min_confidence=1.0)
        assert settings.semantic_min_confidence == 1.0

        # Out of bounds
        with pytest.raises(ValidationError):
            MemorySettings(semantic_min_confidence=-0.1)

        with pytest.raises(ValidationError):
            MemorySettings(semantic_min_confidence=1.1)

    def test_get_working_memory_config(self) -> None:
        """Test working memory config dictionary."""
        settings = MemorySettings()
        config = settings.get_working_memory_config()

        assert config["backend"] == "redis"
        assert config["default_ttl_seconds"] == 3600
        assert config["max_items_per_session"] == 1000
        assert config["max_value_size_bytes"] == 1048576
        assert config["checkpoint_enabled"] is True

    def test_get_redis_config(self) -> None:
        """Test Redis config dictionary."""
        settings = MemorySettings()
        config = settings.get_redis_config()

        assert config["url"] == "redis://localhost:6379/0"
        assert config["prefix"] == "mem"
        assert config["connection_timeout_seconds"] == 5

    def test_get_embedding_config(self) -> None:
        """Test embedding config dictionary."""
        settings = MemorySettings()
        config = settings.get_embedding_config()

        assert config["model"] == "text-embedding-3-small"
        assert config["dimensions"] == 1536
        assert config["batch_size"] == 100
        assert config["cache_enabled"] is True

    def test_get_consolidation_config(self) -> None:
        """Test consolidation config dictionary."""
        settings = MemorySettings()
        config = settings.get_consolidation_config()

        assert config["enabled"] is True
        assert config["batch_size"] == 100
        assert config["schedule_cron"] == "0 3 * * *"
        assert config["working_to_episodic_delay_minutes"] == 30

    def test_to_dict(self) -> None:
        """Test full settings to dictionary."""
        settings = MemorySettings()
        config = settings.to_dict()

        assert "working_memory" in config
        assert "redis" in config
        assert "episodic" in config
        assert "semantic" in config
        assert "procedural" in config
        assert "embedding" in config
        assert "retrieval" in config
        assert "consolidation" in config
        assert "pruning" in config
        assert "cache" in config
        assert "performance" in config


class TestMemorySettingsSingleton:
    """Tests for MemorySettings singleton functions."""

    def setup_method(self) -> None:
        """Reset singleton before each test."""
        reset_memory_settings()

    def teardown_method(self) -> None:
        """Reset singleton after each test."""
        reset_memory_settings()

    def test_get_memory_settings_singleton(self) -> None:
        """Test that get_memory_settings returns same instance."""
        settings1 = get_memory_settings()
        settings2 = get_memory_settings()

        assert settings1 is settings2

    def test_reset_memory_settings(self) -> None:
        """Test that reset creates new instance."""
        settings1 = get_memory_settings()
        reset_memory_settings()
        settings2 = get_memory_settings()

        assert settings1 is not settings2

    def test_get_default_settings_cached(self) -> None:
        """Test that get_default_settings is cached."""
        # Clear the lru_cache first
        get_default_settings.cache_clear()

        settings1 = get_default_settings()
        settings2 = get_default_settings()

        assert settings1 is settings2

    def test_default_settings_immutable_pattern(self) -> None:
        """Test that default settings provide consistent values."""
        defaults = get_default_settings()
        assert defaults.working_memory_backend == "redis"
        assert defaults.embedding_model == "text-embedding-3-small"
