package com.qmth.boot.data.upgrade.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.util.ClassUtils;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

public class ClassHelper {

    private static final Logger log = LoggerFactory.getLogger(ClassHelper.class);

    public static <T> List<T> findInterfaceInstances(Class<T> interfaceClass, String basePackage) {
        List<Class<?>> implClasses = ClassHelper.findInterfaceImpls(interfaceClass, basePackage);

        List<T> instances = new ArrayList<>();
        for (Class<?> clazz : implClasses) {
            try {
                Constructor<?> constructor = ClassUtils.getConstructorIfAvailable(clazz);
                if (constructor != null) {
                    instances.add((T) constructor.newInstance());
                }
            } catch (Exception e) {
                log.warn("{} class newInstance fail. {}", clazz.getSimpleName(), e.getMessage());
            }
        }

        return instances;
    }

    public static List<Class<?>> findInterfaceImpls(Class<?> interfaceClass, String basePackage) {
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
        scanner.addIncludeFilter(new AssignableTypeFilter(interfaceClass));

        List<Class<?>> impls = new ArrayList<>();
        scanner.findCandidateComponents(basePackage).forEach(bean -> {
            try {
                Class<?> clazz = ClassUtils.forName(Objects.requireNonNull(bean.getBeanClassName()), ClassUtils.getDefaultClassLoader());
                // 排除：接口和抽象类
                if (!clazz.isInterface() && !Modifier.isAbstract(clazz.getModifiers())) {
                    impls.add(clazz);
                }
            } catch (Exception e) {
                log.warn("{} class load fail. {}", bean.getBeanClassName(), e.getMessage());
            }
        });

        return impls;
    }

}
