"""缓存服务：Redis → 内存回退（Redis 不可用时自动降级，无连接等待）"""

import json
import logging

logger = logging.getLogger(__name__)


class CacheService:
    """内存缓存实现（生产环境可替换为 Redis）"""

    def __init__(self):
        self._store: dict[str, dict] = {}
        self._redis_failed = False
        self._redis = None

    async def _get_redis(self):
        """尝试获取 Redis 连接，失败即降级（只尝试一次）"""
        if self._redis_failed:
            return None
        if self._redis is not None:
            return self._redis
        try:
            from redis.asyncio import Redis
            from app.config import settings
            r = Redis.from_url(settings.REDIS_URL, decode_responses=True, socket_connect_timeout=1)
            await r.ping()
            self._redis = r
            return r
        except Exception:
            self._redis_failed = True
            self._redis = None
            logger.info("Redis 不可用，使用内存缓存")
            return None

    async def get(self, key: str) -> dict | None:
        r = await self._get_redis()
        if r:
            try:
                val = await r.get(key)
                return json.loads(val) if val else None
            except Exception:
                logger.exception("Redis GET failed: %s", key)
                return None
        return self._store.get(key)

    async def set(self, key: str, value: dict, ttl: int = 300) -> bool:
        r = await self._get_redis()
        if r:
            try:
                await r.set(key, json.dumps(value, default=str), ex=ttl)
                return True
            except Exception:
                logger.exception("Redis SET failed: %s", key)
                return False
        self._store[key] = value
        return True

    async def delete(self, key: str):
        r = await self._get_redis()
        if r:
            import contextlib
            with contextlib.suppress(Exception):
                await r.delete(key)
        else:
            self._store.pop(key, None)

    async def pop(self, key: str) -> dict | None:
        r = await self._get_redis()
        if r:
            try:
                val = await r.get(key)
                if val is not None:
                    await r.delete(key)
                return json.loads(val) if val else None
            except Exception:
                logger.exception("Redis POP failed: %s", key)
                return None
        return self._store.pop(key, None)

    async def get_or_set(self, key: str, loader, ttl: int = 300) -> dict | None:
        """原子化 get-then-set-or-fallback：先查缓存，命中即返回；未命中调用 loader() 加载、写缓存后返回。"""
        cached = await self.get(key)
        if cached is not None:
            return cached
        value = await loader()
        if value is not None:
            await self.set(key, value, ttl=ttl)
        return value

    async def invalidate_user(self, user_id: str):
        await self.delete(f"user:{user_id}:profile")

    async def invalidate_tenant(self, tenant_id: str):
        await self.delete(f"tenant:{tenant_id}:plan")
        await self.delete(f"tenant:{tenant_id}:settings")


cache = CacheService()
