"""
Batch Ask-Back Service.
批量追问服务 - 支持一次追问多个缺失槽位

[AC-MRS-SLOT-ASKBACK-01] 批量追问

职责：
1. 支持一次追问多个缺失槽位
2. 选择策略：必填优先、场景相关优先、最近未追问过优先
3. 输出形式：单条自然语言合并提问或分段提问
"""

import logging
import time
from dataclasses import dataclass, field
from typing import Any

from sqlalchemy.ext.asyncio import AsyncSession

from app.services.cache.slot_state_cache import get_slot_state_cache
from app.services.slot_definition_service import SlotDefinitionService

logger = logging.getLogger(__name__)


@dataclass
class AskBackSlot:
    """
    追问槽位信息
    
    Attributes:
        slot_key: 槽位键名
        label: 显示标签
        ask_back_prompt: 追问提示
        priority: 优先级（数值越大越优先）
        last_asked_at: 上次追问时间戳
        is_required: 是否必填
        scene_relevance: 场景相关度
    """
    slot_key: str
    label: str
    ask_back_prompt: str | None = None
    priority: int = 0
    last_asked_at: float | None = None
    is_required: bool = False
    scene_relevance: float = 0.0


@dataclass
class BatchAskBackConfig:
    """
    批量追问配置
    
    Attributes:
        max_ask_back_slots_per_turn: 每轮最多追问槽位数
        prefer_required: 是否优先追问必填槽位
        prefer_scene_relevant: 是否优先追问场景相关槽位
        avoid_recent_asked: 是否避免最近追问过的槽位
        recent_asked_threshold_seconds: 最近追问阈值（秒）
        merge_prompts: 是否合并追问提示
        merge_template: 合并模板
    """
    max_ask_back_slots_per_turn: int = 2
    prefer_required: bool = True
    prefer_scene_relevant: bool = True
    avoid_recent_asked: bool = True
    recent_asked_threshold_seconds: float = 60.0
    merge_prompts: bool = True
    merge_template: str = "为了更好地为您服务，请告诉我：{prompts}"


@dataclass
class BatchAskBackResult:
    """
    批量追问结果
    
    Attributes:
        selected_slots: 选中的追问槽位列表
        prompts: 追问提示列表
        merged_prompt: 合并后的追问提示
        ask_back_count: 追问数量
    """
    selected_slots: list[AskBackSlot] = field(default_factory=list)
    prompts: list[str] = field(default_factory=list)
    merged_prompt: str | None = None
    ask_back_count: int = 0
    
    def has_ask_back(self) -> bool:
        return self.ask_back_count > 0
    
    def get_prompt(self) -> str:
        """获取最终追问提示"""
        if self.merged_prompt:
            return self.merged_prompt
        if self.prompts:
            return self.prompts[0]
        return "请提供更多信息以便我更好地帮助您。"


class BatchAskBackService:
    """
    [AC-MRS-SLOT-ASKBACK-01] 批量追问服务
    
    支持一次追问多个缺失槽位，提高补全效率。
    """
    
    ASK_BACK_HISTORY_KEY_PREFIX = "slot_ask_back_history"
    ASK_BACK_HISTORY_TTL = 300
    
    def __init__(
        self,
        session: AsyncSession,
        tenant_id: str,
        session_id: str,
        config: BatchAskBackConfig | None = None,
    ):
        self._session = session
        self._tenant_id = tenant_id
        self._session_id = session_id
        self._config = config or BatchAskBackConfig()
        self._slot_def_service = SlotDefinitionService(session)
        self._cache = get_slot_state_cache()
    
    async def generate_batch_ask_back(
        self,
        missing_slots: list[dict[str, str]],
        current_scene: str | None = None,
    ) -> BatchAskBackResult:
        """
        生成批量追问
        
        Args:
            missing_slots: 缺失槽位列表
            current_scene: 当前场景
            
        Returns:
            BatchAskBackResult: 批量追问结果
        """
        if not missing_slots:
            return BatchAskBackResult()
        
        ask_back_slots = await self._prepare_ask_back_slots(missing_slots, current_scene)
        
        selected_slots = self._select_slots_for_ask_back(ask_back_slots)
        
        asked_history = await self._get_asked_history()
        selected_slots = self._filter_recently_asked(selected_slots, asked_history)
        
        if not selected_slots:
            selected_slots = ask_back_slots[:self._config.max_ask_back_slots_per_turn]
        
        prompts = self._generate_prompts(selected_slots)
        merged_prompt = self._merge_prompts(prompts) if self._config.merge_prompts else None
        
        await self._record_ask_back_history([s.slot_key for s in selected_slots])
        
        return BatchAskBackResult(
            selected_slots=selected_slots,
            prompts=prompts,
            merged_prompt=merged_prompt,
            ask_back_count=len(selected_slots),
        )
    
    async def _prepare_ask_back_slots(
        self,
        missing_slots: list[dict[str, str]],
        current_scene: str | None,
    ) -> list[AskBackSlot]:
        """准备追问槽位列表"""
        ask_back_slots = []
        
        for missing in missing_slots:
            slot_key = missing.get("slot_key", "")
            label = missing.get("label", slot_key)
            ask_back_prompt = missing.get("ask_back_prompt")
            field_key = missing.get("field_key")
            
            slot_def = await self._slot_def_service.get_slot_definition_by_key(
                self._tenant_id, slot_key
            )
            
            is_required = False
            scene_relevance = 0.0
            
            if slot_def:
                is_required = slot_def.required
                if not ask_back_prompt:
                    ask_back_prompt = slot_def.ask_back_prompt
                
                if current_scene and slot_def.scene_scope:
                    if current_scene in slot_def.scene_scope:
                        scene_relevance = 1.0
            
            priority = self._calculate_priority(is_required, scene_relevance)
            
            ask_back_slots.append(AskBackSlot(
                slot_key=slot_key,
                label=label,
                ask_back_prompt=ask_back_prompt,
                priority=priority,
                is_required=is_required,
                scene_relevance=scene_relevance,
            ))
        
        return ask_back_slots
    
    def _calculate_priority(self, is_required: bool, scene_relevance: float) -> int:
        """计算槽位优先级"""
        priority = 0
        
        if self._config.prefer_required and is_required:
            priority += 100
        
        if self._config.prefer_scene_relevant:
            priority += int(scene_relevance * 50)
        
        return priority
    
    def _select_slots_for_ask_back(
        self,
        ask_back_slots: list[AskBackSlot],
    ) -> list[AskBackSlot]:
        """选择要追问的槽位"""
        sorted_slots = sorted(
            ask_back_slots,
            key=lambda s: s.priority,
            reverse=True,
        )
        
        return sorted_slots[:self._config.max_ask_back_slots_per_turn]
    
    async def _get_asked_history(self) -> dict[str, float]:
        """获取最近追问历史"""
        history_key = f"{self.ASK_BACK_HISTORY_KEY_PREFIX}:{self._tenant_id}:{self._session_id}"
        
        try:
            client = await self._cache._get_client()
            if client is None:
                return {}
            
            import json
            data = await client.get(history_key)
            if data:
                return json.loads(data)
        except Exception as e:
            logger.warning(f"[BatchAskBack] Failed to get asked history: {e}")
        
        return {}
    
    async def _record_ask_back_history(self, slot_keys: list[str]) -> None:
        """记录追问历史"""
        history_key = f"{self.ASK_BACK_HISTORY_KEY_PREFIX}:{self._tenant_id}:{self._session_id}"
        
        try:
            client = await self._cache._get_client()
            if client is None:
                return
            
            history = await self._get_asked_history()
            current_time = time.time()
            
            for slot_key in slot_keys:
                history[slot_key] = current_time
            
            import json
            await client.setex(
                history_key,
                self.ASK_BACK_HISTORY_TTL,
                json.dumps(history),
            )
        except Exception as e:
            logger.warning(f"[BatchAskBack] Failed to record asked history: {e}")
    
    def _filter_recently_asked(
        self,
        slots: list[AskBackSlot],
        asked_history: dict[str, float],
    ) -> list[AskBackSlot]:
        """过滤最近追问过的槽位"""
        if not self._config.avoid_recent_asked:
            return slots
        
        current_time = time.time()
        threshold = self._config.recent_asked_threshold_seconds
        
        return [
            slot for slot in slots
            if slot.slot_key not in asked_history or
            current_time - asked_history[slot.slot_key] > threshold
        ]
    
    def _generate_prompts(self, slots: list[AskBackSlot]) -> list[str]:
        """生成追问提示列表"""
        prompts = []
        
        for slot in slots:
            if slot.ask_back_prompt:
                prompts.append(slot.ask_back_prompt)
            else:
                prompts.append(f"请告诉我您的{slot.label}")
        
        return prompts
    
    def _merge_prompts(self, prompts: list[str]) -> str | None:
        """合并追问提示"""
        if not prompts:
            return None
        
        if len(prompts) == 1:
            return prompts[0]
        
        if len(prompts) == 2:
            return f"{prompts[0]}，以及{prompts[1]}"
        
        all_but_last = "、".join(prompts[:-1])
        return f"{all_but_last}，以及{prompts[-1]}"


def create_batch_ask_back_service(
    session: AsyncSession,
    tenant_id: str,
    session_id: str,
    config: BatchAskBackConfig | None = None,
) -> BatchAskBackService:
    """
    创建批量追问服务实例
    
    Args:
        session: 数据库会话
        tenant_id: 租户 ID
        session_id: 会话 ID
        config: 配置
        
    Returns:
        BatchAskBackService: 批量追问服务实例
    """
    return BatchAskBackService(
        session=session,
        tenant_id=tenant_id,
        session_id=session_id,
        config=config,
    )
