"""
Scene Slot Bundle Cache Service.
[AC-SCENE-SLOT-03] 场景槽位包缓存服务

职责：
1. 缓存场景槽位包配置，减少数据库查询
2. 支持缓存失效和刷新
3. 支持租户隔离
"""

import json
import logging
from dataclasses import dataclass, field
from datetime import datetime, timedelta
from typing import Any

logger = logging.getLogger(__name__)

try:
    import redis.asyncio as redis
    REDIS_AVAILABLE = True
except ImportError:
    REDIS_AVAILABLE = False


@dataclass
class CachedSceneSlotBundle:
    """缓存的场景槽位包"""
    scene_key: str
    scene_name: str
    description: str | None
    required_slots: list[str]
    optional_slots: list[str]
    slot_priority: list[str] | None
    completion_threshold: float
    ask_back_order: str
    status: str
    version: int
    cached_at: datetime = field(default_factory=datetime.utcnow)
    
    def to_dict(self) -> dict[str, Any]:
        return {
            "scene_key": self.scene_key,
            "scene_name": self.scene_name,
            "description": self.description,
            "required_slots": self.required_slots,
            "optional_slots": self.optional_slots,
            "slot_priority": self.slot_priority,
            "completion_threshold": self.completion_threshold,
            "ask_back_order": self.ask_back_order,
            "status": self.status,
            "version": self.version,
            "cached_at": self.cached_at.isoformat(),
        }
    
    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> "CachedSceneSlotBundle":
        return cls(
            scene_key=data["scene_key"],
            scene_name=data["scene_name"],
            description=data.get("description"),
            required_slots=data.get("required_slots", []),
            optional_slots=data.get("optional_slots", []),
            slot_priority=data.get("slot_priority"),
            completion_threshold=data.get("completion_threshold", 1.0),
            ask_back_order=data.get("ask_back_order", "priority"),
            status=data.get("status", "draft"),
            version=data.get("version", 1),
            cached_at=datetime.fromisoformat(data["cached_at"]) if data.get("cached_at") else datetime.utcnow(),
        )


class SceneSlotBundleCache:
    """
    [AC-SCENE-SLOT-03] 场景槽位包缓存
    
    使用 Redis 或内存缓存场景槽位包配置
    """
    
    CACHE_PREFIX = "scene_slot_bundle"
    CACHE_TTL_SECONDS = 300  # 5分钟缓存
    
    def __init__(
        self,
        redis_client: Any = None,
        ttl_seconds: int = 300,
    ):
        self._redis = redis_client
        self._ttl = ttl_seconds or self.CACHE_TTL_SECONDS
        self._memory_cache: dict[str, tuple[CachedSceneSlotBundle, datetime]] = {}
    
    def _get_cache_key(self, tenant_id: str, scene_key: str) -> str:
        """生成缓存键"""
        return f"{self.CACHE_PREFIX}:{tenant_id}:{scene_key}"
    
    async def get(
        self,
        tenant_id: str,
        scene_key: str,
    ) -> CachedSceneSlotBundle | None:
        """
        获取缓存的场景槽位包
        
        Args:
            tenant_id: 租户 ID
            scene_key: 场景标识
            
        Returns:
            缓存的场景槽位包或 None
        """
        cache_key = self._get_cache_key(tenant_id, scene_key)
        
        if self._redis and REDIS_AVAILABLE:
            try:
                cached_data = await self._redis.get(cache_key)
                if cached_data:
                    data = json.loads(cached_data)
                    return CachedSceneSlotBundle.from_dict(data)
            except Exception as e:
                logger.warning(
                    f"[AC-SCENE-SLOT-03] Redis get failed: {e}, falling back to memory cache"
                )
        
        if cache_key in self._memory_cache:
            cached_bundle, cached_at = self._memory_cache[cache_key]
            if datetime.utcnow() - cached_at < timedelta(seconds=self._ttl):
                return cached_bundle
            else:
                del self._memory_cache[cache_key]
        
        return None
    
    async def set(
        self,
        tenant_id: str,
        scene_key: str,
        bundle: CachedSceneSlotBundle,
    ) -> bool:
        """
        设置缓存
        
        Args:
            tenant_id: 租户 ID
            scene_key: 场景标识
            bundle: 要缓存的场景槽位包
            
        Returns:
            是否成功
        """
        cache_key = self._get_cache_key(tenant_id, scene_key)
        
        if self._redis and REDIS_AVAILABLE:
            try:
                await self._redis.setex(
                    cache_key,
                    self._ttl,
                    json.dumps(bundle.to_dict()),
                )
                return True
            except Exception as e:
                logger.warning(
                    f"[AC-SCENE-SLOT-03] Redis set failed: {e}, falling back to memory cache"
                )
        
        self._memory_cache[cache_key] = (bundle, datetime.utcnow())
        return True
    
    async def delete(
        self,
        tenant_id: str,
        scene_key: str,
    ) -> bool:
        """
        删除缓存
        
        Args:
            tenant_id: 租户 ID
            scene_key: 场景标识
            
        Returns:
            是否成功
        """
        cache_key = self._get_cache_key(tenant_id, scene_key)
        
        if self._redis and REDIS_AVAILABLE:
            try:
                await self._redis.delete(cache_key)
            except Exception as e:
                logger.warning(
                    f"[AC-SCENE-SLOT-03] Redis delete failed: {e}"
                )
        
        if cache_key in self._memory_cache:
            del self._memory_cache[cache_key]
        
        return True
    
    async def delete_by_tenant(self, tenant_id: str) -> bool:
        """
        删除租户下所有缓存
        
        Args:
            tenant_id: 租户 ID
            
        Returns:
            是否成功
        """
        pattern = f"{self.CACHE_PREFIX}:{tenant_id}:*"
        
        if self._redis and REDIS_AVAILABLE:
            try:
                keys = []
                async for key in self._redis.scan_iter(match=pattern):
                    keys.append(key)
                if keys:
                    await self._redis.delete(*keys)
            except Exception as e:
                logger.warning(
                    f"[AC-SCENE-SLOT-03] Redis delete by tenant failed: {e}"
                )
        
        keys_to_delete = [
            k for k in self._memory_cache
            if k.startswith(f"{self.CACHE_PREFIX}:{tenant_id}:")
        ]
        for key in keys_to_delete:
            del self._memory_cache[key]
        
        return True
    
    async def invalidate_on_update(
        self,
        tenant_id: str,
        scene_key: str,
    ) -> bool:
        """
        当场景槽位包更新时使缓存失效
        
        Args:
            tenant_id: 租户 ID
            scene_key: 场景标识
            
        Returns:
            是否成功
        """
        logger.info(
            f"[AC-SCENE-SLOT-03] Invalidating cache for scene slot bundle: "
            f"tenant={tenant_id}, scene={scene_key}"
        )
        return await self.delete(tenant_id, scene_key)


_scene_slot_bundle_cache: SceneSlotBundleCache | None = None


def get_scene_slot_bundle_cache() -> SceneSlotBundleCache:
    """获取场景槽位包缓存实例"""
    global _scene_slot_bundle_cache
    if _scene_slot_bundle_cache is None:
        _scene_slot_bundle_cache = SceneSlotBundleCache()
    return _scene_slot_bundle_cache


def init_scene_slot_bundle_cache(redis_client: Any = None, ttl_seconds: int = 300) -> None:
    """初始化场景槽位包缓存"""
    global _scene_slot_bundle_cache
    _scene_slot_bundle_cache = SceneSlotBundleCache(
        redis_client=redis_client,
        ttl_seconds=ttl_seconds,
    )
    logger.info(
        f"[AC-SCENE-SLOT-03] Scene slot bundle cache initialized: "
        f"ttl={ttl_seconds}s, redis={redis_client is not None}"
    )
