package cd.casic.plugin.register;

import cd.casic.plugin.BasePlugin;
import cd.casic.plugin.PluginClassLoaderCache;
import cd.casic.plugin.PluginInfo;
import cd.casic.plugin.annotation.AdminGroups;
import cd.casic.plugin.annotation.InterceptPath;
import cd.casic.plugin.register.group.AnnotationUtils;
import cd.casic.plugin.register.group.ClassGroupHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Mapper;
import org.pf4j.PluginWrapper;
import org.pf4j.util.FileUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RestController;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.stream.Collectors;

/**
 * @Author：mianbin
 * @Package：cd.casic.plugin.register
 * @Project：ops
 * @name：AnnotationHandler
 * @Date：2024/03/18 15:59
 * @Filename：AnnotationHandler
 * @deprecated 不再使用，改用{@link ClassGroupHandler} 实现扫包后分组存储。
 */
@Slf4j
@Deprecated
public class AnnotationHandler implements BasePluginHandler {
    private final static Class<?>[] REGISTER_ANNO = {
            Bean.class,
            Mapper.class,
            Service.class,
            Component.class,
            Repository.class,
            Controller.class,
            Configuration.class,
            RestController.class,
            InterceptPath.class,
    };

    @Override
    public void initialize() throws Exception {

    }

    @Override
    public void registry(PluginInfo plugin) throws Exception {
        List<Class<?>> classList = new ArrayList<>();
        List<Class<?>> adminGroupsClassList = new ArrayList<>();
        Set<String> classPackageName = scanClassPackageName(plugin.getBasePlugin().scanPackage(), plugin.getBasePlugin().getWrapper());
        for (String packageName : classPackageName) {
            ClassLoader loader = PluginClassLoaderCache.getPlugin(plugin.getPluginId());
            log.info("Load class {} using classloader {} for plugin {}", packageName, PluginClassLoaderCache.getPlugin(plugin.getPluginId()), plugin.getPluginId());
            Class<?> clazz = loader.loadClass(packageName);
//            Class<?> clazz = plugin.getPluginWrapper().getPluginClassLoader().loadClass(packageName);
//            log.info("Load class {} using classloader {} for plugin {}", packageName, plugin.getPluginWrapper().getPluginClassLoader(), plugin.getPluginId());
            if (!BasePlugin.class.isAssignableFrom(clazz)) {
                classList.add(clazz);
            }
            AdminGroups annotation = clazz.getAnnotation(AdminGroups.class);
            if (annotation != null) {
                adminGroupsClassList.add(clazz);
            }
        }
        plugin.setClassList(classList);
        plugin.setAdminGroupsClassList(adminGroupsClassList);
        List<Class<?>> pluginClassList = plugin.getClassList().stream().filter(item -> !item.isInterface()).collect(Collectors.toList());
        if (!pluginClassList.isEmpty()) {
            List<Class<?>> registryClassList = new ArrayList<>();
            for (Class<?> aClass : pluginClassList) {
                // 原本这里使用Collections.disjoint()实现，是有问题的，结果总是true。
                // 因为aClass.getAnnotations()获得的是Proxy数组，而REGIS_ANNO是Class<Annotation>数组
                if(AnnotationUtils.hasAnnotations(aClass, false, Bean.class,
                        Mapper.class,
                        Service.class,
                        Component.class,
                        Repository.class,
                        Controller.class,
                        Configuration.class,
                        RestController.class,
                        InterceptPath.class)){
                    registryClassList.add(aClass);
                }
            }
            if (!registryClassList.isEmpty()) {
                plugin.getPluginApplicationContext().register(registryClassList.toArray(new Class[0]));
                // plugin.getBeanCache().putAll(BeanUtils.getTempBeans(registryClassList));
            }
        }
    }

    @Override
    public void unRegistry(PluginInfo plugin) throws Exception {
        // 不做操作，直接通过关闭PluginApplicationContext完成注销
    }

    /**
     * 扫描jar包中的类。
     *
     * @param basePackage   包名
     * @param pluginWrapper jar的PluginWrapper
     * @return 类全路径
     * @throws IOException 扫描异常
     */
    public static Set<String> scanClassPackageName(String basePackage, PluginWrapper pluginWrapper) throws IOException {
        Path pluginPath = pluginWrapper.getPluginPath();
        Set<String> classPackageNames = new HashSet<>();
        File jarFile = null;
        if(Files.isDirectory(pluginPath)){
            List<File> jars = FileUtils.getJars(pluginPath);
            jarFile = jars.get(0);
        }else if(pluginPath.toFile().getName().toLowerCase().endsWith(".jar")){
            jarFile = pluginPath.toFile();
        }else {
            throw new RuntimeException("不正确的pluginPath");
        }
        try (JarFile jar = new JarFile(jarFile)) {
            Enumeration<JarEntry> jarEntries = jar.entries();
            while (jarEntries.hasMoreElements()) {
                JarEntry entry = jarEntries.nextElement();
                String jarEntryName = entry.getName();
                if (jarEntryName.contains(".class") && jarEntryName.replaceAll("/", ".").startsWith(basePackage)) {
                    String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replace("/", ".");
                    classPackageNames.add(className);
                }
            }
        }
        return classPackageNames;
    }

}
