package jnpf.cultivate.v2.config;

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * @author W
 */
@Slf4j
@Component
public class LocalDisableRocketMqListenerProcessor
        implements BeanDefinitionRegistryPostProcessor, EnvironmentAware, PriorityOrdered {

    private static final String NACOS_REGISTER_ENABLED =
            "spring.cloud.nacos.discovery.register-enabled";

    private static final String LOCAL_LISTENER_REGISTER_ENABLED =
            "local.listener.register-enabled";

    private Environment environment;

    @Override
    public void setEnvironment(@NotNull Environment environment) {
        this.environment = environment;
    }

    @Override
    public void postProcessBeanDefinitionRegistry(@NotNull BeanDefinitionRegistry registry) throws BeansException {
        if (!shouldDisableRocketMqListener()) {
            return;
        }

        List<String> removeBeanNames = new ArrayList<>();
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();

        for (String beanName : registry.getBeanDefinitionNames()) {
            BeanDefinition beanDefinition = registry.getBeanDefinition(beanName);
            String beanClassName = beanDefinition.getBeanClassName();

            if (!StringUtils.hasText(beanClassName)) {
                continue;
            }

            try {
                Class<?> beanClass = ClassUtils.forName(beanClassName, classLoader);

                RocketMQMessageListener listenerAnnotation =
                        AnnotationUtils.findAnnotation(beanClass, RocketMQMessageListener.class);

                if (listenerAnnotation != null) {
                    removeBeanNames.add(beanName);

                    log.info(
                            "Local mode detected, remove RocketMQ listener bean. beanName={}, group={}, topic={}",
                            beanName,
                            listenerAnnotation.consumerGroup(),
                            listenerAnnotation.topic()
                    );
                }
            } catch (Throwable ex) {
                log.error("Check RocketMQ listener bean failed. beanName={}, className={}",
                        beanName, beanClassName, ex);
            }
        }

        for (String beanName : removeBeanNames) {
            if (registry.containsBeanDefinition(beanName)) {
                registry.removeBeanDefinition(beanName);
            }
        }

        if (!removeBeanNames.isEmpty()) {
            log.info("RocketMQ listeners disabled because {}=false, removed listener count={}",
                    NACOS_REGISTER_ENABLED,
                    removeBeanNames.size());
        }
    }

    @Override
    public void postProcessBeanFactory(@NotNull ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 不需要处理
    }

    @Override
    public int getOrder() {
        return HIGHEST_PRECEDENCE;
    }

    private boolean shouldDisableRocketMqListener() {
        //本地启用消费
        BindResult<Boolean> localListenerResult = Binder.get(environment)
                .bind(LOCAL_LISTENER_REGISTER_ENABLED, Boolean.class);
        if (localListenerResult.isBound() && Boolean.TRUE.equals(localListenerResult.get())) {
            return false;
        }
        BindResult<Boolean> bindResult = Binder.get(environment)
                .bind(NACOS_REGISTER_ENABLED, Boolean.class);
        // 要求：配置存在，并且值为 false 时才禁用
        return bindResult.isBound() && Boolean.FALSE.equals(bindResult.get());
    }
}
