"""
Data Cache Module - Caching mechanism for market data and indicators

This module provides a TTL-based cache to reduce API calls and improve performance.
"""

import logging
from typing import Any, Optional
from cachetools import TTLCache
import threading


class DataCache:
    """
    Data cache manager using TTL (Time-To-Live) cache

    This cache automatically expires entries after a specified time period,
    reducing unnecessary API calls while ensuring data freshness.
    """

    def __init__(self, ttl_minutes: int = 60, maxsize: int = 1000):
        """
        Initialize data cache

        Args:
            ttl_minutes: Time-to-live in minutes for cached entries
            maxsize: Maximum number of entries in cache
        """
        self.logger = logging.getLogger(__name__)
        self.ttl_seconds = ttl_minutes * 60
        self.cache = TTLCache(maxsize=maxsize, ttl=self.ttl_seconds)
        self.lock = threading.Lock()

        self.logger.info(f"DataCache initialized: TTL={ttl_minutes}min, maxsize={maxsize}")

    def get(self, key: str) -> Optional[Any]:
        """
        Get cached data by key

        Args:
            key: Cache key

        Returns:
            Cached data if exists and not expired, None otherwise
        """
        with self.lock:
            value = self.cache.get(key)

            if value is not None:
                self.logger.debug(f"Cache hit: {key}")
            else:
                self.logger.debug(f"Cache miss: {key}")

            return value

    def set(self, key: str, value: Any) -> None:
        """
        Set cached data

        Args:
            key: Cache key
            value: Data to cache
        """
        with self.lock:
            self.cache[key] = value
            self.logger.debug(f"Cache set: {key}")

    def invalidate(self, pattern: str) -> int:
        """
        Invalidate (remove) cached entries matching a pattern

        Args:
            pattern: Pattern to match against cache keys (substring match)

        Returns:
            Number of entries removed
        """
        with self.lock:
            keys_to_remove = [key for key in self.cache.keys() if pattern in key]

            for key in keys_to_remove:
                del self.cache[key]

            count = len(keys_to_remove)
            if count > 0:
                self.logger.info(f"Invalidated {count} cache entries matching pattern: {pattern}")

            return count

    def clear(self) -> None:
        """Clear all cached entries"""
        with self.lock:
            size = len(self.cache)
            self.cache.clear()
            self.logger.info(f"Cache cleared: {size} entries removed")

    def size(self) -> int:
        """
        Get current cache size

        Returns:
            Number of entries in cache
        """
        with self.lock:
            return len(self.cache)

    def stats(self) -> dict:
        """
        Get cache statistics

        Returns:
            Dictionary with cache statistics
        """
        with self.lock:
            return {
                "size": len(self.cache),
                "maxsize": self.cache.maxsize,
                "ttl_seconds": self.ttl_seconds,
                "ttl_minutes": self.ttl_seconds / 60,
            }

    @staticmethod
    def make_key(*args, **kwargs) -> str:
        """
        Create a cache key from arguments

        Args:
            *args: Positional arguments
            **kwargs: Keyword arguments

        Returns:
            Cache key string

        Example:
            >>> DataCache.make_key('BTC/USDT', '1h', limit=500)
            'BTC/USDT:1h:limit=500'
        """
        parts = [str(arg) for arg in args]

        if kwargs:
            sorted_kwargs = sorted(kwargs.items())
            kw_parts = [f"{k}={v}" for k, v in sorted_kwargs]
            parts.extend(kw_parts)

        return ":".join(parts)
