package cd.casic.plugin.core.register.mybatis;

import cd.casic.plugin.dataobject.pojo.PluginInfo;
import cd.casic.plugin.utils.ResourceUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
import org.springframework.util.ClassUtils;

import java.io.IOException;
import java.util.*;

/**
 * @description: 插件资源发现
 * @author: mianbin
 * @date: 2025/5/22 19:45
 * @version: 1.0
 */
@Slf4j
@RequiredArgsConstructor
public class PluginResourceFinder {

    private final ClassLoader classLoader;
    private final ResourcePatternResolver resourcePatternResolver;
    private final MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();

    public PluginResourceFinder(PluginInfo pluginInfo) {
        this.classLoader = pluginInfo.getPluginWrapper().getPluginClassLoader();
        this.resourcePatternResolver = new PathMatchingResourcePatternResolver(classLoader);
    }

    /**
     * 获取插件中xml资源
     * @param xmlLocationsMatchSet xml资源匹配集合
     * @return xml Resource 数组
     * @throws IOException 获取xml资源异常
     */
    public Resource[] getXmlResource(Set<String> xmlLocationsMatchSet) throws IOException {
        if(xmlLocationsMatchSet == null || xmlLocationsMatchSet.isEmpty()){
            return null;
        }
        List<Resource> resources = new ArrayList<>();
        for (String xmlLocationsMatch : xmlLocationsMatchSet) {
            if(xmlLocationsMatchSet.isEmpty()){
                continue;
            }
            List<Resource> loadResources = getXmlResources(xmlLocationsMatch);
            if(loadResources != null && !loadResources.isEmpty()){
                resources.addAll(loadResources);
            }
        }

        if(resources.isEmpty()){
            return null;
        }
        return resources.toArray(new Resource[0]);
    }



    /**
     * 获取插件的实体类及其别名
     * @param packagePatterns 实体类包名
     * @return class 数组
     * @throws IOException 获取医院异常
     */
    public Class<?>[] getAliasesClasses(Set<String> packagePatterns) throws IOException {
        if(packagePatterns == null || packagePatterns.isEmpty()){
            return null;
        }
        Set<Class<?>> aliasesClasses = new HashSet<>();
        for (String packagePattern : packagePatterns) {
            Resource[] resources = resourcePatternResolver.getResources(
                    ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                            + ClassUtils.convertClassNameToResourcePath(packagePattern) + "/**/*.class");
            for (Resource resource : resources) {
                try {
                    ClassMetadata classMetadata = metadataReaderFactory.getMetadataReader(resource).getClassMetadata();
                    Class<?> clazz = classLoader.loadClass(classMetadata.getClassName());
                    aliasesClasses.add(clazz);
                } catch (Throwable e) {
                    log.warn("Cannot load the '{}'. Cause by {}", resource, e.toString());
                }
            }
        }
        return aliasesClasses.toArray(new Class<?>[0]);
    }

    /**
     * 获取Xml资源
     * @param mybatisMapperXmlLocationMatch mybatis xml 批量规则
     * @return 匹配到的xml资源
     * @throws IOException IO 异常
     */
    private List<Resource> getXmlResources(String mybatisMapperXmlLocationMatch) throws IOException {
        String matchLocation = ResourceUtils.getMatchLocation(mybatisMapperXmlLocationMatch);
        if(matchLocation == null){
            log.error("mybatisMapperXmlLocation {} illegal", mybatisMapperXmlLocationMatch);
            return null;
        }
        try {
            Resource[] resources = resourcePatternResolver.getResources(matchLocation);
            if(resources.length > 0){
                return Arrays.asList(resources);
            } else {
                return null;
            }
        } catch (IOException e) {
            log.error("mybatis xml resource '{}' match error : {}", mybatisMapperXmlLocationMatch,
                    e.getMessage(), e);
            throw e;
        }
    }

}
