import os
import sys
import threading
import traceback

from framework import F


class PluginManager:
    plugin_list = {}
    plugin_menus = {}
    setting_menus = []


    @classmethod 
    def get_plugin_name_list(cls):
        #if not app.config['config']['auth_status']:
        #    return
        """ 
        plugin_path = os.path.join(frame.config['path_app'], 'plugins')
        sys.path.insert(0, plugin_path)
        from system import SystemModelSetting
        plugins = os.listdir(plugin_path)
        """
        plugins = []
        pass_include = []
        except_plugin_list = []

        #2019-07-17 
        if F.config.get('plugin_loading_only_devpath', None) != True:
            try:
                plugin_path = os.path.join(F.config['path_data'], 'plugins')
                if os.path.exists(plugin_path) == True and os.path.isdir(plugin_path) == True:
                    sys.path.insert(1, plugin_path)
                    tmps = os.listdir(plugin_path)
                    add_plugin_list = []
                    for t in tmps:
                        if not t.startswith('_') and os.path.isdir(os.path.join(plugin_path, t)):
                            add_plugin_list.append(t)
                    plugins = plugins + add_plugin_list
                    pass_include = pass_include + add_plugin_list
            except Exception as exception:
                F.logger.error('Exception:%s', exception)
                F.logger.error(traceback.format_exc())

        # 2018-09-04
        try:
            plugin_path = F.SystemModelSetting.get('plugin_dev_path')
            if plugin_path != '':
                if os.path.exists(plugin_path):
                    sys.path.insert(0, plugin_path)
                    tmps = os.listdir(plugin_path)
                    add_plugin_list = []
                    for t in tmps:
                        if not t.startswith('_')  and os.path.isdir(os.path.join(plugin_path, t)):
                            add_plugin_list.append(t)
                    plugins = plugins + add_plugin_list
                    pass_include = pass_include + add_plugin_list
        except Exception as exception:
            F.logger.error('Exception:%s', exception)
            F.logger.error(traceback.format_exc())

        # plugin_loading_list
        try:
            plugin_loading_list = F.config.get('plugin_loading_list', None)
            if plugin_loading_list != None and type(plugin_loading_list) == type([]):
                new_plugins = []
                for _ in plugins:
                    if _ in plugin_loading_list:
                        new_plugins.append(_)
                plugins = new_plugins
        except Exception as exception:
            F.logger.error('Exception:%s', exception)
            F.logger.error(traceback.format_exc())

        # plugin_except_list
        try:
            plugin_except_list = F.config.get('plugin_except_list', None)
            if plugin_except_list != None and type(plugin_except_list) == type([]):
                new_plugins = []
                for _ in plugins:
                    if _ not in plugin_except_list:
                        new_plugins.append(_)
                plugins = new_plugins
        except Exception as exception:
            F.logger.error('Exception:%s', exception)
            F.logger.error(traceback.format_exc())
        return plugins

    # menu, blueprint, plugin_info, plugin_load, plugin_unload
    @classmethod
    def plugin_init(cls):
        try:
            plugins = cls.get_plugin_name_list()
            plugins = sorted(plugins)

            F.logger.debug(plugins)
            for plugin_name in plugins:
                 
                #logger.debug(len(system.LogicPlugin.current_loading_plugin_list))
                #if plugin_name.startswith('_'):
                #    continue
                #if plugin_name == 'terminal' and platform.system() == 'Windows':
                #    continue
                #if plugin_name in except_plugin_list:
                #    F.logger.debug('Except plugin : %s' % frame.plugin_menu)
                #    continue
                F.logger.debug(f'[+] PLUGIN LOADING Start.. [{plugin_name}]') 
                entity = {'version':'3'}
                try:
                    mod = __import__('%s' % (plugin_name), fromlist=[])
                    mod_plugin_info = None
                    # 2021-12-31
                    #import system
                    #if plugin_name not in system.LogicPlugin.current_loading_plugin_list:
                    #    system.LogicPlugin.current_loading_plugin_list[plugin_name] = {'status':'loading'}
                     
                    try:
                        mod_plugin_info = getattr(mod, 'plugin_info')
                        entity['module'] = mod 
                        """
                        if 'category'  not in mod_plugin_info and 'category_name' in mod_plugin_info:
                            mod_plugin_info['category'] = mod_plugin_info['category_name']
                        if 'policy_point' in mod_plugin_info:
                            if mod_plugin_info['policy_point'] > app.config['config']['point']:
                                system.LogicPlugin.current_loading_plugin_list[plugin_name]['status'] = 'violation_policy_point'
                                continue
                        if 'policy_level' in mod_plugin_info:
                            if mod_plugin_info['policy_level'] > app.config['config']['level']:
                                system.LogicPlugin.current_loading_plugin_list[plugin_name]['status'] = 'violation_policy_level'
                                continue
                        if 'category' in mod_plugin_info and mod_plugin_info['category'] == 'beta':
                            if SystemModelSetting.get_bool('use_beta') == False:
                                system.LogicPlugin.current_loading_plugin_list[plugin_name]['status'] = 'violation_beta'
                                continue 
                        """
                    except Exception as exception:
                        #logger.error('Exception:%s', exception)
                        #logger.error(traceback.format_exc())  
                        
                        #mod_plugin_info = getattr(mod, 'setup')
                        F.logger.warning(f'[!] PLUGIN_INFO not exist : [{plugin_name}]')  
                    if mod_plugin_info == None:
                        try:
                            mod = __import__(f'{plugin_name}.setup', fromlist=['setup'])
                            entity['version'] = '4'
                        except Exception as e:
                            F.logger.error(f'Exception:{str(e)}')
                            F.logger.error(traceback.format_exc())
                            F.logger.warning(f'[!] NOT normal plugin : [{plugin_name}]')
                            #entity['version'] = 'not_plugin'


                    try:
                        if entity['version'] != '4':
                            mod_blue_print = getattr(mod, 'blueprint')
                            
                        else:
                            entity['setup_mod'] = mod
                            entity['P'] = getattr(mod, 'P')
                            mod_blue_print = getattr(entity['P'], 'blueprint')
                        if mod_blue_print: 
                            #if plugin_name in pass_include or is_include_menu(plugin_name):
                            F.app.register_blueprint(mod_blue_print)
                    except Exception as exception:
                        #logger.error('Exception:%s', exception)
                        #logger.error(traceback.format_exc())
                        F.logger.warning(f'[!] BLUEPRINT not exist : [{plugin_name}]') 
                    cls.plugin_list[plugin_name] = entity
                    #system.LogicPlugin.current_loading_plugin_list[plugin_name]['status'] = 'success'
                    #system.LogicPlugin.current_loading_plugin_list[plugin_name]['info'] = mod_plugin_info
                except Exception as exception:
                    F.logger.error('Exception:%s', exception)
                    F.logger.error(traceback.format_exc())
                    F.logger.debug('no blueprint')
            
            #from tool_base import d 
            #logger.error(d(system.LogicPlugin.current_loading_plugin_list))
            # 2021-07-01 모듈에 있는 DB 테이블 생성이 안되는 문제
            # 기존 구조 : db.create_all() => 모듈 plugin_load => celery task 등록 후 리턴
            # 변경 구조 : 모듈 plugin_load => db.create_all() => celery인 경우 리턴

            # plugin_load 를 해야 하위 로직에 있는 DB가 로딩된다.
            # plugin_load 에 db는 사용하는 코드가 있으면 안된다. (테이블도 없을 때 에러발생)
            try:   
                #logger.warning('module plugin_load in celery ')
                cls.plugin_list['mod']['module'].plugin_load()
            except Exception as exception:
                F.logger.debug(f'mod plugin_load error!!') 
                #logger.error('Exception:%s', exception)
                #logger.error(traceback.format_exc())

            # import가 끝나면 DB를 만든다.
            # 플러그인 로드시 DB 초기화를 할 수 있다.
            if not F.config['run_celery']:
                try: 
                    F.db.create_all()
                except Exception as exception:
                    F.logger.error('Exception:%s', exception)
                    F.logger.error(traceback.format_exc())
                    F.logger.debug('db.create_all error')

            if not F.config['run_flask']:
                # 2021-06-03 
                # 모듈의 로직에 있는 celery 함수는 등록해주어야한다.
                #try:
                #    logger.warning('module plugin_load in celery ')
                #    plugin_instance_list['mod'].plugin_load()
                #except Exception as exception:
                #    logger.error('module plugin_load error')
                #    logger.error('Exception:%s', exception)
                #    logger.error(traceback.format_exc())
                # 2021-07-01
                # db때문에 위에서 로딩함.
                return
            
            for key, entity in cls.plugin_list.items():
                try:
                    mod_plugin_load = None
                    if entity['version'] == '3':
                        mod_plugin_load = getattr(entity['module'], 'plugin_load')
                    elif entity['version'] == '4':
                        mod_plugin_load = getattr(entity['P'], 'plugin_load')
                    #if mod_plugin_load and (key in pass_include or is_include_menu(key)):
                    if mod_plugin_load:
                        def func(mod_plugin_load, key):
                            try:
                                F.logger.debug(f'[!] plugin_load threading start : [{key}]') 
                                #mod.plugin_load()
                                mod_plugin_load()
                                F.logger.debug(f'[!] plugin_load threading end : [{key}]') 
                            except Exception as exception:
                                F.logger.error('### plugin_load exception : %s', key)
                                F.logger.error('Exception:%s', exception)
                                F.logger.error(traceback.format_exc())
                        # mod는 위에서 로딩
                        if key != 'mod':
                            t = threading.Thread(target=func, args=(mod_plugin_load, key))
                            t.setDaemon(True)
                            t.start()
                        #if key == 'mod':
                        #    t.join()
                except Exception as exception:
                    F.logger.debug(f'[!] PLUGIN_LOAD function not exist : [{key}]') 
                    #logger.error('Exception:%s', exception)
                    #logger.error(traceback.format_exc())
                    #logger.debug('no init_scheduler') 
                try:
                    mod_menu = None
                    if entity['version'] == '3':
                        mod_menu = getattr(entity['module'], 'menu')
                    elif entity['version'] == '4':
                        mod_menu = getattr(entity['P'], 'menu')
                    
                    if mod_menu:# and (key in pass_include or is_include_menu(key)):
                        cls.plugin_menus[key]=  {'menu':mod_menu, 'match':False}
                    if entity['version'] == '4':
                        setting_menu = getattr(entity['P'], 'setting_menu')
                        if setting_menu != None:
                            cls.setting_menus.append(setting_menu)

                            
                except Exception as exception:
                    F.logger.debug('no menu')
            F.logger.debug('### plugin_load threading all start.. : %s ', len(cls.plugin_list))
            # 모든 모듈을 로드한 이후에 app 등록, table 생성, start

        except Exception as exception:
            F.logger.error('Exception:%s', exception)
            F.logger.error(traceback.format_exc())



    @classmethod
    def plugin_unload(cls):
        for key, entity in cls.plugin_list.items():
            try:
                if entity['version'] == '3':
                    mod_plugin_unload = getattr(entity['module'], 'plugin_unload')
                elif entity['version'] == '4':
                    mod_plugin_unload = getattr(entity['P'], 'plugin_unload')
                    
                #if plugin_name == 'rss':
                #    continue
                #mod_plugin_unload = getattr(mod, 'plugin_unload')
                if mod_plugin_unload:
                    mod_plugin_unload()
                    #mod.plugin_unload()
            except Exception as e:
                F.logger.error('module:%s', key)
                F.logger.error(f'Exception:{str(e)}')
                F.logger.error(traceback.format_exc())
        try:
            from system.setup import P
            P.plugin_unload()
        except Exception as e:
            F.logger.error(f'Exception:{str(e)}')
            F.logger.error(traceback.format_exc())
