"""Intelligent caching system for pricing data."""

import time
from typing import Dict, Any, Optional, Tuple
from datetime import datetime, timedelta
from collections import OrderedDict
from src.core.logger import get_logger

logger = get_logger(__name__)


class CacheEntry:
    """Cache entry with TTL support."""
    
    def __init__(self, data: Any, ttl: float):
        """Initialize cache entry.
        
        Args:
            data: Cached data
            ttl: Time to live in seconds
        """
        self.data = data
        self.expires_at = time.time() + ttl
        self.created_at = time.time()
        self.access_count = 0
        self.last_accessed = time.time()
    
    def is_expired(self) -> bool:
        """Check if entry is expired.
        
        Returns:
            True if expired
        """
        return time.time() > self.expires_at
    
    def touch(self):
        """Update access statistics."""
        self.access_count += 1
        self.last_accessed = time.time()
    
    def age(self) -> float:
        """Get age of entry in seconds.
        
        Returns:
            Age in seconds
        """
        return time.time() - self.created_at


class CacheManager:
    """Intelligent cache manager with TTL and size limits.
    
    Implements LRU (Least Recently Used) eviction when size limit is reached.
    """
    
    def __init__(
        self,
        default_ttl: float = 60.0,
        max_size: int = 1000,
        ticker_ttl: float = 2.0,
        ohlcv_ttl: float = 60.0
    ):
        """Initialize cache manager.
        
        Args:
            default_ttl: Default TTL in seconds
            max_size: Maximum number of cache entries
            ticker_ttl: TTL for ticker data in seconds
            ohlcv_ttl: TTL for OHLCV data in seconds
        """
        self.default_ttl = default_ttl
        self.max_size = max_size
        self.ticker_ttl = ticker_ttl
        self.ohlcv_ttl = ohlcv_ttl
        
        # Use OrderedDict for LRU eviction
        self._cache: OrderedDict[str, CacheEntry] = OrderedDict()
        self._hits = 0
        self._misses = 0
        self._evictions = 0
        
        self.logger = get_logger(__name__)
    
    def get(self, key: str) -> Optional[Any]:
        """Get value from cache.
        
        Args:
            key: Cache key
            
        Returns:
            Cached value or None if not found/expired
        """
        # Clean expired entries
        self._cleanup_expired()
        
        if key not in self._cache:
            self._misses += 1
            return None
        
        entry = self._cache[key]
        
        if entry.is_expired():
            # Remove expired entry
            del self._cache[key]
            self._misses += 1
            return None
        
        # Update access (move to end for LRU)
        entry.touch()
        self._cache.move_to_end(key)
        self._hits += 1
        return entry.data
    
    def set(
        self,
        key: str,
        value: Any,
        ttl: Optional[float] = None,
        cache_type: Optional[str] = None
    ):
        """Set value in cache.
        
        Args:
            key: Cache key
            value: Value to cache
            ttl: Time to live in seconds (uses type-specific or default if None)
            cache_type: Type of cache ('ticker' or 'ohlcv') for type-specific TTL
        """
        # Determine TTL
        if ttl is None:
            if cache_type == 'ticker':
                ttl = self.ticker_ttl
            elif cache_type == 'ohlcv':
                ttl = self.ohlcv_ttl
            else:
                ttl = self.default_ttl
        
        # Check if we need to evict
        if len(self._cache) >= self.max_size and key not in self._cache:
            self._evict_lru()
        
        # Create entry
        entry = CacheEntry(value, ttl)
        
        # Add or update
        if key in self._cache:
            self._cache.move_to_end(key)
        self._cache[key] = entry
    
    def delete(self, key: str) -> bool:
        """Delete entry from cache.
        
        Args:
            key: Cache key
            
        Returns:
            True if entry was deleted, False if not found
        """
        if key in self._cache:
            del self._cache[key]
            return True
        return False
    
    def clear(self):
        """Clear all cache entries."""
        self._cache.clear()
        self.logger.info("Cache cleared")
    
    def _cleanup_expired(self):
        """Remove expired entries from cache."""
        expired_keys = [
            key for key, entry in self._cache.items()
            if entry.is_expired()
        ]
        for key in expired_keys:
            del self._cache[key]
    
    def _evict_lru(self):
        """Evict least recently used entry."""
        if self._cache:
            # Remove oldest (first) entry
            self._cache.popitem(last=False)
            self._evictions += 1
    
    def get_stats(self) -> Dict[str, Any]:
        """Get cache statistics.
        
        Returns:
            Dictionary with cache statistics
        """
        total_requests = self._hits + self._misses
        hit_rate = (self._hits / total_requests * 100) if total_requests > 0 else 0.0
        
        # Calculate average age
        if self._cache:
            avg_age = sum(entry.age() for entry in self._cache.values()) / len(self._cache)
        else:
            avg_age = 0.0
        
        return {
            'size': len(self._cache),
            'max_size': self.max_size,
            'hits': self._hits,
            'misses': self._misses,
            'hit_rate': round(hit_rate, 2),
            'evictions': self._evictions,
            'avg_age_seconds': round(avg_age, 2),
        }
    
    def invalidate_pattern(self, pattern: str):
        """Invalidate entries matching a pattern.
        
        Args:
            pattern: String pattern to match (simple substring match)
        """
        keys_to_delete = [key for key in self._cache.keys() if pattern in key]
        for key in keys_to_delete:
            del self._cache[key]
        
        if keys_to_delete:
            self.logger.info(f"Invalidated {len(keys_to_delete)} cache entries matching '{pattern}'")
