"""
Cache layer for MassiveToolkit with per-endpoint TTLs.

Different endpoints have different data freshness requirements:
- Options Greeks change tick-by-tick (15 min TTL for decision cadence)
- Short Interest updates bi-monthly (12 hour TTL)
- Short Volume updates daily (6 hour TTL)
- Earnings data is quarterly (24 hour TTL)
- Analyst Ratings are sporadic (4 hour TTL)
"""

from navconfig.logging import logging

from ..cache import ToolCache, DEFAULT_TOOL_CACHE_TTL


class MassiveCache:
    """
    Cache layer for MassiveToolkit with per-endpoint TTLs.

    Wraps the existing ToolCache infrastructure to provide endpoint-aware
    caching with appropriate TTLs based on data freshness requirements.

    Usage:
        cache = MassiveCache()

        # Check cache before API call
        cached = await cache.get("options_chain", underlying="AAPL")
        if cached:
            return cached

        # Store result after API call
        await cache.set("options_chain", result, underlying="AAPL")
    """

    # Tool name used as prefix in cache keys
    TOOL_NAME = "massive"

    # TTLs by endpoint (in seconds)
    TTLS = {
        "options_chain": 900,       # 15 minutes - Greeks change frequently
        "short_interest": 43200,    # 12 hours - FINRA bi-monthly data
        "short_volume": 21600,      # 6 hours - daily data, post-market
        "earnings": 86400,          # 24 hours - quarterly data
        "analyst_ratings": 14400,   # 4 hours - sporadic updates
        "consensus_ratings": 14400,  # 4 hours - same as analyst ratings
    }

    def __init__(
        self,
        redis_url: str | None = None,
        default_ttl: int = DEFAULT_TOOL_CACHE_TTL,
    ):
        """
        Initialize MassiveCache.

        Args:
            redis_url: Optional Redis URL (uses default from config if not provided)
            default_ttl: Default TTL for endpoints not in TTLS dict
        """
        self._cache = ToolCache(
            prefix="massive_cache",
            ttl=default_ttl,
            redis_url=redis_url,
        )
        self._default_ttl = default_ttl
        self.logger = logging.getLogger(__name__)

    def _make_key(self, endpoint: str, **params) -> str:
        """
        Generate a human-readable cache key from endpoint and parameters.

        This is primarily for logging/debugging. The actual Redis key
        is generated by ToolCache with parameter hashing.

        Args:
            endpoint: API endpoint name (e.g., "options_chain")
            **params: Query parameters

        Returns:
            Human-readable key string
        """
        param_parts = []
        for k, v in sorted(params.items()):
            if v is not None:
                param_parts.append(f"{k}={v}")

        param_str = ":".join(param_parts) if param_parts else "no_params"
        return f"massive:{endpoint}:{param_str}"

    def get_ttl(self, endpoint: str) -> int:
        """
        Get the TTL for a specific endpoint.

        Args:
            endpoint: API endpoint name

        Returns:
            TTL in seconds
        """
        return self.TTLS.get(endpoint, self._default_ttl)

    async def get(self, endpoint: str, **params) -> dict | None:
        """
        Get cached result for endpoint with given parameters.

        Args:
            endpoint: API endpoint name (e.g., "options_chain", "short_interest")
            **params: Query parameters used for this API call

        Returns:
            Cached dict if hit, None if miss
        """
        # Filter out None values from params
        filtered_params = {k: v for k, v in params.items() if v is not None}

        result = await self._cache.get(
            self.TOOL_NAME,
            endpoint,
            **filtered_params,
        )

        if result is not None:
            self.logger.debug(
                "Cache HIT: %s",
                self._make_key(endpoint, **filtered_params),
            )
        else:
            self.logger.debug(
                "Cache MISS: %s",
                self._make_key(endpoint, **filtered_params),
            )

        return result

    async def set(self, endpoint: str, data: dict, ttl: int | None = None, **params) -> None:
        """
        Cache result with endpoint-specific TTL.

        Args:
            endpoint: API endpoint name
            data: Data to cache (must be JSON-serializable)
            ttl: Optional override for endpoint TTL
            **params: Query parameters used for this API call
        """
        # Filter out None values from params
        filtered_params = {k: v for k, v in params.items() if v is not None}

        if ttl is None:
            ttl = self.get_ttl(endpoint)

        await self._cache.set(
            self.TOOL_NAME,
            endpoint,
            data,
            ttl=ttl,
            **filtered_params,
        )

        self.logger.debug(
            "Cache SET: %s (ttl=%ds)",
            self._make_key(endpoint, **filtered_params),
            ttl,
        )

    async def invalidate(self, endpoint: str, **params) -> bool:
        """
        Invalidate a specific cache entry.

        Note: This requires direct Redis access since ToolCache doesn't
        expose a delete method. Returns False if not supported.

        Args:
            endpoint: API endpoint name
            **params: Query parameters

        Returns:
            True if invalidated, False if not supported or failed
        """
        try:
            # Access the underlying Redis client
            r = await self._cache._get_redis()
            key = self._cache._build_key(
                self.TOOL_NAME,
                endpoint,
                **{k: v for k, v in params.items() if v is not None},
            )
            result = await r.delete(key)
            if result:
                self.logger.debug("Cache INVALIDATE: %s", key)
            return bool(result)
        except Exception as e:
            self.logger.warning("Cache invalidate error: %s", e)
            return False

    async def invalidate_endpoint(self, endpoint: str) -> int:
        """
        Invalidate all cache entries for an endpoint.

        Uses Redis SCAN to find and delete all keys matching the endpoint pattern.

        Args:
            endpoint: API endpoint name

        Returns:
            Number of keys deleted
        """
        try:
            r = await self._cache._get_redis()
            pattern = f"{self._cache.prefix}:{self.TOOL_NAME}:{endpoint}:*"
            count = 0

            # Use SCAN to find matching keys (non-blocking)
            async for key in r.scan_iter(match=pattern, count=100):
                await r.delete(key)
                count += 1

            if count:
                self.logger.debug(
                    "Cache INVALIDATE endpoint %s: %d keys",
                    endpoint,
                    count,
                )
            return count
        except Exception as e:
            self.logger.warning("Cache invalidate_endpoint error: %s", e)
            return 0

    async def close(self) -> None:
        """Close the underlying cache connection."""
        await self._cache.close()
