"""多 Agent 配置管理服务"""
import uuid
from typing import Optional, List, Tuple, Any
from sqlalchemy.orm import Session
from sqlalchemy import select, desc

from app.models import MultiAgentConfig, App, AgentConfig
from app.schemas.multi_agent_schema import (
    MultiAgentConfigCreate,
    MultiAgentConfigUpdate,
    MultiAgentRunRequest
)
from app.services.multi_agent_orchestrator import MultiAgentOrchestrator
from app.core.exceptions import ResourceNotFoundException, BusinessException
from app.core.error_codes import BizCode
from app.core.logging_config import get_business_logger
from app.models import AppRelease

logger = get_business_logger()


def convert_uuids_to_str(obj: Any) -> Any:
    """递归转换对象中的所有 UUID 为字符串
    
    Args:
        obj: 要转换的对象（dict, list, UUID 等）
        
    Returns:
        转换后的对象
    """
    if isinstance(obj, uuid.UUID):
        return str(obj)
    elif isinstance(obj, dict):
        return {k: convert_uuids_to_str(v) for k, v in obj.items()}
    elif isinstance(obj, list):
        return [convert_uuids_to_str(item) for item in obj]
    else:
        return obj


class MultiAgentService:
    """多 Agent 配置管理服务"""
    
    def __init__(self, db: Session):
        self.db = db
    
    def create_config(
        self,
        app_id: uuid.UUID,
        data: MultiAgentConfigCreate,
        created_by: uuid.UUID
    ) -> MultiAgentConfig:
        """创建多 Agent 配置
        
        Args:
            app_id: 应用 ID
            data: 配置数据
            created_by: 创建者 ID
            
        Returns:
            多 Agent 配置
        """
        # 1. 验证应用存在
        app = self.db.get(App, app_id)
        if not app:
            raise ResourceNotFoundException("应用", str(app_id))
        
        # 2. 检查是否已有有效配置
        existing = self.db.scalars(
            select(MultiAgentConfig)
            .where(
                MultiAgentConfig.app_id == app_id,
                MultiAgentConfig.is_active == True
            )
            .order_by(MultiAgentConfig.updated_at.desc())
        ).first()
        if existing:
            raise BusinessException("应用已有多 Agent 配置", BizCode.DUPLICATE_RESOURCE)
        
        # 3. 验证主 Agent 存在
        master_agent = self.db.get(AgentConfig, data.master_agent_id)
        if not master_agent:
            raise ResourceNotFoundException("主 Agent", str(data.master_agent_id))
        
        # 4. 验证子 Agent 存在
        for sub_agent in data.sub_agents:
            agent = self.db.get(AgentConfig, sub_agent.agent_id)
            if not agent:
                raise ResourceNotFoundException("子 Agent", str(sub_agent.agent_id))
        
        # 5. 创建配置（转换 UUID 为字符串以支持 JSON 序列化）
        sub_agents_data = [convert_uuids_to_str(sub_agent.model_dump()) for sub_agent in data.sub_agents]
        routing_rules_data = [convert_uuids_to_str(rule.model_dump()) for rule in data.routing_rules] if data.routing_rules else None
        
        # 处理 execution_config（可能是 None、字典或 Pydantic 模型）
        if data.execution_config is None:
            execution_config_data = {}
        elif isinstance(data.execution_config, dict):
            execution_config_data = convert_uuids_to_str(data.execution_config)
        else:
            execution_config_data = convert_uuids_to_str(data.execution_config.model_dump())
        
        config = MultiAgentConfig(
            app_id=app_id,
            master_agent_id=data.master_agent_id,
            master_agent_name=data.master_agent_name,
            orchestration_mode=data.orchestration_mode,
            sub_agents=sub_agents_data,
            routing_rules=routing_rules_data,
            execution_config=execution_config_data,
            aggregation_strategy=data.aggregation_strategy
        )
        
        self.db.add(config)
        self.db.commit()
        self.db.refresh(config)
        
        logger.info(
            f"创建多 Agent 配置成功",
            extra={
                "config_id": str(config.id),
                "app_id": str(app_id),
                "mode": data.orchestration_mode,
                "sub_agent_count": len(data.sub_agents)
            }
        )
        
        return config
    
    def get_config(self, app_id: uuid.UUID) -> Optional[MultiAgentConfig]:
        """获取多 Agent 配置
        
        Args:
            app_id: 应用 ID
            
        Returns:
            多 Agent 配置，如果不存在返回 None
        """
        return self.db.scalars(
            select(MultiAgentConfig)
            .where(
                MultiAgentConfig.app_id == app_id,
                MultiAgentConfig.is_active == True
            )
            .order_by(MultiAgentConfig.updated_at.desc())
        ).first()
    
    def get_multi_agent_configs(self, app_id: uuid.UUID) -> Optional[dict]:
        """通过 app_id 获取最新有效的多智能体配置，并将 agent_id 转换为 app_id
        
        Args:
            app_id: 应用 ID
            
        Returns:
            转换后的配置字典，如果不存在返回 None
        """
        config = self.get_config(app_id)
        if not config:
            return None
        
        # 转换 master_agent_id (release_id) 为 app_id
        master_release = self.db.get(AppRelease, config.master_agent_id)
        master_app_id = master_release.app_id if master_release else config.master_agent_id
        
        # 转换 sub_agents 中的 agent_id (release_id) 为 app_id
        converted_sub_agents = []
        for sub_agent in config.sub_agents:
            sub_agent_copy = sub_agent.copy()
            release_id = sub_agent.get("agent_id")
            if release_id:
                try:
                    release_id_uuid = uuid.UUID(release_id) if isinstance(release_id, str) else release_id
                    sub_release = self.db.get(AppRelease, release_id_uuid)
                    if sub_release:
                        sub_agent_copy["agent_id"] = str(sub_release.app_id)
                except Exception as e:
                    logger.warning(f"转换 sub_agent agent_id 失败: {release_id}, 错误: {str(e)}")
            converted_sub_agents.append(sub_agent_copy)
        
        # 构建返回的配置字典
        return {
            "id": config.id,
            "app_id": config.app_id,
            "master_agent_id": master_app_id,
            "master_agent_name": config.master_agent_name,
            "orchestration_mode": config.orchestration_mode,
            "sub_agents": converted_sub_agents,
            "routing_rules": config.routing_rules,
            "execution_config": config.execution_config,
            "aggregation_strategy": config.aggregation_strategy,
            "is_active": config.is_active,
            "created_at": config.created_at,
            "updated_at": config.updated_at
        }
    
    def get_published_config_by_agent_id(self, agent_id: uuid.UUID) -> Optional[dict]:
        """通过 agent_id 获取当前发布版本的完整配置
        
        Args:
            agent_id: Agent 配置 ID
            
        Returns:
            当前发布版本的配置字典，如果没有发布版本则返回 None
        """
        from app.models import AppRelease
        
        # 查询 Agent 配置
        agent_config = self.db.get(AgentConfig, agent_id)
        if not agent_config:
            logger.warning(f"Agent 配置不存在: {agent_id}")
            return None
                
        # 获取关联的应用
        app = self.db.get(App, agent_config.app_id)
        if not app or not app.current_release_id:
            logger.warning(f"应用未发布或不存在: app_id={agent_config.app_id}")
            return None
        
        # 获取当前发布版本
        release = self.db.get(AppRelease, app.current_release_id)
        if not release:
            logger.warning(f"发布版本不存在: release_id={app.current_release_id}")
            return None
        
        # 从发布版本的 config 中获取完整配置
        # config 是一个 JSON 对象，包含了发布时的配置快照
        config_data = release.config
        if config_data and isinstance(config_data, dict):
            return config_data
        
        return None

    def get_published_by_agent_id(self, agent_id: uuid.UUID) -> Optional[AppRelease]:
        """通过 agent_id 获取当前发布版本的完整配置
        
        Args:
            agent_id: Agent 配置 ID
            
        Returns:
            当前发布版本的配置字典，如果没有发布版本则返回 None
        """
                
        # 获取关联的应用
        app = self.db.get(App, agent_id)
        if not app or not app.current_release_id:
            logger.warning(f"应用未发布或不存在: app_id={agent_id}")
            return None
        
        # 获取当前发布版本
        release = self.db.get(AppRelease, app.current_release_id)
        if not release:
            logger.warning(f"发布版本不存在: release_id={app.current_release_id}")
            return None
        return release
    
    def update_config(
        self,
        app_id: uuid.UUID,
        data: MultiAgentConfigUpdate
    ) -> MultiAgentConfig:
        """更新多 Agent 配置
        
        Args:
            app_id: 应用 ID
            data: 更新数据
            
        Returns:
            更新后的配置
        """
        config = self.get_config(app_id)
        if not config:
            # 1. 验证应用存在
            app = self.db.get(App, app_id)
            if not app:
                raise ResourceNotFoundException("应用", str(app_id))
            
            # 2. 验证主 Agent 存在并获取发布版本 ID
            master_app_release = self.get_published_by_agent_id(data.master_agent_id)
            if not master_app_release:
                raise ResourceNotFoundException("主 Agent 未发布或不存在", str(data.master_agent_id))
            
            # 使用发布版本 ID
            data.master_agent_id = master_app_release.id

            # 3. 验证子 Agent 存在并获取发布版本 ID
            for sub_agent in data.sub_agents:
                agent_app_release = self.get_published_by_agent_id(sub_agent.agent_id)
                if not agent_app_release:
                    raise ResourceNotFoundException("子 Agent 未发布或不存在", str(sub_agent.agent_id))
                
                # 使用发布版本 ID
                sub_agent.agent_id = agent_app_release.id
                
            
            # 5. 创建配置（转换 UUID 为字符串以支持 JSON 序列化）
            sub_agents_data = [convert_uuids_to_str(sub_agent.model_dump()) for sub_agent in data.sub_agents]
            # routing_rules_data = [convert_uuids_to_str(rule.model_dump()) for rule in data.routing_rules] if data.routing_rules else None
            
            # 处理 execution_config（可能是 None、字典或 Pydantic 模型）
            if data.execution_config is None:
                execution_config_data = {}
            elif isinstance(data.execution_config, dict):
                execution_config_data = convert_uuids_to_str(data.execution_config)
            else:
                execution_config_data = convert_uuids_to_str(data.execution_config.model_dump())
            
            config = MultiAgentConfig(
                app_id=app_id,
                master_agent_id=data.master_agent_id,
                master_agent_name=data.master_agent_name,
                orchestration_mode=data.orchestration_mode,
                sub_agents=sub_agents_data,
                # routing_rules=routing_rules_data,
                execution_config=execution_config_data,
                aggregation_strategy=data.aggregation_strategy
            )
            
            self.db.add(config)
            self.db.commit()
            self.db.refresh(config)
            
            logger.info(
                f"创建多 Agent 配置成功",
                extra={
                    "config_id": str(config.id),
                    "app_id": str(app_id),
                    "mode": data.orchestration_mode,
                    "sub_agent_count": len(data.sub_agents)
                }
            )
            return config
            # raise ResourceNotFoundException("多 Agent 配置", str(app_id))
        
        # 更新字段
        if data.master_agent_id is not None:
            # 验证主 Agent 存在
            # 3. 验证主 Agent 存在并获取发布配置
            master_app_release = self.get_published_by_agent_id(data.master_agent_id)
            if not master_app_release:
                raise ResourceNotFoundException("主 Agent 未发布或", str(data.master_agent_id))

            config.master_agent_id = master_app_release.id
        
        if data.master_agent_name is not None:
            config.master_agent_name = data.master_agent_name
        
        if data.orchestration_mode is not None:
            config.orchestration_mode = data.orchestration_mode
        
        if data.sub_agents is not None:
            # 验证子 Agent 存在，并获取其发布的 config_id
            updated_sub_agents = []
            for sub_agent in data.sub_agents:
                agent_app_release = self.get_published_by_agent_id(sub_agent.agent_id)
                if not agent_app_release:
                    raise ResourceNotFoundException("子 Agent 未发布或", str(sub_agent.agent_id))
                sub_agent.agent_id = agent_app_release.id               
                sub_agent_dict = convert_uuids_to_str(sub_agent.model_dump())
                updated_sub_agents.append(sub_agent_dict)
            
            config.sub_agents = updated_sub_agents
        
        # if data.routing_rules is not None:
        #     config.routing_rules = [convert_uuids_to_str(rule.model_dump()) for rule in data.routing_rules] if data.routing_rules else None
        
        if data.execution_config is None:
            execution_config_data = {}
        elif isinstance(data.execution_config, dict):
            execution_config_data = convert_uuids_to_str(data.execution_config)
        else:
            execution_config_data = convert_uuids_to_str(data.execution_config.model_dump())

        if data.aggregation_strategy is not None:
            config.aggregation_strategy = data.aggregation_strategy
        
        if data.is_active is not None:
            config.is_active = data.is_active
        
        self.db.commit()
        self.db.refresh(config)
        
        logger.info(
            f"更新多 Agent 配置成功",
            extra={
                "config_id": str(config.id),
                "app_id": str(app_id)
            }
        )
        
        return config
    
    def delete_config(self, app_id: uuid.UUID) -> None:
        """删除多 Agent 配置
        
        Args:
            app_id: 应用 ID
        """
        config = self.get_config(app_id)
        if not config:
            raise ResourceNotFoundException("多 Agent 配置", str(app_id))
        
        self.db.delete(config)
        self.db.commit()
        
        logger.info(
            f"删除多 Agent 配置成功",
            extra={
                "config_id": str(config.id),
                "app_id": str(app_id)
            }
        )
    
    async def run(
        self,
        app_id: uuid.UUID,
        request: MultiAgentRunRequest
    ) -> dict:
        """运行多 Agent 任务
        
        Args:
            app_id: 应用 ID
            request: 运行请求
            
        Returns:
            执行结果
        """
        # 1. 获取配置
        config = self.get_config(app_id)
        if not config:
            raise ResourceNotFoundException("多 Agent 配置", str(app_id))
        
        if not config.is_active:
            raise BusinessException("多 Agent 配置已禁用", BizCode.RESOURCE_DISABLED)
        
        # 2. 创建编排器
        orchestrator = MultiAgentOrchestrator(self.db, config)
        
        # 3. 执行任务
        result = await orchestrator.execute(
            message=request.message,
            conversation_id=request.conversation_id,
            user_id=request.user_id,
            variables=request.variables,
            use_llm_routing=getattr(request, 'use_llm_routing', True),  # 默认启用 LLM 路由
            web_search=getattr(request, 'web_search', False),  # 网络搜索参数
            memory=getattr(request, 'memory', True)  # 记忆功能参数
        )
        
        return result
    
    async def run_stream(
        self,
        app_id: uuid.UUID,
        request: MultiAgentRunRequest,
        storage_type :str,
        user_rag_memory_id :str
    ):
        """运行多 Agent 任务（流式返回）
        
        Args:
            app_id: 应用 ID
            request: 运行请求
            
        Yields:
            SSE 格式的事件流
        """
        # 1. 获取配置
        config = self.get_config(app_id)
        if not config:
            raise ResourceNotFoundException("多 Agent 配置", str(app_id))
        
        if not config.is_active:
            raise BusinessException("多 Agent 配置已禁用", BizCode.RESOURCE_DISABLED)
        
        # 2. 创建编排器
        orchestrator = MultiAgentOrchestrator(self.db, config)
        
        # 3. 流式执行任务
        async for event in orchestrator.execute_stream(
            message=request.message,
            conversation_id=request.conversation_id,
            user_id=request.user_id,
            variables=request.variables,
            use_llm_routing=getattr(request, 'use_llm_routing', True),
            web_search=getattr(request, 'web_search', False),  # 网络搜索参数
            memory=getattr(request, 'memory', True) , # 记忆功能参数
            storage_type=storage_type,
            user_rag_memory_id=user_rag_memory_id
        ):
            yield event
    
    def add_sub_agent(
        self,
        app_id: uuid.UUID,
        agent_id: uuid.UUID,
        name: str,
        role: Optional[str] = None,
        priority: int = 1,
        capabilities: Optional[List[str]] = None
    ) -> MultiAgentConfig:
        """添加子 Agent
        
        Args:
            app_id: 应用 ID
            agent_id: Agent ID
            name: Agent 名称
            role: 角色描述
            priority: 优先级
            capabilities: 能力列表
            
        Returns:
            更新后的配置
        """
        config = self.get_config(app_id)
        if not config:
            raise ResourceNotFoundException("多 Agent 配置", str(app_id))
        
        # 验证 Agent 存在
        agent = self.db.get(AgentConfig, agent_id)
        if not agent:
            raise ResourceNotFoundException("Agent", str(agent_id))
        
        # 检查是否已存在
        for sub_agent in config.sub_agents:
            if sub_agent["agent_id"] == str(agent_id):
                raise BusinessException("Agent 已存在于配置中", BizCode.DUPLICATE_RESOURCE)
        
        # 添加子 Agent
        new_sub_agent = {
            "agent_id": str(agent_id),
            "name": name,
            "role": role,
            "priority": priority,
            "capabilities": capabilities or []
        }
        
        config.sub_agents.append(new_sub_agent)
        
        # 标记为已修改
        self.db.add(config)
        self.db.commit()
        self.db.refresh(config)
        
        logger.info(
            f"添加子 Agent 成功",
            extra={
                "config_id": str(config.id),
                "agent_id": str(agent_id),
                "agent_name": name
            }
        )
        
        return config
    
    def remove_sub_agent(
        self,
        app_id: uuid.UUID,
        agent_id: uuid.UUID
    ) -> MultiAgentConfig:
        """移除子 Agent
        
        Args:
            app_id: 应用 ID
            agent_id: Agent ID
            
        Returns:
            更新后的配置
        """
        config = self.get_config(app_id)
        if not config:
            raise ResourceNotFoundException("多 Agent 配置", str(app_id))
        
        # 查找并移除
        original_count = len(config.sub_agents)
        config.sub_agents = [
            sub_agent for sub_agent in config.sub_agents
            if sub_agent["agent_id"] != str(agent_id)
        ]
        
        if len(config.sub_agents) == original_count:
            raise ResourceNotFoundException("子 Agent", str(agent_id))
        
        # 标记为已修改
        self.db.add(config)
        self.db.commit()
        self.db.refresh(config)
        
        logger.info(
            f"移除子 Agent 成功",
            extra={
                "config_id": str(config.id),
                "agent_id": str(agent_id)
            }
        )
        
        return config
    
    def list_configs(
        self,
        workspace_id: uuid.UUID,
        page: int = 1,
        pagesize: int = 20
    ) -> Tuple[List[MultiAgentConfig], int]:
        """列出多 Agent 配置
        
        Args:
            workspace_id: 工作空间 ID
            page: 页码
            pagesize: 每页数量
            
        Returns:
            配置列表和总数
        """
        # 构建查询
        stmt = (
            select(MultiAgentConfig)
            .join(App)
            .where(App.workspace_id == workspace_id)
            .order_by(desc(MultiAgentConfig.created_at))
        )
        
        # 总数
        count_stmt = stmt.with_only_columns(MultiAgentConfig.id)
        total = len(self.db.execute(count_stmt).all())
        
        # 分页
        stmt = stmt.offset((page - 1) * pagesize).limit(pagesize)
        configs = list(self.db.scalars(stmt).all())
        
        return configs, total
