"""Unit tests for cache manager."""

import pytest
import time
from src.data.cache_manager import CacheManager, CacheEntry


class TestCacheEntry:
    """Tests for CacheEntry."""
    
    def test_init(self):
        """Test cache entry initialization."""
        entry = CacheEntry("test_data", 60.0)
        assert entry.data == "test_data"
        assert entry.expires_at > time.time()
        assert entry.access_count == 0
    
    def test_is_expired(self):
        """Test expiration checking."""
        entry = CacheEntry("test_data", 0.01)  # Very short TTL
        assert not entry.is_expired()
        time.sleep(0.02)
        assert entry.is_expired()
    
    def test_touch(self):
        """Test access tracking."""
        entry = CacheEntry("test_data", 60.0)
        initial_count = entry.access_count
        entry.touch()
        assert entry.access_count == initial_count + 1


class TestCacheManager:
    """Tests for CacheManager."""
    
    @pytest.fixture
    def cache(self):
        """Create a cache manager instance."""
        return CacheManager(default_ttl=1.0, max_size=10)
    
    def test_get_set(self, cache):
        """Test basic get and set operations."""
        cache.set("key1", "value1")
        assert cache.get("key1") == "value1"
    
    def test_get_missing(self, cache):
        """Test getting non-existent key."""
        assert cache.get("missing") is None
    
    def test_expiration(self, cache):
        """Test cache entry expiration."""
        cache.set("key1", "value1", ttl=0.1)
        assert cache.get("key1") == "value1"
        time.sleep(0.2)
        assert cache.get("key1") is None
    
    def test_lru_eviction(self, cache):
        """Test LRU eviction when max size reached."""
        # Fill cache to max size
        for i in range(10):
            cache.set(f"key{i}", f"value{i}")
        
        # Add one more - should evict oldest
        cache.set("key10", "value10")
        
        # Oldest key should be evicted
        assert cache.get("key0") is None
        assert cache.get("key10") == "value10"
    
    def test_type_specific_ttl(self, cache):
        """Test type-specific TTL."""
        cache.set("ticker1", {"price": 100}, cache_type='ticker')
        cache.set("ohlcv1", [[1, 2, 3, 4, 5, 6]], cache_type='ohlcv')
        
        # Both should be cached
        assert cache.get("ticker1") is not None
        assert cache.get("ohlcv1") is not None
    
    def test_delete(self, cache):
        """Test cache entry deletion."""
        cache.set("key1", "value1")
        assert cache.get("key1") == "value1"
        
        cache.delete("key1")
        assert cache.get("key1") is None
    
    def test_clear(self, cache):
        """Test cache clearing."""
        cache.set("key1", "value1")
        cache.set("key2", "value2")
        
        cache.clear()
        
        assert cache.get("key1") is None
        assert cache.get("key2") is None
    
    def test_stats(self, cache):
        """Test cache statistics."""
        cache.set("key1", "value1")
        cache.get("key1")  # Hit
        cache.get("missing")  # Miss
        
        stats = cache.get_stats()
        
        assert stats['hits'] >= 1
        assert stats['misses'] >= 1
        assert stats['size'] == 1
        assert 'hit_rate' in stats
    
    def test_invalidate_pattern(self, cache):
        """Test pattern-based invalidation."""
        cache.set("ticker:BTC/USD", "value1")
        cache.set("ticker:ETH/USD", "value2")
        cache.set("ohlcv:BTC/USD", "value3")
        
        cache.invalidate_pattern("ticker:")
        
        assert cache.get("ticker:BTC/USD") is None
        assert cache.get("ticker:ETH/USD") is None
        assert cache.get("ohlcv:BTC/USD") is not None
