"""
OneBot 适配器（基于 MoFox-Bus 完全重写版）

核心流程：
1. OneBot WebSocket 连接 → 接收 OneBot 格式消息
2. from_platform_message: OneBot dict → MessageEnvelope
3. CoreSink → 推送到 MoFox-Bot 核心
4. 核心回复 → _send_platform_message: MessageEnvelope → OneBot API 调用
"""

from __future__ import annotations

import asyncio
import uuid
from typing import Any, cast

import orjson
from mofox_wire import CoreSink, MessageEnvelope, WebSocketAdapterOptions


from src.app.plugin_system.api.log_api import get_logger
from src.core.components.base import BaseAdapter, BasePlugin
from src.core.components.loader import register_plugin

from .config import OneBotAdapterConfig
from .src.handlers import utils as handler_utils
from .src.handlers.to_core.message_handler import MessageHandler
from .src.handlers.to_core.meta_event_handler import MetaEventHandler
from .src.handlers.to_core.notice_handler import NoticeHandler
from .src.handlers.to_napcat.send_handler import SendHandler

logger = get_logger("onebot_adapter")


def _validate_bot_identity(config: OneBotAdapterConfig) -> None:
    """校验 Bot 身份配置。"""

    qq_id = str(config.bot.qq_id).strip()
    qq_nickname = str(config.bot.qq_nickname).strip()

    invalid_id_values = {"", "0", "none", "null", "undefined", "pydanticundefined"}
    if qq_id.lower() in invalid_id_values or not qq_id.isdigit():
        raise ValueError("配置项 bot.qq_id 无效：必须为非空数字字符串")

    invalid_nickname_values = {"", "none", "null", "undefined", "pydanticundefined"}
    if qq_nickname.lower() in invalid_nickname_values:
        raise ValueError("配置项 bot.qq_nickname 无效：必须为非空昵称")


class OneBotAdapter(BaseAdapter):
    """OneBot 适配器 - 完全基于 mofox-wire 架构"""

    adapter_name = "onebot_adapter"
    adapter_version = "2.0.0"
    adapter_author = "MoFox Team"
    adapter_description = "基于 MoFox-Bus 的 OneBot 11 适配器"
    platform = "qq"

    run_in_subprocess = False

    def __init__(self, core_sink: CoreSink, plugin: OneBotAdapterPlugin | None = None, **kwargs):
        """初始化 OneBot 适配器"""
        # 从插件配置读取 WebSocket URL
        if plugin and plugin.config:
            config = cast(OneBotAdapterConfig, plugin.config)
            host = config.onebot_server.host
            port = config.onebot_server.port
            access_token = config.onebot_server.access_token
            mode_str = config.onebot_server.mode
            ws_mode = "client" if mode_str == "direct" else "server"

            ws_url = f"ws://{host}:{port}"
            headers = {}
            if access_token:
                headers["Authorization"] = f"Bearer {access_token}"
        else:
            ws_url = "ws://127.0.0.1:8095"
            headers = {}
            ws_mode = "server"

        # 配置 WebSocket 传输
        transport = WebSocketAdapterOptions(
            mode=ws_mode,
            url=ws_url,
            headers=headers if headers else None,
        )

        super().__init__(core_sink, plugin=plugin, transport=transport, **kwargs)

        # 初始化处理器
        self.message_handler = MessageHandler(self)
        self.notice_handler = NoticeHandler(self)
        self.meta_event_handler = MetaEventHandler(self)
        self.send_handler = SendHandler(self)

        # 响应池：用于存储等待的 API 响应
        self._response_pool: dict[str, asyncio.Future] = {}
        self._response_timeout = 30.0

        # WebSocket 连接（用于发送 API 请求）
        # 注意：_ws 继承自 BaseAdapter，是 WebSocketLike 协议类型
        self._onebot_ws = None  # 可选的额外连接引用

        # 注册 utils 内部使用的适配器实例，便于工具方法自动获取 WS
        handler_utils.register_adapter(self)

    def _should_process_event(self, raw: dict[str, Any]) -> bool:
        """
        检查事件是否应该被处理（黑白名单过滤）

        此方法在 from_platform_message 顶层调用，对所有类型的事件（消息、通知、元事件）进行过滤。

        Args:
            raw: OneBot 原始事件数据

        Returns:
            bool: True表示应该处理，False表示应该过滤
        """
        if not self.plugin or not self.plugin.config:
            return True

        config = cast(OneBotAdapterConfig, self.plugin.config)
        features_config = config.features
        post_type = raw.get("post_type")

        # 获取用户信息（根据事件类型从不同字段获取）
        user_id: str = ""
        if post_type == "message":
            sender_info = raw.get("sender", {})
            user_id = str(sender_info.get("user_id", ""))
        elif post_type == "notice":
            user_id = str(raw.get("user_id", ""))
        else:
            # 元事件或其他类型不需要过滤
            return True

        # 检查全局封禁用户列表
        ban_user_ids = [str(item) for item in features_config.ban_user_id]
        if user_id and user_id in ban_user_ids:
            logger.debug(f"用户 {user_id} 在全局封禁列表中，事件被过滤")
            return False

        # 获取消息类型（消息事件使用 message_type，通知事件根据 group_id 判断）
        message_type = raw.get("message_type")
        group_id = raw.get("group_id")

        # 如果是通知事件，根据是否有 group_id 判断是群通知还是私聊通知
        if post_type == "notice":
            message_type = "group" if group_id else "private"

        # 群聊/群通知过滤
        if message_type == "group" and group_id:
            group_id_str = str(group_id)
            group_list_type = features_config.group_list_type
            group_list = [str(item) for item in features_config.group_list]

            if group_list_type == "blacklist":
                if group_id_str in group_list:
                    logger.debug(f"群聊 {group_id_str} 在黑名单中，事件被过滤")
                    return False
            else:  # whitelist
                if group_id_str not in group_list:
                    logger.debug(f"群聊 {group_id_str} 不在白名单中，事件被过滤")
                    return False

        # 私聊/私聊通知过滤
        elif message_type == "private":
            private_list_type = features_config.private_list_type
            private_list = [str(item) for item in features_config.private_list]

            if private_list_type == "blacklist":
                if user_id in private_list:
                    logger.debug(f"私聊用户 {user_id} 在黑名单中，事件被过滤")
                    return False
            else:  # whitelist
                if user_id not in private_list:
                    logger.debug(f"私聊用户 {user_id} 不在白名单中，事件被过滤")
                    return False

        # 通过所有过滤条件
        return True

    async def on_adapter_loaded(self) -> None:
        """适配器加载时的初始化"""
        logger.info("OneBot 适配器正在启动...")

        if not self.plugin or not self.plugin.config:
            raise RuntimeError("OneBot 适配器启动失败：缺少插件配置")

        config = cast(OneBotAdapterConfig, self.plugin.config)
        _validate_bot_identity(config)

        # 设置处理器配置（将整个 plugin 对象传递给处理器）
        # 注意：handlers 现在会直接访问 plugin.config 而不是接收 dict
        # 这里不再需要调用 set_plugin_config，因为处理器会通过 adapter.plugin 访问
        logger.info("OneBot 适配器已加载")

    async def on_adapter_unloaded(self) -> None:
        """适配器卸载时的清理"""
        logger.info("OneBot 适配器正在关闭...")

        self.meta_event_handler.stop_heartbeat_monitor()

        # 清理响应池
        for future in self._response_pool.values():
            if not future.done():
                future.cancel()
        self._response_pool.clear()

        logger.info("OneBot 适配器已关闭")

    async def from_platform_message(self, raw: dict[str, Any]) -> MessageEnvelope | None:  # type: ignore[override]
        """
        将 OneBot 原始消息转换为 MessageEnvelope

        这是核心转换方法，处理：
        - message 事件 → 消息
        - notice 事件 → 通知（戳一戳、表情回复等）
        - meta_event 事件 → 元事件（心跳、生命周期）
        - API 响应 → 存入响应池

        注意：黑白名单等过滤机制在此方法最开始执行，确保所有类型的事件都能被过滤。
        """
        post_type = raw.get("post_type")

        # API 响应（没有 post_type，有 echo）
        if post_type is None and "echo" in raw:
            echo = raw.get("echo")
            if echo and echo in self._response_pool:
                future = self._response_pool[echo]
                if not future.done():
                    future.set_result(raw)
            return None

        # 顶层过滤：黑白名单等过滤机制
        if not self._should_process_event(raw):
            return None

        try:
            # 消息事件
            if post_type == "message":
                return await self.message_handler.handle_raw_message(raw)  # type: ignore[return-value]

            # 通知事件
            elif post_type == "notice":
                return await self.notice_handler.handle_notice(raw)  # type: ignore[return-value]

            # 元事件
            elif post_type == "meta_event":
                return await self.meta_event_handler.handle_meta_event(raw)  # type: ignore[return-value]

            # 未知事件类型
            else:
                return None
        except ValueError as ve:
            logger.warning(f"处理 OneBot 事件时数据无效: {ve}")
            return None
        except Exception as e:
            logger.error(f"处理 OneBot 事件失败: {e}, 原始数据: {raw}")
            return None

    async def _send_platform_message(self, envelope: MessageEnvelope) -> None:  # type: ignore[override]
        """
        将 MessageEnvelope 转换并发送到 OneBot

        这里不直接通过 WebSocket 发送 envelope，
        而是调用 OneBot API（send_group_msg, send_private_msg 等）
        """
        try:
            await self.send_handler.handle_message(envelope)
        except Exception as e:
            logger.error(f"发送 OneBot 消息失败: {e}")

    async def send_onebot_api(self, action: str, params: dict[str, Any], timeout: float = 30.0) -> dict[str, Any]:
        """
        发送 OneBot API 请求并等待响应

        Args:
            action: API 动作名称（如 send_group_msg）
            params: API 参数
            timeout: 超时时间（秒）

        Returns:
            API 响应数据
        """
        if not self._ws:
            raise RuntimeError("WebSocket 连接未建立")

        # 生成唯一的 echo ID
        echo = str(uuid.uuid4())

        # 创建 Future 用于等待响应
        future = asyncio.Future()
        self._response_pool[echo] = future

        # 构造请求
        # OneBot expects JSON text frames; orjson.dumps returns bytes so decode to str
        request = orjson.dumps(
            {
                "action": action,
                "params": params,
                "echo": echo,
            }
        ).decode()

        try:
            loop = asyncio.get_running_loop()
            started_at = loop.time()

            # 发送请求
            await asyncio.wait_for(self._ws.send(request), timeout=timeout)

            elapsed = loop.time() - started_at
            remaining_timeout = timeout - elapsed
            if remaining_timeout <= 0:
                raise asyncio.TimeoutError()

            # 等待响应
            response = await asyncio.wait_for(future, timeout=remaining_timeout)
            return response

        except asyncio.TimeoutError:
            logger.error(f"API 请求超时: {action}")
            raise
        except Exception as e:
            logger.error(f"API 请求失败: {action}, 错误: {e}")
            raise
        finally:
            # 清理响应池
            self._response_pool.pop(echo, None)

    def get_ws_connection(self):
        """获取 WebSocket 连接（用于发送 API 请求）"""
        if not self._ws:
            raise RuntimeError("WebSocket 连接未建立")
        return self._ws

    async def get_bot_info(self) -> dict[str, Any]:  # type: ignore[override]
        """获取 Bot 信息（QQ ID 和昵称）"""
        if not self.plugin or not self.plugin.config:
            return {}

        config = cast(OneBotAdapterConfig, self.plugin.config)
        return {
            "bot_id": config.bot.qq_id,
            "bot_name": config.bot.qq_nickname,
            "platform": self.platform,
        }

@register_plugin
class OneBotAdapterPlugin(BasePlugin):
    """OneBot 适配器插件"""

    plugin_name = "onebot_adapter"
    plugin_version = "2.0.0"
    plugin_author = "MoFox Team"
    plugin_description = "OneBot 11 适配器（基于 Neo-MoFox 重写）"
    configs = [OneBotAdapterConfig]


    def get_components(self) -> list[type]:
        """获取插件内所有组件类

        Returns:
            list[type]: 插件内所有组件类的列表
        """
        return [OneBotAdapter]
