"""AgentScope Service Module

Provides the main service interface for agent-based operations.
Uses single-agent + skills architecture with MCP tools.
Uses OpenAI-compatible model interface for better compatibility.
""" 
import agentscope
from agentscope.formatter import OpenAIChatFormatter
from agentscope.message import Msg
from agentscope.agent import ReActAgent
from agentscope.tool import Toolkit
from agentscope.plan import PlanNotebook

import logging
from typing import List, Dict, Any, AsyncGenerator, Optional
import json
import time

from src.services.provider.agentscope_adapter import PixelAgentScopeModel

# Import unified ToolkitFactory
from src.services.agent_engine.toolkit import ToolkitFactory

# 导入 utilities and models
from .models import AgentResponse
from .utils.common import create_error_chunk, extract_structured_data

# 导入 prompts
from .prompts import SYSTEM_PROMPT

logger = logging.getLogger(__name__)

class AgentScopeService:
    """ AgentScope Service - Entry point for agent operations.
    
    Uses single-agent + skills architecture with MCP-based toolkits.
    """
    _instance = None
    _initialized = False

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super(AgentScopeService, cls).__new__(cls)
        return cls._instance

    def __init__(self, enable_skills: bool = True, user_id: Optional[str] = None):
        """初始化 AgentScope 服务

        Args:
            enable_skills: 是否启用 skills（推荐开启）
            user_id: 当前用户 ID，用于生成工具的 API key 获取
        """
        if not self._initialized:
            self.enable_skills = enable_skills
            self._model = None
            self.user_id = user_id

            self.init_agentscope()
            self._initialized = True

            logger.info("Using single-agent + skills architecture")

    async def shutdown(self):
        """清理 resources on shutdown."""
        # 清理 toolkit 缓存
        ToolkitFactory.clear_cache()
        logger.info("AgentScopeService shutdown complete")
    
    def _create_model(
        self, 
        provider: str = "dashscope", 
        model_name: str = "qwen-plus"
    ) -> PixelAgentScopeModel:
        """ 创建 OpenAI-compatible model instance.
        
        Args:
            provider: Provider name (e.g., "dashscope", "moonshot", "google")
            model_name: Model name (e.g., "qwen-plus", "kimi-k2.5")
        
        Returns:
            PixelAgentScopeModel instance configured from registry
        
        Note:
            base_url will be automatically loaded from ModelRegistry configuration
        """
        logger.info(f"Creating model: provider={provider}, model_name={model_name}")
        
        return PixelAgentScopeModel(
            provider=provider,
            model_name=model_name
        )
    
    def init_agentscope(self):
        """ 初始化 AgentScope environment with robust configuration."""
        try:
            # AgentScope philosophy: Explicit initialization with project info and logging
            agentscope.init(
                project="pixel-backend",
                name="service-node",
                logging_level="INFO"
            )
            logger.info("AgentScope initialized successfully with OpenAI-compatible mode.")
        except Exception as e:
            logger.error(f"CRITICAL: Failed to initialize AgentScope: {e}", exc_info=True)

    async def stream_chat(self, messages: List[Dict[str, Any]]) -> AsyncGenerator[str, None]:
        """ 流式对话处理
        
        使用单智能体 + skills 架构
        """
        # Ensure initialization
        if not self._initialized:
            self.init_agentscope()
            if not self._initialized:
                yield create_error_chunk("AgentScope service failed to initialize.")
                return

        logger.info(f"AgentScope stream_chat received {len(messages)} messages")

        # Prepare History
        history_msgs = []
        for m in messages:
            role = "assistant" if m["role"] in ["assistant", "model"] else "user"
            content = m["content"]
            history_msgs.append(Msg(name=role, content=content, role=role))

        # Single ReActAgent + Skills
        async for chunk in self._stream_chat_single_agent(history_msgs):
            yield chunk
    
    async def _stream_chat_single_agent(self, history_msgs: List[Msg]) -> AsyncGenerator[str, None]:
        """处理单智能体 + skills 模式的对话
        
        使用 AgentScope 官方推荐的单智能体 + Skills 架构：
        1. 获取统一的 Toolkit（包含所有工具和 Skills）
        2. 获取 Skills 提示词并附加到系统提示词
        3. 创建 ReActAgent 并执行
        
        参考：https://doc.agentscope.io/tutorial/task_agent_skill.html
        """

        # 模型配置
        try:
            model = self._create_model()
        except Exception as e:
            logger.error(f"Failed to configure model: {e}")
            yield create_error_chunk("Failed to configure AI model.")
            return
        
        # 获取统一的 Toolkit（单例，包含所有工具和 Skills）
        # 符合 AgentScope 官方最佳实践
        toolkit = ToolkitFactory.get_toolkit(skill_domains=["general"], user_id=self.user_id)
        
        # 构建系统提示词
        system_prompt = SYSTEM_PROMPT
        
        # 使用 AgentScope 标准方式获取 skills 提示词
        # Skills 会自动附加到系统提示词中，告诉智能体如何使用这些技能
        if self.enable_skills:
            try:
                skills_prompt = toolkit.get_agent_skill_prompt()
                if skills_prompt:
                    system_prompt = f"{SYSTEM_PROMPT}\n\n{skills_prompt}"
                    logger.info(f"✓ Added skills prompt: {len(skills_prompt)} characters")
                else:
                    logger.warning("Skills enabled but no skills prompt generated")
            except Exception as e:
                logger.error(f"Failed to get skills prompt: {e}")
        
        # 创建单个 ReActAgent，配备所有工具和技能
        # 这是 AgentScope 推荐的架构，相比多智能体更简单高效
        agent = ReActAgent(
            name="PixelAgent",
            sys_prompt=system_prompt,
            model=model,
            formatter=OpenAIChatFormatter(),  # 使用 OpenAI 格式化器
            toolkit=toolkit,
            memory=None,  # 使用默认 InMemoryMemory
            plan_notebook=PlanNotebook(),  # 启用规划模块
            enable_meta_tool=True,  # 允许智能体管理自己的计划
            max_iters=15,  # 允许足够的迭代次数用于规划和执行
        )
        
        logger.info(f"✓ Created single ReActAgent with {len(toolkit.tools) if hasattr(toolkit, 'tools') else 'unknown'} tools")
        
        # Load History
        if len(history_msgs) > 1:
            try:
                await agent.memory.add(history_msgs[:-1])
                logger.info(f"Added {len(history_msgs)-1} messages to agent memory")
            except Exception as e:
                logger.error(f"Failed to load history into agent memory: {e}")
                yield create_error_chunk("Failed to load chat history.")
                return
        
        # Agent Execution
        try:
            # 不使用 structured_model，避免循环问题
            response_msg = await agent(history_msgs[-1])
            logger.info(f"Agent execution completed.")
            
            # 提取内容 - 处理 AgentScope 的列表格式
            raw_content = response_msg.content if response_msg.content else ""
            
            # 如果是列表格式，提取文本
            if isinstance(raw_content, list):
                text_parts = []
                for item in raw_content:
                    if isinstance(item, dict):
                        if item.get("type") == "text":
                            text_parts.append(item.get("text", ""))
                    elif isinstance(item, str):
                        text_parts.append(item)
                content = "\n".join(text_parts)
            else:
                content = str(raw_content)
            
            # 尝试从响应中提取节点信息（如果有 JSON 代码块）
            nodes_json_block = ""
            if "```json:nodes" in content:
                # 已经包含节点信息，保持原样
                pass
            elif "```json" in content:
                # 可能包含节点信息，尝试提取
                try:
                    json_match = content.split("```json")[1].split("```")[0].strip()
                    parsed = json.loads(json_match)
                    if isinstance(parsed, dict) and "nodes" in parsed:
                        nodes = parsed["nodes"]
                        nodes_json = json.dumps(nodes, indent=2)
                        nodes_json_block = f"\n\n```json:nodes\n{nodes_json}\n```"
                        # 从 content 中移除原始 JSON 块
                        content = content.split("```json")[0].strip()
                except Exception as e:
                    logger.debug(f"Failed to extract nodes from JSON block: {e}")

            # Construct Final Output
            final_output = content + nodes_json_block
            
            # Send Stream Response
            chunk_data = {
                "id": "chatcmpl-react",
                "object": "chat.completion.chunk",
                "created": int(time.time()),
                "model": "qwen-plus",
                "choices": [{
                    "index": 0,
                    "delta": {"content": final_output},
                    "finish_reason": "stop"
                }]
            }
            yield f"data: {json.dumps(chunk_data)}\n\n"
            yield "data: [DONE]\n\n"

        except Exception as e:
            logger.error(f"AgentScope execution error: {e}", exc_info=True)
            yield create_error_chunk(f"An error occurred during agent execution: {str(e)}")
