# 中文注释：
# 文件：echobot/agent/instance.py
# 说明：单个 Agent 实例封装。

"""Single Agent Instance."""

import asyncio
from pathlib import Path
from typing import Any

from loguru import logger

from echobot.bus.queue import MessageBus
from echobot.bus.events import InboundMessage, OutboundMessage
from echobot.agent.loop import AgentLoop
from echobot.agent.tools.call_agent import CallAgentTool, ListAgentsTool
from echobot.channels.telegram import TelegramChannel
from echobot.config.schema import AgentInstanceConfig
from echobot.providers.litellm_provider import LiteLLMProvider


class AgentInstance:
    """
    单个 Agent 实例封装

    每个 Agent 实例包含：
    - 独立的 Telegram Bot token
    - 独立的消息总线
    - 独立的 AgentLoop
    - 独立的 Telegram Channel
    """

    def __init__(
        self,
        config: AgentInstanceConfig,
        provider: LiteLLMProvider,
        defaults: dict[str, Any],
        collaboration_config: dict[str, Any] | None = None,
        shared_channel=None,
        shared_bus=None,
        mcp_config: dict | None = None,
    ):
        """
        Initialize Agent Instance.

        Args:
            config: Agent 实例配置
            provider: LLM Provider
            defaults: 全局默认配置 (AgentDefaults)
            collaboration_config: 协作配置，用于 Agent 间调用
            shared_channel: 共享的 Telegram Channel（多 Agent 模式使用同一个 Bot 时）
            shared_bus: 共享的 Message Bus（多 Agent 模式使用同一个 Bot 时）
            mcp_config: MCP 配置
        """
        self.config = config
        self.id = config.id
        self.name = config.name
        self.token = config.token
        self.role = config.role

        # 使用共享的 Message Bus 或创建独立的
        self.bus = shared_bus if shared_bus else MessageBus()

        # 保存协作配置引用
        self._collaboration_config = collaboration_config or {}

        # 使用共享的 Telegram Channel 或创建独立的
        if shared_channel:
            self.channel = shared_channel
        else:
            from echobot.config.schema import TelegramConfig
            tg_config = TelegramConfig(enabled=True, token=self.token)
            self.channel = TelegramChannel(config=tg_config, bus=self.bus)

        # 创建 Agent Loop
        workspace = Path(config.workspace).expanduser()
        workspace.mkdir(parents=True, exist_ok=True)

        # 创建工作区模板文件
        self._create_workspace_templates(workspace)

        self.agent_loop = AgentLoop(
            bus=self.bus,
            provider=provider,
            workspace=workspace,
            instance=config.id,  # 使用 agent id 作为实例名
            model=defaults.get("model", "anthropic/claude-sonnet-4-5"),
            max_iterations=defaults.get("max_tool_iterations", 20),
            role_name=self.role,
            default_role=defaults.get("default_role"),
            context_compression_enabled=defaults.get("context_compression_enabled", True),
            context_history_max_messages=defaults.get("context_history_max_messages", 40),
            context_keep_recent_messages=defaults.get("context_keep_recent_messages", 16),
            context_summary_max_chars=defaults.get("context_summary_max_chars", 2000),
            approval_ttl_seconds=defaults.get("approval_ttl_seconds", 15 * 60),
            approval_required_tools=defaults.get("approval_required_tools"),
            denied_exec_patterns=defaults.get("denied_exec_patterns"),
            webhook_secret=defaults.get("webhook_secret"),
            mcp_servers=mcp_config,
        )

        # 运行状态
        self._running = False
        self._channel_task: asyncio.Task | None = None

        # 出站消息回调 - 将消息转发到全局总线
        self._outbound_callback: Any = None

    def set_outbound_callback(self, callback) -> None:
        """设置出站消息回调，用于将消息发送到全局消息处理"""
        self._outbound_callback = callback

    def set_collaboration_callbacks(
        self,
        call_agent_callback,
        get_available_agents_callback,
    ) -> None:
        """
        设置协作回调函数

        Args:
            call_agent_callback: 调用其他 Agent 的回调
            get_available_agents_callback: 获取可用 Agent 列表的回调
        """
        self._call_agent_callback = call_agent_callback
        self._get_available_agents_callback = get_available_agents_callback

        # 注册协作工具
        if self._collaboration_config.get("enabled", False):
            self._register_collaboration_tools()

    def _register_collaboration_tools(self) -> None:
        """注册 Agent 间协作工具"""
        # 注册 call_agent 工具
        call_agent_tool = CallAgentTool(
            call_agent_callback=self._call_agent_callback,
            get_available_agents_callback=self._get_available_agents_callback,
            current_agent_id=self.id,
        )
        self.agent_loop.tools.register(call_agent_tool)

        # 注册 list_agents 工具
        list_agents_tool = ListAgentsTool(
            get_available_agents_callback=self._get_available_agents_callback,
        )
        self.agent_loop.tools.register(list_agents_tool)

        logger.debug(f"Registered collaboration tools for agent {self.id}")

    def _create_workspace_templates(self, workspace: Path) -> None:
        """Create default workspace template files for the agent."""
        # 根据 Agent 角色生成不同的模板内容
        templates = {
            "SOUL.md": f"""# Soul - 我是谁

我是 {self.name}，echobot 的 {self.role} 角色。

## 身份

- 角色：{self.role}
- 用途：{self._get_role_description()}

## 性格

- 乐于助人
- 准确严谨
- 友好亲切

## 价值观

- 用户隐私优先
- 行动透明
- 持续学习
""",
            "AGENTS.md": f"""# Agent Instructions - 怎么干活

你是 {self.name}，一个专业的 {self.role}。

## 工作原则

1. 在执行操作前先解释要做什么
2. 请求不明确时主动询问
3. 善于使用工具完成任务
4. 重要信息记录到记忆文件

## 技能

- 文件读写与编辑
- 执行 Shell 命令
- 网络搜索与获取
- 代码编写与调试
""",
            "USER.md": """# User - 用户信息

用户信息将记录在这里。

## 用户偏好

- 交流风格：
- 时区：
- 语言：

## 重要备注

（用户的重要信息）
""",
        }

        for filename, content in templates.items():
            file_path = workspace / filename
            if not file_path.exists():
                file_path.write_text(content)
                logger.debug(f"Created {filename} for agent {self.id}")

        # 创建 memory 目录和 MEMORY.md
        memory_dir = workspace / "memory"
        memory_dir.mkdir(exist_ok=True)
        memory_file = memory_dir / "MEMORY.md"
        if not memory_file.exists():
            memory_file.write_text("""# Long-term Memory - 长期记忆

这里存储跨会话的重要信息。

## 用户信息

（关于用户的重要事实）

## 偏好设置

（用户偏好学习）

## 重要备注

（需要记住的重要事项）
""")

    def _get_role_description(self) -> str:
        """Get role description based on role name."""
        role_descriptions = {
            "coder": "编程助手，擅长代码编写和调试",
            "reviewer": "代码审查者，擅长代码审查和优化",
            "tester": "测试工程师，擅长测试用例编写",
            "default": "通用助手",
            "analyzer": "数据分析师",
            "text_assistant": "文本助手",
            "excel_assistant": "Excel 助手",
            "data_analyst": "数据分析师",
        }
        return role_descriptions.get(self.role, "通用助手")

    async def _dispatch_outbound(self) -> None:
        """将本 Agent 的出站消息发送到 Telegram"""
        while self._running:
            try:
                msg = await asyncio.wait_for(self.bus.consume_outbound(), timeout=1.0)
                if msg:
                    # 开始显示 typing（在发送消息期间持续显示）
                    if self.channel and hasattr(self.channel, 'start_typing'):
                        await self.channel.start_typing(msg.chat_id)

                    # 发送到 Telegram
                    if self.channel:
                        try:
                            await self.channel.send(msg)
                        except Exception as e:
                            logger.error(f"Error sending to Telegram: {e}")

                    # 停止 typing
                    if self.channel and hasattr(self.channel, 'stop_typing'):
                        await self.channel.stop_typing(msg.chat_id)

                    # 同时调用回调（用于日志等）
                    if self._outbound_callback:
                        await self._outbound_callback(msg)
            except asyncio.TimeoutError:
                continue
            except Exception as e:
                logger.error(f"Error dispatching outbound message: {e}")

    async def start(self, start_channel: bool = True) -> None:
        """启动 Agent

        Args:
            start_channel: 是否启动 Telegram Channel（共享 channel 时由管理器统一启动）
        """
        if self._running:
            logger.warning(f"Agent {self.id} already running")
            return

        logger.info(f"Starting agent: {self.id} ({self.name}) with role: {self.role}")

        self._running = True

        # 启动 Telegram Channel（如果不是共享的）
        if start_channel:
            self._channel_task = asyncio.create_task(self.channel.start())

        # 启动 Agent Loop
        asyncio.create_task(self.agent_loop.run())

        # 启动出站消息分发器
        asyncio.create_task(self._dispatch_outbound())

        logger.info(f"Agent {self.id} started successfully")

    async def stop(self) -> None:
        """停止 Agent"""
        if not self._running:
            return

        logger.info(f"Stopping agent: {self.id}")

        self._running = False

        # 停止 Channel
        if self.channel:
            await self.channel.stop()

        # 停止 Agent Loop
        if self.agent_loop:
            self.agent_loop.stop()

        # 取消 Channel 任务
        if self._channel_task:
            self._channel_task.cancel()
            try:
                await self._channel_task
            except asyncio.CancelledError:
                pass

        logger.info(f"Agent {self.id} stopped")

    async def process_message(self, msg: InboundMessage) -> str | None:
        """
        直接处理消息（用于 Agent 间调用）

        Args:
            msg: 入站消息

        Returns:
            响应内容
        """
        return await self.agent_loop.process_direct(
            content=msg.content,
            session_key=msg.session_key,
        )

    def get_status(self) -> dict:
        """获取 Agent 状态"""
        return {
            "id": self.id,
            "name": self.name,
            "role": self.role,
            "running": self._running,
            "workspace": self.agent_loop.workspace,
        }
