package cd.casic.plugin;

import cd.casic.plugin.utils.PluginsUtils;
import cn.hutool.setting.dialect.Props;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.pf4j.PluginWrapper;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.web.servlet.HandlerInterceptor;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Author：mianbin
 * @Package：cd.casic.plugin
 * @Project：ops
 * @name：PluginDetail
 * @Date：2024/03/18 9:34
 * @Filename：PluginDetail
 * @description：插件信息类
 */
@Slf4j
@Data
public class PluginInfo {

    // jar或zip的list集合
    private List<Class<?>> classList;
    private ApplicationContext mainApplicationContext;
    private Boolean applicationContextIsRefresh = false;
    private AnnotationConfigApplicationContext pluginApplicationContext;
    private PluginWrapper pluginWrapper;
    private List<Class<?>> adminGroupsClassList = new ArrayList<>();
    private List<String> websocketPaths = new ArrayList<>();
    private String pluginId;
    private String mapperXmlDir;
    private final BasePlugin basePlugin;
    private List<HandlerInterceptor> handlerInterceptorList = new ArrayList<>();
    private Set<String> staticClassPathLocations = new HashSet<>();
    private Set<String> staticFileLocations = new HashSet<>();
    private List<Class<?>> controllers = new ArrayList<>();
    private Set<Object> pluginConfigObjects = new HashSet<>();
    private Map<String, String> webSocketPathMap = new ConcurrentHashMap<>();
    // private ConcurrentHashMap<Class<?>, Object> beanCache = new ConcurrentHashMap<>();

    // TODO 这个map用于替代前面的ClassList
    private Map<String, List<Class<?>>> classGroups = new ConcurrentHashMap<>();

    public PluginInfo(PluginWrapper pluginWrapper, ApplicationContext applicationContext) {
        this.classList = new ArrayList<>();
        this.pluginWrapper = pluginWrapper;
        this.pluginId = pluginWrapper.getPluginId();
        this.pluginApplicationContext = getContext();
        this.mainApplicationContext = applicationContext;
        this.basePlugin = (BasePlugin) pluginWrapper.getPlugin();
        this.pluginApplicationContext.setParent(mainApplicationContext);
        Props setting = PluginsUtils.getSetting(pluginWrapper.getPluginId());
        if (!setting.isEmpty()) {
            this.mapperXmlDir = setting.getStr("mybatis.mapper.location", null);
            String locations = setting.getStr("static.locations", null);
            if (StringUtils.isNotBlank(locations)) {
                loadResources(locations);
            }
        }
    }


    public AnnotationConfigApplicationContext getContext() {
        AnnotationConfigApplicationContext pluginApplicationContext =
                PluginApplicationContextManager.getApplicationContext(pluginWrapper.getPluginId());
        if (pluginApplicationContext == null)
            pluginApplicationContext = new AnnotationConfigApplicationContext();
        pluginApplicationContext.setClassLoader(pluginWrapper.getPluginClassLoader());
        PluginApplicationContextManager.addPluginApplicationContext(pluginWrapper.getPluginId(), pluginApplicationContext);
        return PluginApplicationContextManager.getApplicationContext(pluginWrapper.getPluginId());
    }

    private void loadResources(String locations) {
        String[] staticLocations = locations.split(",");
        for (String staticLocation : staticLocations) {
            if (staticLocation.contains("classpath:")) {
                staticLocation = staticLocation.replace("classpath:", "");
                if (StringUtils.isNotBlank(staticLocation) && staticLocation.startsWith("/")) {
                    staticLocation = staticLocation.substring(1);
                }
                this.staticClassPathLocations.add(staticLocation);
            } else {
                this.staticFileLocations.add(staticLocation);
            }
        }
    }

    public String getMapperXmlDir() {
        if (StringUtils.isNotBlank(mapperXmlDir) && mapperXmlDir.startsWith("classpath:")) {
            mapperXmlDir = mapperXmlDir.replace("classpath:", "");
        }
        if (StringUtils.isNotBlank(mapperXmlDir) && mapperXmlDir.startsWith("/")) {
            mapperXmlDir = mapperXmlDir.substring(1);
        }
        return mapperXmlDir;
    }

    /**
     * 清理ApplicationContext
     */
    public void clearApplicationContext() {
        Optional.of(this.getPluginId())
                .ifPresent(var -> PluginApplicationContextManager.removePluginApplicationContext(var.trim()));
        Optional.of(this.getPluginApplicationContext())
                .ifPresent(var -> {
                    var.getDefaultListableBeanFactory().destroySingletons();
                    var.close();
                });
        this.applicationContextIsRefresh = false;
        this.pluginApplicationContext = null;
    }

    /**
     * @param c class
     * @return java.util.List<java.lang.Class < ?>>
     * @description 获取插件内实现指定类的bean
     * @author dolphin
     */
    public <T> T getPluginBean(Class<T> c) {
        try {
            return pluginApplicationContext.getBean(c);
        } catch (Exception e) {
            log.error("要删除的bean：{}不存在" , c.getName());
            return null;
        }
    }

    public void addController(Class<?> controller) {
        this.controllers.add(controller);
    }

    public void addPluginConfigObject(Object config) {
        this.pluginConfigObjects.add(config);
    }

    public void addGroupClass(String groupName, Class<?> clazz) {
        classGroups.computeIfAbsent(groupName, k -> new ArrayList<>()).add(clazz);
    }

    public List<Class<?>> getGroupClass(String groupName) {
        List<Class<?>> result = new ArrayList<>();
        List<Class<?>> classes = classGroups.get(groupName);
        if (classes != null) {
            result.addAll(classes);
        }
        return result;
    }
}
