"""Tests for fix runtime cache."""

import time

import pytest

from risicare.runtime.cache import FixCache
from risicare.runtime.config import ActiveFix, FixRuntimeConfig


@pytest.fixture
def config():
    """Create test configuration."""
    return FixRuntimeConfig(
        cache_enabled=True,
        cache_ttl_seconds=5,
        cache_max_entries=100,
    )


@pytest.fixture
def cache(config):
    """Create test cache."""
    return FixCache(config)


@pytest.fixture
def sample_fix():
    """Create sample fix."""
    return ActiveFix(
        fix_id="fix-123",
        deployment_id="deploy-456",
        error_code="TOOL.EXECUTION.TIMEOUT",
        fix_type="retry",
        config={"max_retries": 3},
        traffic_percentage=100,
        version=1,
    )


class TestFixCache:
    """Tests for fix cache."""

    def test_set_and_get(self, cache, sample_fix):
        """Test basic set and get."""
        cache.set(sample_fix)
        result = cache.get("TOOL.EXECUTION.TIMEOUT")

        assert result is not None
        assert result.fix_id == "fix-123"
        assert result.fix_type == "retry"

    def test_get_nonexistent(self, cache):
        """Test get for nonexistent key."""
        result = cache.get("NONEXISTENT.ERROR.CODE")
        assert result is None

    def test_wildcard_matching(self, cache):
        """Test wildcard pattern matching."""
        wildcard_fix = ActiveFix(
            fix_id="fix-wildcard",
            deployment_id="deploy-789",
            error_code="TOOL.EXECUTION.*",
            fix_type="retry",
            config={},
            traffic_percentage=100,
            version=1,
        )
        cache.set(wildcard_fix)

        # Should match specific error code
        result = cache.get("TOOL.EXECUTION.TIMEOUT")
        assert result is not None
        assert result.fix_id == "fix-wildcard"

        # Should also match other codes in same category
        result = cache.get("TOOL.EXECUTION.FAILURE")
        assert result is not None
        assert result.fix_id == "fix-wildcard"

        # Should not match different category
        result = cache.get("TOOL.VALIDATION.ERROR")
        assert result is None

    def test_exact_match_priority(self, cache):
        """Test exact match takes priority over wildcard."""
        wildcard_fix = ActiveFix(
            fix_id="fix-wildcard",
            deployment_id="deploy-1",
            error_code="TOOL.*",
            fix_type="retry",
            config={},
            traffic_percentage=100,
            version=1,
        )
        exact_fix = ActiveFix(
            fix_id="fix-exact",
            deployment_id="deploy-2",
            error_code="TOOL.EXECUTION.TIMEOUT",
            fix_type="fallback",
            config={},
            traffic_percentage=100,
            version=1,
        )

        cache.set(wildcard_fix)
        cache.set(exact_fix)

        result = cache.get("TOOL.EXECUTION.TIMEOUT")
        assert result is not None
        assert result.fix_id == "fix-exact"

    def test_cache_expiration(self, cache, sample_fix):
        """Test cache entry expiration."""
        cache.set(sample_fix)

        # Should exist immediately
        assert cache.get("TOOL.EXECUTION.TIMEOUT") is not None

        # Wait for expiration
        time.sleep(6)  # TTL is 5 seconds

        # Should be expired
        result = cache.get("TOOL.EXECUTION.TIMEOUT")
        assert result is None

    def test_get_all(self, cache):
        """Test get all fixes."""
        fixes = [
            ActiveFix(
                fix_id=f"fix-{i}",
                deployment_id=f"deploy-{i}",
                error_code=f"ERROR.CODE.{i}",
                fix_type="retry",
                config={},
                traffic_percentage=100,
                version=1,
            )
            for i in range(3)
        ]

        for fix in fixes:
            cache.set(fix)

        all_fixes = cache.get_all()
        assert len(all_fixes) == 3

    def test_set_all(self, cache):
        """Test set all replaces cache."""
        # Add initial fix
        cache.set(
            ActiveFix(
                fix_id="initial",
                deployment_id="d1",
                error_code="ERROR.1",
                fix_type="retry",
                config={},
                traffic_percentage=100,
                version=1,
            )
        )

        # Replace with new fixes
        new_fixes = [
            ActiveFix(
                fix_id=f"new-{i}",
                deployment_id=f"d-{i}",
                error_code=f"NEW.ERROR.{i}",
                fix_type="retry",
                config={},
                traffic_percentage=100,
                version=1,
            )
            for i in range(2)
        ]
        cache.set_all(new_fixes)

        # Old fix should be gone
        assert cache.get("ERROR.1") is None

        # New fixes should exist
        all_fixes = cache.get_all()
        assert len(all_fixes) == 2

    def test_invalidate(self, cache, sample_fix):
        """Test invalidate specific entry."""
        cache.set(sample_fix)

        # Should exist
        assert cache.get("TOOL.EXECUTION.TIMEOUT") is not None

        # Invalidate
        result = cache.invalidate("TOOL.EXECUTION.TIMEOUT")
        assert result is True

        # Should be gone
        assert cache.get("TOOL.EXECUTION.TIMEOUT") is None

    def test_invalidate_nonexistent(self, cache):
        """Test invalidate nonexistent entry."""
        result = cache.invalidate("NONEXISTENT")
        assert result is False

    def test_clear(self, cache):
        """Test clear all entries."""
        for i in range(5):
            cache.set(
                ActiveFix(
                    fix_id=f"fix-{i}",
                    deployment_id=f"d-{i}",
                    error_code=f"ERROR.{i}",
                    fix_type="retry",
                    config={},
                    traffic_percentage=100,
                    version=1,
                )
            )

        count = cache.clear()
        assert count == 5
        assert len(cache.get_all()) == 0

    def test_stats(self, cache, sample_fix):
        """Test cache statistics."""
        cache.set(sample_fix)

        # Hit
        cache.get("TOOL.EXECUTION.TIMEOUT")
        # Miss
        cache.get("NONEXISTENT")

        stats = cache.stats()
        assert stats.hits == 1
        assert stats.misses == 1
        assert stats.size == 1

    def test_max_entries_eviction(self):
        """Test eviction when max entries exceeded."""
        config = FixRuntimeConfig(
            cache_enabled=True,
            cache_ttl_seconds=3600,
            cache_max_entries=5,
        )
        cache = FixCache(config)

        # Add 6 fixes
        for i in range(6):
            cache.set(
                ActiveFix(
                    fix_id=f"fix-{i}",
                    deployment_id=f"d-{i}",
                    error_code=f"ERROR.{i}",
                    fix_type="retry",
                    config={},
                    traffic_percentage=100,
                    version=1,
                )
            )

        # Should have max 5 entries
        assert len(cache.get_all()) <= 5

    def test_disabled_cache(self):
        """Test disabled cache returns None."""
        config = FixRuntimeConfig(cache_enabled=False)
        cache = FixCache(config)

        cache.set(
            ActiveFix(
                fix_id="fix-1",
                deployment_id="d-1",
                error_code="ERROR.1",
                fix_type="retry",
                config={},
                traffic_percentage=100,
                version=1,
            )
        )

        # Should return None when disabled
        assert cache.get("ERROR.1") is None
