"""
POI Cache Service

Caches POI detection results to avoid hammering Overpass API.
POI data doesn't change frequently, so aggressive caching is appropriate.
"""

from typing import Optional, Dict, Any
import json
import structlog
from datetime import timedelta

from app.core.poi_config import (
    POI_CACHE_TTL_DAYS,
    POI_COORDINATE_PRECISION
)

logger = structlog.get_logger()


class POICacheService:
    """
    Redis-based cache for POI detection results.

    Caches results by rounded coordinates to allow reuse for nearby locations.
    Reduces load on Overpass API and improves onboarding performance.
    """

    def __init__(self, redis_client):
        """
        Initialize cache service.

        Args:
            redis_client: Redis client instance
        """
        self.redis = redis_client
        self.cache_ttl_days = POI_CACHE_TTL_DAYS
        self.coordinate_precision = POI_COORDINATE_PRECISION

    def _generate_cache_key(self, latitude: float, longitude: float) -> str:
        """
        Generate cache key from coordinates.

        Rounds coordinates to specified precision (default 4 decimals ≈ 10m).
        This allows cache reuse for bakeries in very close proximity.

        Args:
            latitude: Bakery latitude
            longitude: Bakery longitude

        Returns:
            Redis cache key
        """
        lat_rounded = round(latitude, self.coordinate_precision)
        lon_rounded = round(longitude, self.coordinate_precision)
        return f"poi_cache:{lat_rounded}:{lon_rounded}"

    async def get_cached_pois(
        self,
        latitude: float,
        longitude: float
    ) -> Optional[Dict[str, Any]]:
        """
        Get cached POI results for location.

        Args:
            latitude: Bakery latitude
            longitude: Bakery longitude

        Returns:
            Cached POI detection results or None if not cached
        """
        cache_key = self._generate_cache_key(latitude, longitude)

        try:
            cached_data = await self.redis.get(cache_key)
            if cached_data:
                logger.info(
                    "POI cache hit",
                    cache_key=cache_key,
                    location=(latitude, longitude)
                )
                return json.loads(cached_data)
            else:
                logger.debug(
                    "POI cache miss",
                    cache_key=cache_key,
                    location=(latitude, longitude)
                )
                return None
        except Exception as e:
            logger.warning(
                "Failed to retrieve POI cache",
                error=str(e),
                cache_key=cache_key
            )
            return None

    async def cache_poi_results(
        self,
        latitude: float,
        longitude: float,
        poi_data: Dict[str, Any]
    ) -> bool:
        """
        Cache POI detection results.

        Args:
            latitude: Bakery latitude
            longitude: Bakery longitude
            poi_data: Complete POI detection results

        Returns:
            True if cached successfully, False otherwise
        """
        cache_key = self._generate_cache_key(latitude, longitude)
        ttl_seconds = self.cache_ttl_days * 24 * 60 * 60

        try:
            await self.redis.setex(
                cache_key,
                ttl_seconds,
                json.dumps(poi_data)
            )
            logger.info(
                "POI results cached",
                cache_key=cache_key,
                ttl_days=self.cache_ttl_days,
                location=(latitude, longitude)
            )
            return True
        except Exception as e:
            logger.error(
                "Failed to cache POI results",
                error=str(e),
                cache_key=cache_key
            )
            return False

    async def invalidate_cache(
        self,
        latitude: float,
        longitude: float
    ) -> bool:
        """
        Invalidate cached POI results for location.

        Useful for manual refresh or data corrections.

        Args:
            latitude: Bakery latitude
            longitude: Bakery longitude

        Returns:
            True if invalidated successfully
        """
        cache_key = self._generate_cache_key(latitude, longitude)

        try:
            deleted = await self.redis.delete(cache_key)
            if deleted:
                logger.info(
                    "POI cache invalidated",
                    cache_key=cache_key,
                    location=(latitude, longitude)
                )
            return bool(deleted)
        except Exception as e:
            logger.error(
                "Failed to invalidate POI cache",
                error=str(e),
                cache_key=cache_key
            )
            return False

    async def get_cache_stats(self) -> Dict[str, Any]:
        """
        Get cache statistics.

        Returns:
            Dictionary with cache stats (key count, memory usage, etc.)
        """
        try:
            # Count POI cache keys
            pattern = "poi_cache:*"
            cursor = 0
            key_count = 0

            while True:
                cursor, keys = await self.redis.scan(
                    cursor=cursor,
                    match=pattern,
                    count=100
                )
                key_count += len(keys)
                if cursor == 0:
                    break

            return {
                "total_cached_locations": key_count,
                "cache_ttl_days": self.cache_ttl_days,
                "coordinate_precision": self.coordinate_precision
            }
        except Exception as e:
            logger.error("Failed to get cache stats", error=str(e))
            return {
                "error": str(e)
            }
