# core/plugin.py
from abc import ABC, abstractmethod
import importlib
from enum import Enum
import os,sys
import pkgutil
import inspect
from typing import Dict, List, Type

sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
from core.behavior_tree import ReleaseFlowAction
from core.defines import ReleasePhase, ProcessStep, Dict
import core.logger as logger
log = logger.get_logger()

"""
# format: PluginName.ReleaseStep.ProcessStep
ReleaseFlowCommon.StepEnv.Pre
"""

class PluginType(str, Enum):
    """
    为插件分类，目前仅有一种插件，FLOW类型的插件用于执行Release Flow
    """
    FLOW = 'flow'

class PluginInterface(ABC):
    """PluginInterface 插件基类"""
    def __init__(self, type: PluginType):
        self.type = type

    def get_type(self) -> PluginType:
        return self.type
    
    @abstractmethod
    def get_version(self) -> str:
        pass
    
    @abstractmethod
    def get_description(self) -> str:
        """返回插件描述"""
        pass

class ReleaseFlowPlugin(PluginInterface, ABC):
    """ReleaseFlowPlugin 插件基类"""
    def __init__(self):
        PluginInterface.__init__(self, PluginType.FLOW)
        self.btrees:Dict = Dict()

    @abstractmethod
    def get_version(self) -> str:
        """返回插件版本"""
        pass

    @abstractmethod
    def get_description(self) -> str:
        """返回插件描述"""
        pass
    # 支持使用.操作符访问btrees
    def __getattr__(self, name: str) -> Dict:
        return self.btrees[name]

def ReleaseFlowPluginDecorator(cls):
    """装饰器，用于自动注册ReleaseFlow插件， 每个 ReleaseFlowPlugin 插件上加上这个装饰器， 插件初始化时会自动搜索并注册行为树
    Args:
        cls: 插件类
    Returns:
        插件类
    """
    original_init = cls.__init__
    # 如果不是ReleaseFlowPlugin的子类，则不进行装饰
    if not issubclass(cls, ReleaseFlowPlugin):
        return cls
    def behavior_tree_register(self: ReleaseFlowPlugin):
        """
        搜索当前目录下的所有行为树节点，并根据命名注册到btrees中
        """
        def _load_actions_from_package(self, package_name: str):
            """从包中加载插件"""
            package = importlib.import_module(package_name)
            for _, name, is_pkg in pkgutil.iter_modules(package.__path__, f"{package_name}."):
                if not is_pkg:
                    _load_actions(self, name)
                else:
                    _load_actions_from_package(self, f"{package_name}.{name}")
        def _load_actions(self, module_name: str):
            """加载单个behavior action"""
            # log.debug(f"Try to check action: {module_name}")
            module = importlib.import_module(module_name)
            # 遍历模块中的所有属性名称
            for attribute_name in dir(module):
                attribute = getattr(module, attribute_name)
                if not isinstance(attribute, type): # 检查是否是类
                    continue
                if not issubclass(attribute, ReleaseFlowAction): # 检查是否是插件类
                    continue
                if inspect.isabstract(attribute): # 检查是否是抽象类
                    continue
                log.info(f"Loaded action: {attribute.__name__}")
                self.sub_actions[attribute.__name__] = attribute
        def discover_actions(self:ReleaseFlowPlugin):
            module = inspect.getmodule(self)
            path = os.path.relpath(os.path.dirname(module.__file__)).replace('/', '.')
            # log.debug(f"Try to search action in: {path}")
            self.sub_actions = {}
            for _, name, is_pkg in pkgutil.iter_modules([os.path.dirname(module.__file__)]):
                # log.trace(f"Try to load action: {name}")
                if is_pkg:
                    _load_actions_from_package(self, f"{path}.{name}")
                else:
                    _load_actions(self, f"{path}.{name}")

        if not isinstance(self, ReleaseFlowPlugin):
            log.warning(f"Plugin class {self.__class__.__name__} is not a ReleaseFlowPlugin")
            raise ReleaseErr("The plugin class decorator is not a ReleaseFlowPlugin")
        discover_actions(self)
        for phase in ReleasePhase:
            phase = phase.value
            self.btrees[phase] = Dict()
            for step in ProcessStep:
                step = step.value
                action_class = f"_{phase}_{step}"
                if not self.sub_actions.get(action_class):
                    log.trace(f"Action {action_class} not found")
                    continue
                log.debug(f"Register ReleaseFlow behavior tree node: {action_class}")
                if not self.btrees[phase].get(step):
                    self.btrees[phase][step] = self.sub_actions[action_class]
                else:
                    log.warning(f"Behavior tree node {action_class} already exists: {self.btrees[phase][step]}")
    def new_init(self, *args, **kwargs):
        original_init(self, *args, **kwargs)
        behavior_tree_register(self)
    # 添加标记，表示该类使用了flow action 装饰器，用于检查是否注册了行为树
    cls._is_action_registered = True
    cls.__init__ = new_init
    return cls

class PluginManager:
    """插件管理器"""
    def __init__(self, plugin_dir: str = "plugins"):
        self.plugin_dir = plugin_dir
        self.plugins: Dict[str, Type[PluginInterface]] = {}
        self.loaded_plugins: Dict[str, PluginInterface] = {}
        self.discover_plugins()
    
    def discover_plugins(self):
        """发现并加载所有插件"""
        for _, name, is_pkg in pkgutil.iter_modules([self.plugin_dir]):
            plugin_dir = f"{self.plugin_dir}.{name}"
            log.trace(f"Try to search plugin in: {plugin_dir}")
            if is_pkg:
                self._load_plugins_from_package(plugin_dir)
            else:
                self._load_plugin(plugin_dir)
    
    def _load_plugins_from_package(self, package_name: str):
        """从包中加载插件"""
        package = importlib.import_module(package_name)
        for _, name, is_pkg in pkgutil.iter_modules(package.__path__, f"{package_name}."):
            if not is_pkg:
                self._load_plugin(name)
            else:
                self._load_plugins_from_package(f"{package_name}.{name}")
    
    def _load_plugin(self, module_name: str):
        """加载单个插件"""
        module = importlib.import_module(module_name)
        for attribute_name in dir(module):
            plugin_class = getattr(module, attribute_name)
            if not isinstance(plugin_class, type): # 检查是否是类
                continue
            if not issubclass(plugin_class, PluginInterface): # 检查是否是插件类
                continue
            if inspect.isabstract(plugin_class): # 检查是否是抽象类
                continue
            # 检查插件类是否使用了 ReleaseFlowActionRegister 装饰器
            if issubclass(plugin_class, ReleaseFlowPlugin) and not hasattr(plugin_class, '_is_action_registered'):
                log.warning(f"Plugin class {plugin_class.__name__} does not have the ReleaseFlowActionRegister decorator.")
                continue
            
            log.info(f"Loaded plugin: {plugin_class.__name__}")
            self.plugins[plugin_class.__name__] = plugin_class
                
    def get_plugin(self, name: str) -> PluginInterface:
        """获取插件实例, 每个插件实例只初始化一次
        Args:
            name: 插件名称
        Returns:
            插件实例
        """
        if name not in self.loaded_plugins:
            if name in self.plugins:
                self.loaded_plugins[name] = self.plugins[name]()
            else:
                raise ValueError(f"Plugin {name} not found")
        return self.loaded_plugins[name]
    
    
    def get_available_plugins(self) -> List[str]:
        """获取所有可用插件名称"""
        return list(self.plugins.keys())

    def __str__(self) -> str:
        string = ""
        string += f"Plugin directory: {self.plugin_dir}\n"
        string += "Available plugins:"
        string += "[" + "  \n".join(self.plugins.keys()) + "]\n"
        string += "Loaded plugins:"
        string += "[" + "  \n".join(self.loaded_plugins.keys()) + "]\n"
        return string

    def __repr__(self) -> str:
        return str(self)

    def __getattr__(self, name: str) -> PluginInterface:
        # 获取插件实例
        plugin = self.get_plugin(name)
        return plugin