package com.cf.imes.framework.organ.config;

import com.cf.imes.framework.common.enums.WebFilterOrderEnum;
import com.cf.imes.framework.mybatis.core.util.MyBatisUtils;
import com.cf.imes.framework.redis.config.ChenfengCacheProperties;
import com.cf.imes.framework.organ.core.aop.OrganIgnoreAspect;
import com.cf.imes.framework.organ.core.db.OrganDatabaseInterceptor;
import com.cf.imes.framework.organ.core.job.OrganJobAspect;
import com.cf.imes.framework.organ.core.mq.rabbitmq.OrganRabbitMQInitializer;
import com.cf.imes.framework.organ.core.mq.redis.OrganRedisMessageInterceptor;
import com.cf.imes.framework.organ.core.mq.rocketmq.OrganRocketMQInitializer;
import com.cf.imes.framework.organ.core.redis.OrganRedisCacheManager;
import com.cf.imes.framework.organ.core.security.OrganSecurityWebFilter;
import com.cf.imes.framework.organ.core.service.OrganFrameworkService;
import com.cf.imes.framework.organ.core.service.OrganFrameworkServiceImpl;
import com.cf.imes.framework.organ.core.web.OrganContextWebFilter;
import com.cf.imes.framework.web.config.WebProperties;
import com.cf.imes.framework.web.core.handler.GlobalExceptionHandler;
import com.cf.imes.module.system.api.organ.OrganApi;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.BatchStrategies;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.Objects;

@AutoConfiguration
@ConditionalOnProperty(prefix = "chenfeng.organ", value = "enable", matchIfMissing = true) // 允许使用 chenfeng.organ.enable=false 禁用多组织
@EnableConfigurationProperties(OrganProperties.class)
public class ChenfengOrganAutoConfiguration {

    @Bean
    public OrganFrameworkService tenantFrameworkService(OrganApi organApi) {
        return new OrganFrameworkServiceImpl(organApi);
    }

    // ========== AOP ==========

    @Bean
    public OrganIgnoreAspect organIgnoreAspect() {
        return new OrganIgnoreAspect();
    }

    // ========== DB ==========

    @Bean
    public TenantLineInnerInterceptor tenantLineInnerInterceptor(OrganProperties properties,
                                                                 MybatisPlusInterceptor interceptor) {
        TenantLineInnerInterceptor inner = new TenantLineInnerInterceptor(new OrganDatabaseInterceptor(properties));
        // 添加到 interceptor 中
        // 需要加在首个，主要是为了在分页插件前面。这个是 MyBatis Plus 的规定
        MyBatisUtils.addInterceptor(interceptor, inner, 0);
        return inner;
    }

    // ========== WEB ==========

    @Bean
    public FilterRegistrationBean<OrganContextWebFilter> organContextWebFilter() {
        FilterRegistrationBean<OrganContextWebFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new OrganContextWebFilter());
        registrationBean.setOrder(WebFilterOrderEnum.TENANT_CONTEXT_FILTER);
        return registrationBean;
    }

    // ========== Security ==========

    @Bean
    public FilterRegistrationBean<OrganSecurityWebFilter> organSecurityWebFilter(OrganProperties tenantProperties,
                                                                                  WebProperties webProperties,
                                                                                  GlobalExceptionHandler globalExceptionHandler,
                                                                                  OrganFrameworkService organFrameworkService) {
        FilterRegistrationBean<OrganSecurityWebFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new OrganSecurityWebFilter(tenantProperties, webProperties,
                globalExceptionHandler, organFrameworkService));
        registrationBean.setOrder(WebFilterOrderEnum.TENANT_SECURITY_FILTER);
        return registrationBean;
    }

    // ========== Job ==========

    @Bean
    @ConditionalOnClass(name = "com.xxl.job.core.handler.annotation.XxlJob")
    public OrganJobAspect organJobAspect(OrganFrameworkService organFrameworkService) {
        return new OrganJobAspect(organFrameworkService);
    }

    // ========== MQ ==========

    /**
     * 多组织 Redis 消息队列的配置类
     *
     * 为什么要单独一个配置类呢？如果直接把 TenantRedisMessageInterceptor Bean 的初始化放外面，会报 RedisMessageInterceptor 类不存在的错误
     */
    @Configuration
    @ConditionalOnClass(name = "com.cf.imes.framework.mq.redis.core.RedisMQTemplate")
    public static class OrganRedisMQAutoConfiguration {

        @Bean
        public OrganRedisMessageInterceptor organRedisMessageInterceptor() {
            return new OrganRedisMessageInterceptor();
        }

    }

    @Bean
    @ConditionalOnClass(name = "org.springframework.amqp.rabbit.core.RabbitTemplate")
    public OrganRabbitMQInitializer organRabbitMQInitializer() {
        return new OrganRabbitMQInitializer();
    }

    @Bean
    @ConditionalOnClass(name = "org.apache.rocketmq.spring.core.RocketMQTemplate")
    public OrganRocketMQInitializer organRocketMQInitializer() {
        return new OrganRocketMQInitializer();
    }

    // ========== Redis ==========

    @Bean
    @Primary // 引入组织时，tenantRedisCacheManager 为主 Bean
    public RedisCacheManager organRedisCacheManager(RedisTemplate<String, Object> redisTemplate,
                                                     RedisCacheConfiguration redisCacheConfiguration,
                                                     ChenfengCacheProperties chenfengCacheProperties) {
        // 创建 RedisCacheWriter 对象
        RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory());
        RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory,
                BatchStrategies.scan(chenfengCacheProperties.getRedisScanBatchSize()));
        // 创建 TenantRedisCacheManager 对象
        return new OrganRedisCacheManager(cacheWriter, redisCacheConfiguration);
    }
}
