from abc import ABC, abstractmethod
from typing import Any, List, Optional, Tuple, TYPE_CHECKING
from rich.traceback import install

from src.common.logger import get_logger
from src.plugin_system.base.component_types import ComponentType, ToolInfo, ToolParamType

if TYPE_CHECKING:
    from src.chat.message_receive.chat_stream import ChatStream

install(extra_lines=3)

logger = get_logger("base_tool")


class BaseTool(ABC):
    """所有工具的基类"""

    name: str = ""
    """工具的名称"""
    description: str = ""
    """工具的描述"""
    parameters: List[Tuple[str, ToolParamType, str, bool, List[str] | None]] = []
    """工具的参数定义，为[("param_name", param_type, "description", required, enum_values)]格式
       param_name: 参数名称
       param_type: 参数类型
       description: 参数描述
       required: 是否必填
       enum_values: 枚举值列表
       例如: [("arg1", ToolParamType.STRING, "参数1描述", True, None), ("arg2", ToolParamType.INTEGER, "参数2描述", False, ["1", "2", "3"])]
    """
    available_for_llm: bool = False
    """是否可供LLM使用"""

    def __init__(self, plugin_config: Optional[dict] = None, chat_stream: Optional["ChatStream"] = None):
        """初始化工具基类

        Args:
            plugin_config: 插件配置字典
            chat_stream: 聊天流对象，用于获取聊天上下文信息
        """
        self.plugin_config = plugin_config or {}  # 直接存储插件配置字典

        # =============================================================================
        # 便捷属性 - 直接在初始化时获取常用聊天信息（与BaseAction保持一致）
        # =============================================================================

        # 获取聊天流对象
        self.chat_stream = chat_stream
        self.chat_id = self.chat_stream.stream_id if self.chat_stream else None
        self.platform = getattr(self.chat_stream, "platform", None) if self.chat_stream else None

    @classmethod
    def get_tool_definition(cls) -> dict[str, Any]:
        """获取工具定义，用于LLM工具调用

        Returns:
            dict: 工具定义字典
        """
        if not cls.name or not cls.description or cls.parameters is None:
            raise NotImplementedError(f"工具类 {cls.__name__} 必须定义 name, description 和 parameters 属性")

        return {"name": cls.name, "description": cls.description, "parameters": cls.parameters}

    @classmethod
    def get_tool_info(cls) -> ToolInfo:
        """获取工具信息"""
        if not cls.name or not cls.description or cls.parameters is None:
            raise NotImplementedError(f"工具类 {cls.__name__} 必须定义 name, description 和 parameters 属性")

        return ToolInfo(
            name=cls.name,
            tool_description=cls.description,
            enabled=cls.available_for_llm,
            tool_parameters=cls.parameters,
            component_type=ComponentType.TOOL,
        )

    @abstractmethod
    async def execute(self, function_args: dict[str, Any]) -> dict[str, Any]:
        """执行工具函数(供llm调用)
           通过该方法，maicore会通过llm的tool call来调用工具
           传入的是json格式的参数，符合parameters定义的格式

        Args:
            function_args: 工具调用参数

        Returns:
            dict: 工具执行结果
        """
        raise NotImplementedError("子类必须实现execute方法")

    async def direct_execute(self, **function_args: dict[str, Any]) -> dict[str, Any]:
        """直接执行工具函数(供插件调用)
           通过该方法，插件可以直接调用工具，而不需要传入字典格式的参数
           插件可以直接调用此方法，用更加明了的方式传入参数
           示例: result = await tool.direct_execute(arg1="参数",arg2="参数2")

           工具开发者可以重写此方法以实现与llm调用差异化的执行逻辑

        Args:
            **function_args: 工具调用参数

        Returns:
            dict: 工具执行结果
        """
        parameter_required = [param[0] for param in self.parameters if param[3]]  # 获取所有必填参数名
        for param_name in parameter_required:
            if param_name not in function_args:
                raise ValueError(f"工具类 {self.__class__.__name__} 缺少必要参数: {param_name}")

        return await self.execute(function_args)

    def get_config(self, key: str, default=None):
        """获取插件配置值，使用嵌套键访问

        Args:
            key: 配置键名，使用嵌套访问如 "section.subsection.key"
            default: 默认值

        Returns:
            Any: 配置值或默认值
        """
        if not self.plugin_config:
            return default

        # 支持嵌套键访问
        keys = key.split(".")
        current = self.plugin_config

        for k in keys:
            if isinstance(current, dict) and k in current:
                current = current[k]
            else:
                return default

        return current
