"""Redis caching service"""

import json
import logging
from datetime import timedelta
from typing import Optional, Any

import redis

from app.core.config import settings

logger = logging.getLogger(__name__)


# Lazy Redis client initialization
class _RedisClient:
    _client = None

    @classmethod
    def get_client(cls):
        if cls._client is None:
            # Parse REDIS_URL or use default
            redis_url = settings.REDIS_URL
            if redis_url.startswith("redis://"):
                cls._client = redis.from_url(redis_url, decode_responses=True)
            else:
                cls._client = redis.Redis(
                    host=settings.REDIS_SERVER,
                    port=settings.REDIS_PORT,
                    db=settings.REDIS_DB,
                    decode_responses=True,
                )
        return cls._client


class CacheService:
    """Redis caching service with JSON serialization"""

    def __init__(self):
        self.client = _RedisClient.get_client()

    def get(self, key: str) -> Optional[Any]:
        """Get value from cache"""
        try:
            value = self.client.get(key)
            if value:
                return json.loads(value)
            return None
        except Exception as e:
            logger.warning(f"Cache get error: {e}")
            return None

    def set(
        self,
        key: str,
        value: Any,
        expire_seconds: int = 300,
    ) -> bool:
        """Set value in cache with expiration"""
        try:
            serialized = json.dumps(value, default=str)
            return self.client.setex(key, expire_seconds, serialized)
        except Exception as e:
            logger.warning(f"Cache set error: {e}")
            return False

    def delete(self, key: str) -> bool:
        """Delete key from cache"""
        try:
            return self.client.delete(key) > 0
        except Exception as e:
            logger.warning(f"Cache delete error: {e}")
            return False

    def delete_pattern(self, pattern: str) -> int:
        """Delete all keys matching pattern"""
        try:
            keys = self.client.keys(pattern)
            if keys:
                return self.client.delete(*keys)
            return 0
        except Exception as e:
            logger.warning(f"Cache delete_pattern error: {e}")
            return 0

    def get_or_set(
        self,
        key: str,
        fallback: callable,
        expire_seconds: int = 300,
    ) -> Optional[Any]:
        """Get value from cache or set it using fallback"""
        value = self.get(key)
        if value is not None:
            return value

        value = fallback()
        if value is not None:
            self.set(key, value, expire_seconds)
        return value

    def invalidate_pattern(self, pattern: str) -> int:
        """Invalidate all keys matching pattern"""
        return self.delete_pattern(pattern)


cache = CacheService()


def cached(expire_seconds: int = 300, key_prefix: str = ""):
    """Decorator for caching function results"""

    def decorator(func):
        async def wrapper(*args, **kwargs):
            cache_key = f"{key_prefix}:{func.__name__}:{args}:{kwargs}"
            cache_key = cache_key.replace(":", "_").replace(" ", "")

            cached_value = cache.get(cache_key)
            if cached_value is not None:
                return cached_value

            result = await func(*args, **kwargs)
            cache.set(cache_key, result, expire_seconds)
            return result

        return wrapper

    return decorator
