"""
Scene Slot Bundle Service.
[AC-SCENE-SLOT-01] 场景-槽位映射配置服务
[AC-SCENE-SLOT-03] 支持缓存失效

职责：
1. 场景槽位包的 CRUD 操作
2. 槽位引用有效性校验
3. 运行时场景槽位包查询
4. 缓存失效管理
"""

import logging
import uuid
from typing import Any

from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.entities import (
    SceneSlotBundle,
    SceneSlotBundleCreate,
    SceneSlotBundleStatus,
    SceneSlotBundleUpdate,
)
from app.services.cache.scene_slot_bundle_cache import get_scene_slot_bundle_cache

logger = logging.getLogger(__name__)


class SceneSlotBundleService:
    """
    [AC-SCENE-SLOT-01] 场景槽位包服务
    """
    
    def __init__(self, session: AsyncSession):
        self._session = session
    
    async def list_bundles(
        self,
        tenant_id: str,
        status: str | None = None,
    ) -> list[SceneSlotBundle]:
        """
        列出场景槽位包
        
        Args:
            tenant_id: 租户 ID
            status: 状态过滤（可选）
            
        Returns:
            场景槽位包列表
        """
        stmt = select(SceneSlotBundle).where(
            SceneSlotBundle.tenant_id == tenant_id
        )
        
        if status:
            stmt = stmt.where(SceneSlotBundle.status == status)
        
        stmt = stmt.order_by(SceneSlotBundle.created_at.desc())
        
        result = await self._session.execute(stmt)
        return list(result.scalars().all())
    
    async def get_bundle(
        self,
        tenant_id: str,
        bundle_id: str,
    ) -> SceneSlotBundle | None:
        """
        获取单个场景槽位包
        
        Args:
            tenant_id: 租户 ID
            bundle_id: 槽位包 ID
            
        Returns:
            场景槽位包或 None
        """
        try:
            stmt = select(SceneSlotBundle).where(
                SceneSlotBundle.tenant_id == tenant_id,
                SceneSlotBundle.id == uuid.UUID(bundle_id),
            )
            result = await self._session.execute(stmt)
            return result.scalar_one_or_none()
        except ValueError:
            return None
    
    async def get_bundle_by_scene_key(
        self,
        tenant_id: str,
        scene_key: str,
    ) -> SceneSlotBundle | None:
        """
        根据场景标识获取槽位包
        
        Args:
            tenant_id: 租户 ID
            scene_key: 场景标识
            
        Returns:
            场景槽位包或 None
        """
        stmt = select(SceneSlotBundle).where(
            SceneSlotBundle.tenant_id == tenant_id,
            SceneSlotBundle.scene_key == scene_key,
        )
        result = await self._session.execute(stmt)
        return result.scalar_one_or_none()
    
    async def get_active_bundle_by_scene(
        self,
        tenant_id: str,
        scene_key: str,
    ) -> SceneSlotBundle | None:
        """
        获取活跃状态的场景槽位包（运行时使用）
        
        Args:
            tenant_id: 租户 ID
            scene_key: 场景标识
            
        Returns:
            活跃状态的场景槽位包或 None
        """
        stmt = select(SceneSlotBundle).where(
            SceneSlotBundle.tenant_id == tenant_id,
            SceneSlotBundle.scene_key == scene_key,
            SceneSlotBundle.status == SceneSlotBundleStatus.ACTIVE.value,
        )
        result = await self._session.execute(stmt)
        return result.scalar_one_or_none()
    
    async def create_bundle(
        self,
        tenant_id: str,
        bundle_create: SceneSlotBundleCreate,
    ) -> SceneSlotBundle:
        """
        创建场景槽位包
        
        Args:
            tenant_id: 租户 ID
            bundle_create: 创建请求数据
            
        Returns:
            创建的场景槽位包
            
        Raises:
            ValueError: 校验失败
        """
        validation_errors = await self._validate_bundle_data(
            tenant_id=tenant_id,
            required_slots=bundle_create.required_slots,
            optional_slots=bundle_create.optional_slots,
            slot_priority=bundle_create.slot_priority,
        )
        
        if validation_errors:
            raise ValueError("; ".join(validation_errors))
        
        existing = await self.get_bundle_by_scene_key(
            tenant_id=tenant_id,
            scene_key=bundle_create.scene_key,
        )
        
        if existing:
            raise ValueError(f"场景标识 '{bundle_create.scene_key}' 已存在")
        
        bundle = SceneSlotBundle(
            tenant_id=tenant_id,
            scene_key=bundle_create.scene_key,
            scene_name=bundle_create.scene_name,
            description=bundle_create.description,
            required_slots=bundle_create.required_slots or [],
            optional_slots=bundle_create.optional_slots or [],
            slot_priority=bundle_create.slot_priority,
            completion_threshold=bundle_create.completion_threshold,
            ask_back_order=bundle_create.ask_back_order,
            status=bundle_create.status,
        )
        
        self._session.add(bundle)
        await self._session.flush()
        
        logger.info(
            f"[AC-SCENE-SLOT-01] Created scene slot bundle: "
            f"tenant={tenant_id}, scene_key={bundle.scene_key}, "
            f"required={len(bundle.required_slots)}, optional={len(bundle.optional_slots)}"
        )
        
        return bundle
    
    async def update_bundle(
        self,
        tenant_id: str,
        bundle_id: str,
        bundle_update: SceneSlotBundleUpdate,
    ) -> SceneSlotBundle | None:
        """
        更新场景槽位包
        
        Args:
            tenant_id: 租户 ID
            bundle_id: 槽位包 ID
            bundle_update: 更新请求数据
            
        Returns:
            更新后的场景槽位包或 None
            
        Raises:
            ValueError: 校验失败
        """
        bundle = await self.get_bundle(tenant_id, bundle_id)
        
        if not bundle:
            return None
        
        required_slots = bundle_update.required_slots if bundle_update.required_slots is not None else bundle.required_slots
        optional_slots = bundle_update.optional_slots if bundle_update.optional_slots is not None else bundle.optional_slots
        slot_priority = bundle_update.slot_priority if bundle_update.slot_priority is not None else bundle.slot_priority
        
        validation_errors = await self._validate_bundle_data(
            tenant_id=tenant_id,
            required_slots=required_slots,
            optional_slots=optional_slots,
            slot_priority=slot_priority,
        )
        
        if validation_errors:
            raise ValueError("; ".join(validation_errors))
        
        if bundle_update.scene_name is not None:
            bundle.scene_name = bundle_update.scene_name
        if bundle_update.description is not None:
            bundle.description = bundle_update.description
        if bundle_update.required_slots is not None:
            bundle.required_slots = bundle_update.required_slots
        if bundle_update.optional_slots is not None:
            bundle.optional_slots = bundle_update.optional_slots
        if bundle_update.slot_priority is not None:
            bundle.slot_priority = bundle_update.slot_priority
        if bundle_update.completion_threshold is not None:
            bundle.completion_threshold = bundle_update.completion_threshold
        if bundle_update.ask_back_order is not None:
            bundle.ask_back_order = bundle_update.ask_back_order
        if bundle_update.status is not None:
            bundle.status = bundle_update.status
        
        bundle.version += 1
        
        await self._session.flush()
        
        # [AC-SCENE-SLOT-03] 使缓存失效
        await self._invalidate_cache(tenant_id, bundle.scene_key)
        
        logger.info(
            f"[AC-SCENE-SLOT-01] Updated scene slot bundle: "
            f"tenant={tenant_id}, scene_key={bundle.scene_key}, version={bundle.version}"
        )
        
        return bundle
    
    async def delete_bundle(
        self,
        tenant_id: str,
        bundle_id: str,
    ) -> bool:
        """
        删除场景槽位包
        
        Args:
            tenant_id: 租户 ID
            bundle_id: 槽位包 ID
            
        Returns:
            是否成功删除
        """
        bundle = await self.get_bundle(tenant_id, bundle_id)
        
        if not bundle:
            return False
        
        scene_key = bundle.scene_key
        
        await self._session.delete(bundle)
        await self._session.flush()
        
        # [AC-SCENE-SLOT-03] 使缓存失效
        await self._invalidate_cache(tenant_id, scene_key)
        
        logger.info(
            f"[AC-SCENE-SLOT-01] Deleted scene slot bundle: "
            f"tenant={tenant_id}, scene_key={scene_key}"
        )
        
        return True
    
    async def _validate_bundle_data(
        self,
        tenant_id: str,
        required_slots: list[str],
        optional_slots: list[str],
        slot_priority: list[str] | None,
    ) -> list[str]:
        """
        校验槽位包数据
        
        Args:
            tenant_id: 租户 ID
            required_slots: 必填槽位列表
            optional_slots: 可选槽位列表
            slot_priority: 优先级列表
            
        Returns:
            错误信息列表（空列表表示校验通过）
        """
        errors = []
        
        required_set = set(required_slots or [])
        optional_set = set(optional_slots or [])
        
        overlap = required_set & optional_set
        if overlap:
            errors.append(f"必填和可选槽位存在交叉: {list(overlap)}")
        
        if slot_priority:
            priority_set = set(slot_priority)
            all_slots = required_set | optional_set
            
            unknown_in_priority = priority_set - all_slots
            if unknown_in_priority:
                errors.append(f"优先级列表包含未定义的槽位: {list(unknown_in_priority)}")
        
        valid_slots = await self._validate_slot_keys(
            tenant_id=tenant_id,
            slot_keys=list(required_set | optional_set),
        )
        
        invalid_slots = (required_set | optional_set) - valid_slots
        if invalid_slots:
            errors.append(f"以下槽位不存在: {list(invalid_slots)}")
        
        return errors
    
    async def _validate_slot_keys(
        self,
        tenant_id: str,
        slot_keys: list[str],
    ) -> set[str]:
        """
        校验槽位键名是否存在
        
        Args:
            tenant_id: 租户 ID
            slot_keys: 待校验的槽位键名列表
            
        Returns:
            有效的槽位键名集合
        """
        if not slot_keys:
            return set()
        
        from app.services.slot_definition_service import SlotDefinitionService
        
        slot_service = SlotDefinitionService(self._session)
        all_slots = await slot_service.list_slot_definitions(tenant_id)
        
        valid_keys = {slot.slot_key for slot in all_slots}
        
        return set(slot_keys) & valid_keys
    
    async def get_bundle_with_slot_details(
        self,
        tenant_id: str,
        bundle_id: str,
    ) -> dict[str, Any] | None:
        """
        获取场景槽位包及其槽位详情
        
        Args:
            tenant_id: 租户 ID
            bundle_id: 槽位包 ID
            
        Returns:
            包含槽位详情的字典或 None
        """
        bundle = await self.get_bundle(tenant_id, bundle_id)
        
        if not bundle:
            return None
        
        from app.services.slot_definition_service import SlotDefinitionService
        
        slot_service = SlotDefinitionService(self._session)
        all_slots = await slot_service.list_slot_definitions(tenant_id)
        
        slot_map = {slot.slot_key: slot for slot in all_slots}
        
        required_slot_details = []
        for slot_key in bundle.required_slots:
            if slot_key in slot_map:
                slot = slot_map[slot_key]
                required_slot_details.append({
                    "slot_key": slot.slot_key,
                    "type": slot.type,
                    "required": slot.required,
                    "ask_back_prompt": slot.ask_back_prompt,
                    "linked_field_id": str(slot.linked_field_id) if slot.linked_field_id else None,
                })
        
        optional_slot_details = []
        for slot_key in bundle.optional_slots:
            if slot_key in slot_map:
                slot = slot_map[slot_key]
                optional_slot_details.append({
                    "slot_key": slot.slot_key,
                    "type": slot.type,
                    "required": slot.required,
                    "ask_back_prompt": slot.ask_back_prompt,
                    "linked_field_id": str(slot.linked_field_id) if slot.linked_field_id else None,
                })
        
        return {
            "id": str(bundle.id),
            "tenant_id": str(bundle.tenant_id),
            "scene_key": bundle.scene_key,
            "scene_name": bundle.scene_name,
            "description": bundle.description,
            "required_slots": bundle.required_slots,
            "optional_slots": bundle.optional_slots,
            "required_slot_details": required_slot_details,
            "optional_slot_details": optional_slot_details,
            "slot_priority": bundle.slot_priority,
            "completion_threshold": bundle.completion_threshold,
            "ask_back_order": bundle.ask_back_order,
            "status": bundle.status,
            "version": bundle.version,
            "created_at": bundle.created_at.isoformat() if bundle.created_at else None,
            "updated_at": bundle.updated_at.isoformat() if bundle.updated_at else None,
        }
    
    async def _invalidate_cache(
        self,
        tenant_id: str,
        scene_key: str,
    ) -> None:
        """
        [AC-SCENE-SLOT-03] 使缓存失效
        
        Args:
            tenant_id: 租户 ID
            scene_key: 场景标识
        """
        try:
            cache = get_scene_slot_bundle_cache()
            await cache.invalidate_on_update(tenant_id, scene_key)
        except Exception as e:
            logger.warning(
                f"[AC-SCENE-SLOT-03] Failed to invalidate cache: {e}"
            )
