"""
Redis 缓存实现

提供缓存装饰器和缓存管理功能。
"""

import functools
import hashlib
import json
import logging
import pickle
from typing import Optional, Callable, Any, Union
from datetime import timedelta

import redis.asyncio as redis
from redis.asyncio.client import Redis

from src.config.settings import REDIS_URL, REDIS_ENABLED

logger = logging.getLogger(__name__)


class RedisCache:
    """
    Redis 缓存管理器

    提供统一的缓存接口。
    """

    _instance: Optional["RedisCache"] = None

    def __new__(cls, *args, **kwargs):
        """单例模式"""
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self, redis_url: Optional[str] = None):
        if hasattr(self, "_initialized"):
            return

        self._redis_url = redis_url or REDIS_URL
        self._redis: Optional[Redis] = None
        self._enabled = REDIS_ENABLED
        self._initialized = True

    async def connect(self):
        """建立 Redis 连接"""
        if not self._enabled:
            logger.info("Redis cache is disabled")
            return

        try:
            self._redis = await redis.from_url(
                self._redis_url,
                encoding="utf-8",
                decode_responses=False  # 支持二进制数据
            )
            await self._redis.ping()
            logger.info(f"Connected to Redis cache at {self._redis_url}")
        except Exception as e:
            logger.error(f"Failed to connect to Redis cache: {e}")
            self._enabled = False

    async def disconnect(self):
        """断开 Redis 连接"""
        if self._redis:
            await self._redis.close()
            self._redis = None
            logger.info("Disconnected from Redis cache")

    def _make_key(self, prefix: str, *args, **kwargs) -> str:
        """
        生成缓存键

        Args:
            prefix: 键前缀
            *args: 位置参数
            **kwargs: 关键字参数

        Returns:
            缓存键
        """
        key_parts = [prefix]

        for arg in args:
            key_parts.append(str(arg))

        for k, v in sorted(kwargs.items()):
            key_parts.append(f"{k}:{v}")

        raw_key = "|".join(key_parts)
        # 使用哈希确保键长度合理
        return f"cache:{prefix}:{hashlib.md5(raw_key.encode()).hexdigest()}"

    async def get(self, key: str) -> Optional[Any]:
        """
        获取缓存值

        Args:
            key: 缓存键

        Returns:
            缓存值或 None
        """
        if not self._enabled or not self._redis:
            return None

        try:
            data = await self._redis.get(key)
            if data:
                return pickle.loads(data)
            return None
        except Exception as e:
            logger.error(f"Cache get error: {e}")
            return None

    async def set(
        self,
        key: str,
        value: Any,
        ttl: Optional[Union[int, timedelta]] = None
    ) -> bool:
        """
        设置缓存值

        Args:
            key: 缓存键
            value: 缓存值
            ttl: 过期时间（秒或 timedelta）

        Returns:
            是否成功
        """
        if not self._enabled or not self._redis:
            return False

        try:
            expire = None
            if isinstance(ttl, timedelta):
                expire = int(ttl.total_seconds())
            elif isinstance(ttl, int):
                expire = ttl

            data = pickle.dumps(value)
            await self._redis.set(key, data, ex=expire)
            return True
        except Exception as e:
            logger.error(f"Cache set error: {e}")
            return False

    async def delete(self, key: str) -> bool:
        """
        删除缓存值

        Args:
            key: 缓存键

        Returns:
            是否成功
        """
        if not self._enabled or not self._redis:
            return False

        try:
            await self._redis.delete(key)
            return True
        except Exception as e:
            logger.error(f"Cache delete error: {e}")
            return False

    async def delete_pattern(self, pattern: str) -> int:
        """
        删除匹配模式的缓存

        Args:
            pattern: 匹配模式

        Returns:
            删除的键数量
        """
        if not self._enabled or not self._redis:
            return 0

        try:
            keys = await self._redis.keys(pattern)
            if keys:
                return await self._redis.delete(*keys)
            return 0
        except Exception as e:
            logger.error(f"Cache delete pattern error: {e}")
            return 0

    async def exists(self, key: str) -> bool:
        """
        检查键是否存在

        Args:
            key: 缓存键

        Returns:
            是否存在
        """
        if not self._enabled or not self._redis:
            return False

        try:
            return await self._redis.exists(key) > 0
        except Exception as e:
            logger.error(f"Cache exists error: {e}")
            return False

    async def ttl(self, key: str) -> int:
        """
        获取键的剩余生存时间

        Args:
            key: 缓存键

        Returns:
            剩余秒数，-1 表示永不过期，-2 表示不存在
        """
        if not self._enabled or not self._redis:
            return -2

        try:
            return await self._redis.ttl(key)
        except Exception as e:
            logger.error(f"Cache ttl error: {e}")
            return -2

    async def clear(self) -> bool:
        """
        清空所有缓存（谨慎使用）

        Returns:
            是否成功
        """
        if not self._enabled or not self._redis:
            return False

        try:
            await self._redis.flushdb()
            logger.warning("Cache cleared")
            return True
        except Exception as e:
            logger.error(f"Cache clear error: {e}")
            return False


# 全局缓存实例
cache = RedisCache()


def cache_result(
    prefix: str,
    ttl: Optional[Union[int, timedelta]] = 300,
    key_func: Optional[Callable] = None
):
    """
    缓存装饰器

    自动缓存函数结果。

    Args:
        prefix: 缓存键前缀
        ttl: 过期时间（秒）
        key_func: 自定义键生成函数

    Usage:
        @cache_result("user", ttl=300)
        async def get_user(user_id: str):
            return await fetch_user(user_id)
    """
    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        async def async_wrapper(*args, **kwargs):
            # 生成缓存键
            if key_func:
                cache_key = key_func(*args, **kwargs)
            else:
                cache_key = cache._make_key(prefix, *args, **kwargs)

            # 尝试从缓存获取
            result = await cache.get(cache_key)
            if result is not None:
                logger.debug(f"Cache hit: {cache_key}")
                return result

            # 执行函数
            result = await func(*args, **kwargs)

            # 存入缓存
            await cache.set(cache_key, result, ttl)
            logger.debug(f"Cache set: {cache_key}")

            return result

        @functools.wraps(func)
        def sync_wrapper(*args, **kwargs):
            # 同步函数不支持缓存
            return func(*args, **kwargs)

        return async_wrapper if asyncio.iscoroutinefunction(func) else sync_wrapper
    return decorator


def cache_with_ttl(ttl: int = 300):
    """
    简化版缓存装饰器

    使用函数名作为前缀。

    Args:
        ttl: 过期时间（秒）

    Usage:
        @cache_with_ttl(300)
        async def get_user(user_id: str):
            return await fetch_user(user_id)
    """
    def decorator(func: Callable) -> Callable:
        return cache_result(prefix=func.__name__, ttl=ttl)(func)
    return decorator


# 导入 asyncio 用于检查协程
import asyncio
