"""消息处理器"""

import asyncio
import logging
import re
from typing import Optional, Dict, Any, List

from .config import TARGET_GROUP_ID, POLL_INTERVAL, POLL_TIMEOUT
from .napcat_client import NapCatClient
from .web_api_client import WebAPIClient
from .state_manager import StateManager
from . import config  # 导入 config 模块，而不是导入 BOT_QQ_ID

logger = logging.getLogger(__name__)


class MessageHandler:
    """消息处理器"""

    def __init__(
        self,
        napcat_client: NapCatClient,
        web_client: WebAPIClient,
        state_manager: StateManager,
    ):
        self.napcat = napcat_client
        self.web = web_client
        self.state = state_manager

    async def handle_message(self, message: str, user_id: int) -> bool:
        """处理群消息

        Args:
            message: 消息内容
            user_id: 发送者 QQ 号

        Returns:
            是否处理了该消息
        """
        logger.debug(f"handle_message 被调用: message={message}, user_id={user_id}")
        logger.debug(f"BOT_QQ_ID={config.BOT_QQ_ID}")

        # 检查是否 @ 机器人
        is_mentioned = self._is_mentioned(message)
        logger.debug(f"是否 @ 机器人: {is_mentioned}")

        if not is_mentioned:
            logger.debug("未 @ 机器人，忽略消息")
            return False

        # 移除 @ 标记
        clean_msg = self._clean_message(message)
        logger.info(f"处理消息: {clean_msg}")

        # 解析指令
        if clean_msg.startswith("/"):
            logger.info(f"处理指令: {clean_msg}")
            await self._handle_command(clean_msg)
        else:
            # 普通消息
            logger.info(f"处理普通消息: {clean_msg}")
            await self._handle_chat_message(clean_msg)

        return True

    def _is_mentioned(self, message: str) -> bool:
        """检查是否 @ 了机器人"""
        logger.debug(f"检查是否 @ 机器人: BOT_QQ_ID={config.BOT_QQ_ID}, message={message}")

        if not config.BOT_QQ_ID:
            logger.warning("BOT_QQ_ID 未设置")
            return False

        # 检查 CQ 码格式的 at
        pattern = rf"\[CQ:at,qq={config.BOT_QQ_ID}\]"
        result = bool(re.search(pattern, message))
        logger.debug(f"匹配结果: {result}, pattern={pattern}")
        return result

    def _clean_message(self, message: str) -> str:
        """清理消息，移除 @ 标记"""
        # 移除所有 CQ:at 标记
        cleaned = re.sub(r"\[CQ:at,qq=\d+\]", "", message)
        return cleaned.strip()

    async def _handle_command(self, command: str):
        """处理指令"""
        parts = command.split(maxsplit=1)
        cmd = parts[0].lower()
        args = parts[1] if len(parts) > 1 else ""

        try:
            if cmd == "/help":
                await self._cmd_help()
            elif cmd == "/test":
                await self._cmd_test()
            elif cmd == "/new":
                await self._cmd_new()
            elif cmd == "/resume":
                limit = int(args) if args.isdigit() else 10
                await self._cmd_resume(limit)
            elif cmd == "/switch":
                if not args:
                    await self.napcat.send_group_msg(
                        TARGET_GROUP_ID, "❌ 请提供对话 ID，例如：/switch conv_xxx"
                    )
                    return
                await self._cmd_switch(args.strip())
            elif cmd == "/stop":
                await self._cmd_stop()
            else:
                await self.napcat.send_group_msg(
                    TARGET_GROUP_ID, f"❌ 未知指令: {cmd}\n发送 /help 查看帮助"
                )
        except Exception as e:
            logger.error(f"处理指令失败: {e}", exc_info=True)
            await self.napcat.send_group_msg(
                TARGET_GROUP_ID, f"❌ 指令执行失败: {str(e)}"
            )

    async def _cmd_help(self):
        """显示帮助信息"""
        help_text = """🤖 QQ Bot 使用帮助

📝 基本指令：
/help - 显示此帮助信息
/new - 创建新对话
/resume [数量] - 查看最近的对话列表（默认10条）
/switch <对话ID> - 切换到指定对话
/stop - 停止当前运行的任务
/test - 测试自定义工具是否可用

💬 发送消息：
直接 @ 我并输入消息即可与 AI 对话
例如：@机器人 今天天气怎么样？

📌 注意事项：
• 只能在空闲时发送消息或执行指令（/stop 除外）
• 运行期间的消息会被忽略
• 模型可以使用工具执行命令、搜索网页等"""

        await self.napcat.send_group_msg(TARGET_GROUP_ID, help_text)

    async def _cmd_test(self):
        """测试自定义工具"""
        try:
            # 发送一个简单的测试消息
            message = "请告诉我你有哪些可用的工具？特别是有没有 send_qq_file 工具？"
            await self._send_message(message)
        except Exception as e:
            logger.error(f"测试失败: {e}", exc_info=True)
            await self.napcat.send_group_msg(TARGET_GROUP_ID, f"❌ 测试失败: {str(e)}")

    async def _cmd_new(self):
        """创建新对话"""
        state = self.state.get_state()
        if state.is_running():
            await self.napcat.send_group_msg(
                TARGET_GROUP_ID, "❌ 当前有任务正在运行，请等待完成或使用 /stop 停止"
            )
            return

        result = await self.web.create_conversation(thinking_mode=False, mode="fast")
        if result.get("success"):
            conv_id = result.get("conversation_id")
            state.set_conversation(conv_id)
            await self.napcat.send_group_msg(
                TARGET_GROUP_ID, f"✅ 已创建新对话: {conv_id}"
            )
        else:
            await self.napcat.send_group_msg(
                TARGET_GROUP_ID, f"❌ 创建对话失败: {result.get('error')}"
            )

    async def _cmd_resume(self, limit: int):
        """获取最近对话列表"""
        state = self.state.get_state()
        if state.is_running():
            await self.napcat.send_group_msg(
                TARGET_GROUP_ID, "❌ 当前有任务正在运行，请等待完成或使用 /stop 停止"
            )
            return

        result = await self.web.list_conversations(limit=limit)
        if not result.get("success"):
            await self.napcat.send_group_msg(
                TARGET_GROUP_ID, f"❌ 获取对话列表失败: {result.get('error')}"
            )
            return

        conversations = result.get("data", {}).get("conversations", [])
        if not conversations:
            await self.napcat.send_group_msg(TARGET_GROUP_ID, "📋 暂无对话记录")
            return

        # 格式化对话列表
        lines = [f"📋 最近 {len(conversations)} 条对话：\n"]
        for conv in conversations:
            conv_id = conv.get("id", "")
            title = conv.get("title", "无标题")
            lines.append(f"• {conv_id}\n  {title}")

        message = "\n".join(lines)
        await self.napcat.send_group_msg(TARGET_GROUP_ID, message)

    async def _cmd_switch(self, conversation_id: str):
        """切换对话"""
        state = self.state.get_state()
        if state.is_running():
            await self.napcat.send_group_msg(
                TARGET_GROUP_ID, "❌ 当前有任务正在运行，请等待完成或使用 /stop 停止"
            )
            return

        result = await self.web.load_conversation(conversation_id)
        if result.get("success"):
            state.set_conversation(conversation_id)
            await self.napcat.send_group_msg(
                TARGET_GROUP_ID, f"✅ 已切换到对话: {conversation_id}"
            )
        else:
            await self.napcat.send_group_msg(
                TARGET_GROUP_ID, f"❌ 切换对话失败: {result.get('error')}"
            )

    async def _cmd_stop(self):
        """停止当前任务"""
        state = self.state.get_state()
        if not state.is_running():
            await self.napcat.send_group_msg(TARGET_GROUP_ID, "ℹ️ 当前没有运行中的任务")
            return

        if not state.task_id:
            await self.napcat.send_group_msg(TARGET_GROUP_ID, "❌ 任务 ID 丢失")
            state.finish_task()
            return

        result = await self.web.cancel_task(state.task_id)
        if result.get("success"):
            await self.napcat.send_group_msg(TARGET_GROUP_ID, "✅ 已停止任务")
            state.finish_task()
        else:
            await self.napcat.send_group_msg(
                TARGET_GROUP_ID, f"❌ 停止任务失败: {result.get('error')}"
            )

    async def _handle_chat_message(self, message: str):
        """处理普通聊天消息"""
        state = self.state.get_state()

        # 检查是否空闲
        if state.is_running():
            logger.info("任务运行中，忽略消息")
            return

        # 检查是否有对话
        if not state.conversation_id:
            # 自动创建对话
            logger.info("自动创建对话...")
            result = await self.web.create_conversation(thinking_mode=False, mode="fast")
            if result.get("success"):
                conv_id = result.get("conversation_id")
                state.set_conversation(conv_id)
                await self.napcat.send_group_msg(
                    TARGET_GROUP_ID, f"✅ 已自动创建对话: {conv_id}"
                )
            else:
                await self.napcat.send_group_msg(
                    TARGET_GROUP_ID, f"❌ 创建对话失败: {result.get('error')}"
                )
                return

        # 发送消息
        try:
            logger.info(f"发送消息到对话 {state.conversation_id}: {message}")
            result = await self.web.send_message(message, state.conversation_id)
            if not result.get("success"):
                await self.napcat.send_group_msg(
                    TARGET_GROUP_ID, f"❌ 发送消息失败: {result.get('error')}"
                )
                return

            task_id = result.get("data", {}).get("task_id")
            if not task_id:
                await self.napcat.send_group_msg(TARGET_GROUP_ID, "❌ 未获取到任务 ID")
                return

            logger.info(f"任务已创建: {task_id}")
            state.start_task(task_id)

            # 开始轮询任务
            logger.info(f"开始轮询任务 {task_id}")
            await self._poll_task(task_id)

        except Exception as e:
            logger.error(f"处理消息失败: {e}", exc_info=True)
            await self.napcat.send_group_msg(
                TARGET_GROUP_ID, f"❌ 处理消息失败: {str(e)}"
            )
            state.finish_task()

    async def _poll_task(self, task_id: str):
        """轮询任务直到完成"""
        state = self.state.get_state()
        timeout_counter = 0
        max_timeout = POLL_TIMEOUT / POLL_INTERVAL

        logger.info(f"开始轮询任务 {task_id}，间隔 {POLL_INTERVAL}秒")

        while state.is_running():
            try:
                logger.debug(f"轮询任务 {task_id}，offset={state.last_offset}")
                result = await self.web.poll_task(task_id, state.last_offset)
                if not result.get("success"):
                    logger.error(f"轮询任务失败: {result}")
                    await self.napcat.send_group_msg(
                        TARGET_GROUP_ID, f"❌ 任务执行失败: {result.get('error')}"
                    )
                    state.finish_task()
                    break

                data = result.get("data", {})
                status = data.get("status")
                events = data.get("events", [])
                next_offset = data.get("next_offset", state.last_offset)

                logger.debug(f"任务状态: {status}, 事件数: {len(events)}, next_offset: {next_offset}")

                # 更新偏移量
                state.last_offset = next_offset

                # 处理事件
                if events:
                    logger.info(f"处理 {len(events)} 个事件")
                await self._process_events(events)

                # 检查任务状态
                if status in {"succeeded", "failed", "cancelled"}:
                    logger.info(f"任务完成，状态: {status}")
                    if status == "failed":
                        await self.napcat.send_group_msg(
                            TARGET_GROUP_ID, "❌ 任务执行失败"
                        )
                    elif status == "cancelled":
                        await self.napcat.send_group_msg(
                            TARGET_GROUP_ID, "ℹ️ 任务已取消"
                        )
                    state.finish_task()
                    break

                # 超时检查
                timeout_counter += 1
                if timeout_counter > max_timeout:
                    logger.warning("任务超时")
                    await self.napcat.send_group_msg(
                        TARGET_GROUP_ID, "⚠️ 任务超时，已自动停止"
                    )
                    await self.web.cancel_task(task_id)
                    state.finish_task()
                    break

                # 等待下次轮询
                await asyncio.sleep(POLL_INTERVAL)

            except Exception as e:
                logger.error(f"轮询任务异常: {e}", exc_info=True)
                await self.napcat.send_group_msg(
                    TARGET_GROUP_ID, f"❌ 任务执行异常: {str(e)}"
                )
                state.finish_task()
                break

    async def _process_events(self, events: List[Dict[str, Any]]):
        """处理任务事件"""
        for event in events:
            event_type = event.get("type")
            event_data = event.get("data", {})

            logger.debug(f"收到事件: type={event_type}, data_keys={list(event_data.keys())}")

            try:
                if event_type == "tool_start":
                    # 工具开始执行
                    tool_name = data.get("name", "")
                    logger.info(f"工具开始: {tool_name}")
                    await self._handle_tool_start(event_data)

                    # 检测到 send_qq_file 工具，记录工具 ID
                    if tool_name == "send_qq_file":
                        tool_id = event_data.get("id", "")
                        logger.info(f"检测到 send_qq_file 工具调用，ID: {tool_id}")
                        # 保存到状态，等待 update_action
                        state = self.state.get_state()
                        if not hasattr(state, 'pending_send_file_tools'):
                            state.pending_send_file_tools = set()
                        state.pending_send_file_tools.add(tool_id)
                elif event_type == "update_action":
                    # 工具执行完成（包含结果）
                    tool_id = event_data.get("id", "")
                    status = event_data.get("status", "")
                    result = event_data.get("result", {})

                    # 检查是否是 send_qq_file 工具
                    state = self.state.get_state()
                    if hasattr(state, 'pending_send_file_tools') and tool_id in state.pending_send_file_tools:
                        logger.info(f"收到 send_qq_file 工具结果，status: {status}")
                        state.pending_send_file_tools.remove(tool_id)

                        # 处理文件发送
                        if status == "success":
                            await self._handle_send_file_result(result)
                elif event_type == "text_end":
                    # 模型输出完成
                    await self._handle_text_end(event_data)
            except Exception as e:
                logger.error(f"处理事件失败: {event_type}, {e}", exc_info=True)

    async def _handle_tool_start(self, data: Dict[str, Any]):
        """处理工具调用开始"""
        tool_name = data.get("name", "unknown")
        arguments = data.get("arguments", {})

        # 格式化工具调用信息
        lines = [f"[工具] {tool_name}"]

        # 提取关键参数
        if tool_name == "run_command":
            cmd = arguments.get("command", "")
            lines.append(f"参数: {cmd}")
        elif tool_name == "web_search":
            query = arguments.get("query", "")
            lines.append(f"参数: {query}")
        elif tool_name == "read_file":
            path = arguments.get("path", "")
            lines.append(f"参数: {path}")
        elif tool_name == "write_file":
            path = arguments.get("path", "")
            lines.append(f"参数: {path}")
        else:
            # 其他工具，显示所有参数
            if arguments:
                args_str = ", ".join(f"{k}={v}" for k, v in arguments.items())
                lines.append(f"参数: {args_str}")

        message = "\n".join(lines)
        await self.napcat.send_group_msg(TARGET_GROUP_ID, message)

    async def _handle_text_end(self, data: Dict[str, Any]):
        """处理模型输出完成"""
        content = data.get("full_content", "")
        if content:
            # 发送模型输出
            await self.napcat.send_group_msg(TARGET_GROUP_ID, content)

    async def _handle_send_file_result(self, result: Dict[str, Any]):
        """处理 send_qq_file 工具结果"""
        try:
            # result 可能是字符串或字典
            if isinstance(result, str):
                import json
                result_data = json.loads(result)
            else:
                result_data = result

            if not result_data.get("success"):
                error = result_data.get("error", "未知错误")
                await self.napcat.send_group_msg(
                    TARGET_GROUP_ID, f"❌ 发送文件失败: {error}"
                )
                return

            file_path = result_data.get("file_path", "")
            description = result_data.get("description", "")

            if not file_path:
                logger.warning("send_qq_file 工具未提供文件路径")
                return

            # 转换容器内路径到宿主机路径
            # 容器内: /workspace/xxx -> 宿主机: users/qqbot/project/xxx
            if file_path.startswith("/workspace/"):
                relative_path = file_path[len("/workspace/"):]
                # 获取项目根目录
                project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
                host_path = os.path.join(project_root, "users", "qqbot", "project", relative_path)
            else:
                # 如果不是 /workspace 开头，假设是相对路径
                project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
                host_path = os.path.join(project_root, "users", "qqbot", "project", file_path)

            logger.info(f"转换文件路径: {file_path} -> {host_path}")

            # 检查文件是否存在
            if not os.path.exists(host_path):
                logger.error(f"文件不存在: {host_path}")
                await self.napcat.send_group_msg(
                    TARGET_GROUP_ID, f"❌ 文件不存在: {file_path}"
                )
                return

            # 发送文件到 QQ 群
            await self.napcat.send_file(TARGET_GROUP_ID, host_path, description)
            logger.info(f"已发送文件: {host_path}")

        except json.JSONDecodeError as e:
            logger.error(f"解析工具输出失败: {e}")
        except Exception as e:
            logger.error(f"发送文件失败: {e}", exc_info=True)
            await self.napcat.send_group_msg(
                TARGET_GROUP_ID, f"❌ 发送文件失败: {str(e)}"
            )

    async def _handle_tool_result(self, data: Dict[str, Any]):
        """处理工具执行结果"""
        tool_name = data.get("name", "")

        # 只处理 send_qq_file 工具
        if tool_name != "send_qq_file":
            return

        # 解析工具输出
        output = data.get("output", "")
        if not output:
            logger.warning("send_qq_file 工具无输出")
            return

        try:
            import json
            result = json.loads(output)

            if not result.get("success"):
                error = result.get("error", "未知错误")
                await self.napcat.send_group_msg(
                    TARGET_GROUP_ID, f"❌ 发送文件失败: {error}"
                )
                return

            file_path = result.get("file_path", "")
            description = result.get("description", "")

            if not file_path:
                logger.warning("send_qq_file 工具未提供文件路径")
                return

            # 转换容器内路径到宿主机路径
            # 容器内: /workspace/xxx -> 宿主机: users/qqbot/project/xxx
            if file_path.startswith("/workspace/"):
                relative_path = file_path[len("/workspace/"):]
                # 获取项目根目录
                project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
                host_path = os.path.join(project_root, "users", "qqbot", "project", relative_path)
            else:
                # 如果不是 /workspace 开头，假设是相对路径
                project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
                host_path = os.path.join(project_root, "users", "qqbot", "project", file_path)

            logger.info(f"转换文件路径: {file_path} -> {host_path}")

            # 检查文件是否存在
            if not os.path.exists(host_path):
                logger.error(f"文件不存在: {host_path}")
                await self.napcat.send_group_msg(
                    TARGET_GROUP_ID, f"❌ 文件不存在: {file_path}"
                )
                return

            # 发送文件到 QQ 群
            await self.napcat.send_file(TARGET_GROUP_ID, host_path, description)
            logger.info(f"已发送文件: {host_path}")

        except json.JSONDecodeError as e:
            logger.error(f"解析工具输出失败: {e}")
        except Exception as e:
            logger.error(f"发送文件失败: {e}", exc_info=True)
            await self.napcat.send_group_msg(
                TARGET_GROUP_ID, f"❌ 发送文件失败: {str(e)}"
            )
