package link.at17.mid.tushare.component;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.AssignableTypeFilter;

import link.at17.mid.tushare.annotation.ConfigInfo;
import link.at17.mid.tushare.interfaces.IConfig;
import lombok.extern.slf4j.Slf4j;

/**
 * 实现自动化注册 Config
 */
@Slf4j
@DependsOn("configService")
public class ConfigAutoRegistrar implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
        scanner.addIncludeFilter(new AssignableTypeFilter(IConfig.class));
        scanner.addIncludeFilter(new AnnotationTypeFilter(ConfigInfo.class));

        scanner.findCandidateComponents("link.at17.mid.tushare.system.config").forEach(beanDefinition -> {
            String className = beanDefinition.getBeanClassName();
            try {
                // 确保其 field 规则与 configService 内 field 生成规则一致，即：
                // 如果 @ConfigInfo 指定了 field 的，使用该 field + "Config"
                // 作为 beanName，否则使用首字母小写的 simpleClassName 作为
                // beanName，且 simpleClassName 无论如何必须以 Config 作为结尾。
                Class<?> clazz = Class.forName(className);
                String beanName = clazz.getSimpleName().substring(0, 1).toLowerCase()
                        + clazz.getSimpleName().substring(1);

                if (!IConfig.class.isAssignableFrom(clazz)) {
                    log.warn("Config {} does not implement IConfig, ignore", beanName);
                    return;
                }
                
                if (!beanName.endsWith("Config")) {
                    log.warn("Config {}'s simple class name does not end with \"Config\", ignore", beanName);
                    return;
                }
                
                ConfigInfo info = clazz.getAnnotation(ConfigInfo.class);
                if (info == null) {
                    log.warn("Config {} does not have @ConfigInfo annotation, ignore", clazz.getName());
                    return;
                }
                if (StringUtils.isNotBlank(info.field())) {
                    beanName = info.field() + "Config";
                }
                
                BeanDefinitionBuilder factoryBean = BeanDefinitionBuilder
                        .genericBeanDefinition(ConfigServiceFactoryBean.class)
                        .addConstructorArgValue(clazz);
                // 注意此处注册 factoryBean 不意味着 FactoryBean.getObject() 方法立即被执行，
                // Spring 管理的 Bean 默认在其被使用时才创建，所以如果 getObject() 调用一些方
                // 法，这些方法会在初次使用 Bean 时才被创建。如果这些方法对于启动过程很重要，
                // 需要在对应 Config(Bean) 上加上 @Bean 和 @Lazy(false) 注解，确保一旦准备好
                // 相应的 Bean 就会被创建。
                registry.registerBeanDefinition(beanName, factoryBean.getBeanDefinition());
                log.info("Add config {} to bean register", beanName);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException("Failed to load class: " + className, e);
            }
        });
    }
}