# -*- coding: utf-8 -*-
"""
Author  : CraftPilot
"""

from .meta_helper import Singleton
from .const import *
from typing import Optional, Callable, Any
import functools


# MCP 工具注册表（全局）
_MCP_TOOL_REGISTRY = []


def get_mcp_tool_registry():
    """获取 MCP 工具注册表"""
    return _MCP_TOOL_REGISTRY


class craftmethod:
    """
    装饰器：将函数标记为 CraftPilot 方法，支持可选的 MCP 暴露。
    
    用法：
        # 方式 1：无参数（向下兼容）
        @craftmethod
        def my_func(...): ...
        
        # 方式 2：带参数（新功能）
        @craftmethod(expose_as_mcp=True, mcp_timeout=60)
        def my_func(...): ...
    
    参数：
        expose_as_mcp (bool): 是否暴露为 MCP 工具，默认 False
        mcp_timeout (int): MCP 工具执行超时时间（秒），默认 30
        mcp_category (str): MCP 工具分类，默认 "general"
        mcp_description (str): MCP 工具描述，默认使用函数 docstring
    """
    
    def __init__(
        self,
        func: Callable = None,
        *,
        expose_as_mcp: bool = False,
        mcp_timeout: int = 30,
        mcp_category: str = "general",
        mcp_description: str = None
    ):
        self._expose_as_mcp = expose_as_mcp
        self._mcp_timeout = mcp_timeout
        self._mcp_category = mcp_category
        self._mcp_description = mcp_description
        
        if func is not None:
            # 无参数调用方式：@craftmethod
            self._wrap_function(func)
        else:
            # 有参数调用方式：@craftmethod(...)
            self._func = None
    
    def __call__(self, *args, **kwargs):
        if self._func is None:
            # 装饰器带参数时，第一次调用接收被装饰的函数
            if len(args) == 1 and callable(args[0]) and not kwargs:
                self._wrap_function(args[0])
                return self
            else:
                raise TypeError("craftmethod decorator used incorrectly")
        else:
            # 作为静态方法被调用
            return self._func(*args, **kwargs)
    
    def _wrap_function(self, func: Callable):
        """包装函数并注册 MCP 工具（如果启用）"""
        self._func = func
        functools.update_wrapper(self, func)
        
        # 注册为 MCP 工具（如果启用）
        if self._expose_as_mcp:
            self._register_as_mcp_tool(func)
    
    def _register_as_mcp_tool(self, func: Callable):
        """将函数注册到 MCP 工具注册表"""
        tool_info = {
            "func": func,
            "name": func.__name__,
            "description": self._mcp_description or func.__doc__ or f"MCP tool: {func.__name__}",
            "timeout": self._mcp_timeout,
            "category": self._mcp_category,
            "source": "craftmethod",
            # 保存类型注解用于后续 DSON 类型转换
            "annotations": getattr(func, '__annotations__', {})
        }
        _MCP_TOOL_REGISTRY.append(tool_info)
    
    def __get__(self, obj, objtype=None):
        """支持作为类方法使用（类似 staticmethod 的行为）"""
        return self._func
    
    @property
    def __func__(self):
        """兼容 staticmethod 接口"""
        return self._func
    
    @property
    def __wrapped__(self):
        """支持 functools.wraps"""
        return self._func
    
    @__wrapped__.setter
    def __wrapped__(self, value):
        """允许 functools.update_wrapper 设置 __wrapped__ 属性"""
        # functools.update_wrapper 会尝试设置此属性，这里忽略设置
        # 因为我们已经通过 self._func 跟踪原始函数
        pass


class DsonMetaInfo:
    """some wrapped meta info in dson file"""
    def __init__(self, cls_name:str, if_in_server:bool = False, dson_main_path:str = None):
        self.cls_name = cls_name
        self.if_in_server = if_in_server
        if(if_in_server == False):
            self.plugin_name = dson_main_path
        else:
            self.plugin_name = None           

    def to_json(self):
        json_data = {dson_meta_clsname_tag: self.cls_name, dson_meta_if_in_server_tag: self.if_in_server}
        if not self.if_in_server:
            json_data[dson_meta_plugin_name] = self.plugin_name
        return json_data

    @staticmethod
    def from_json(json_data):
        clsname = json_data[dson_meta_clsname_tag]
        if_in_server = True
        if dson_meta_if_in_server_tag in json_data:
            if_in_server = json_data[dson_meta_if_in_server_tag]
        dson_main_path = None
        if not if_in_server:
            dson_main_path = json_data[dson_meta_plugin_name]
        return DsonMetaInfo(clsname, if_in_server, dson_main_path)

    @staticmethod
    def from_dson(json_data):
        """from full dson file json"""
        for _, json_value in json_data.items():
            if dson_meta_field_tag in json_value:
                return DsonMetaInfo.from_json(json_value[dson_meta_field_tag])
        raise Exception("can't find dson meta info ")


class DsonManager(metaclass=Singleton):
    def __init__(self):
        self.dsonMap = {}


class DsonMeta(type):
    def __init__(cls, *args, **kwargs):
        assert hasattr(cls, 'label')
        DsonManager().dsonMap[cls.__name__] = cls
        super().__init__(*args, **kwargs)


class DsonBase(metaclass=DsonMeta):
    label = "pda"
    if_in_server = False # whether this class in CraftPilot Server

    def __init__(self):
        pass
