package cd.casic.plugin.dataobject.pojo;

import cd.casic.plugin.core.BasePlugin;
import cd.casic.plugin.dataobject.dao.PluginFacadeMemoryCache;
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;

/**
 * @description: 重构了内容
 * @author: mianbin
 * @date: 2025/5/20 15:12
 * @version: 1.0
 */
@Data
@Slf4j
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) throws Exception {
        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);
        loadSettings();
    }

    /**
     * 从配置文件加载插件设置
     */
    private void loadSettings() throws Exception {
        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 = PluginFacadeMemoryCache.getPluginApplicationContext(pluginWrapper.getPluginId());
        if (pluginApplicationContext == null) {
            pluginApplicationContext = new AnnotationConfigApplicationContext();
            pluginApplicationContext.setClassLoader(pluginWrapper.getPluginClassLoader());
            PluginFacadeMemoryCache.putPluginApplicationContext(pluginWrapper.getPluginId(), pluginApplicationContext);
        }
        return PluginFacadeMemoryCache.getPluginApplicationContext(pluginWrapper.getPluginId());
    }

    private void loadResources(String locations) {
        String[] staticLocations = locations.split(",");
        for (String staticLocation : staticLocations) {
            String processedLocation = processResourceLocation(staticLocation);
            if (staticLocation.contains("classpath:")) {
                this.staticClassPathLocations.add(processedLocation);
            } else {
                this.staticFileLocations.add(processedLocation);
            }
        }
    }

    /**
     * 处理资源路径，去除 "classpath:" 前缀和开头的斜杠
     *
     * @param location 原始资源路径
     * @return 处理后的资源路径
     */
    private String processResourceLocation(String location) {
        if (location.contains("classpath:")) {
            location = location.replace("classpath:", "");
        }
        if (StringUtils.isNotBlank(location) && location.startsWith("/")) {
            location = location.substring(1);
        }
        return location;
    }

    public String getMapperXmlDir() {
        return processResourceLocation(mapperXmlDir);
    }

    /**
     * 清理ApplicationContext
     */
    public void clearApplicationContext() {
        PluginFacadeMemoryCache.removePlugin(this.getPluginId().trim());
        if (pluginApplicationContext != null) {
            pluginApplicationContext.getDefaultListableBeanFactory().destroySingletons();
            pluginApplicationContext.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) {
        if (pluginApplicationContext != null && pluginApplicationContext.containsBeanDefinition(c.getName())) {
            try {
                return pluginApplicationContext.getBean(c);
            } catch (Exception e) {
                // 记录日志，可根据实际情况添加日志框架
                log.error("获取插件 bean 失败，类型: " + c.getName() + ", 错误信息: " + e.getMessage());
            }
        }
        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) {
        return classGroups.getOrDefault(groupName, Collections.emptyList());
    }
}
