package cn.reghao.tnb.admin.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.lettuce.core.ReadFrom;
import io.lettuce.core.resource.DefaultClientResources;
import jakarta.annotation.PreDestroy;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.util.HashSet;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author reghao
 * @date 2026-01-23 14:25:52
 */
@Slf4j
//@Configuration
public class RedisConfig {
    @Resource
    private RedisProperties redisProperties;
    private DefaultClientResources clientResources;

    /**
     * 构建commonsPool配置
     *
     * @return .
     */
    @Bean
    public GenericObjectPoolConfig<?> genericObjectPoolConfig() {
        GenericObjectPoolConfig<?> config = new GenericObjectPoolConfig<>();
        config.setMinIdle(redisProperties.getLettuce().getPool().getMinIdle());
        config.setMaxIdle(redisProperties.getLettuce().getPool().getMaxIdle());
        config.setMaxTotal(redisProperties.getLettuce().getPool().getMaxActive());
        config.setMaxWait(redisProperties.getLettuce().getPool().getMaxWait());
        return config;
    }

    /**
     * 构建RedisSentinel配置
     *
     * @return .
     */
    @Bean
    public RedisSentinelConfiguration sentinelConfiguration() {
        List<String> nodeList = redisProperties.getSentinel().getNodes();
        RedisSentinelConfiguration redisSentinelConfiguration =
                new RedisSentinelConfiguration(redisProperties.getSentinel().getMaster(), new HashSet<>(nodeList));
        // 主节点密码
        redisSentinelConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword()));
        // 哨兵密码
        redisSentinelConfiguration.setSentinelPassword(RedisPassword.of(redisProperties.getSentinel().getPassword().toCharArray()));
        return redisSentinelConfiguration;
    }

    /**
     * 构建lettuce连接池
     *
     * @param genericObjectPoolConfig .
     * @param sentinelConfiguration   .
     * @return .
     */
    @Bean
    public RedisConnectionFactory lettuceConnectionFactory(GenericObjectPoolConfig<?> genericObjectPoolConfig,
                                                           RedisSentinelConfiguration sentinelConfiguration) {
        // 声明资源
        this.clientResources = DefaultClientResources.create();

        // 构建lettuce配置
        LettucePoolingClientConfiguration lettuceClientConfiguration =
                LettucePoolingClientConfiguration.builder()
                        .poolConfig(genericObjectPoolConfig)
                        .readFrom(ReadFrom.REPLICA)
                        .clientResources(clientResources)
                        .build();

        // 构建lettuce连接池
        LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(sentinelConfiguration, lettuceClientConfiguration);
        // 每次获取连接时验证连接的有效性(注意性能影响)
        lettuceConnectionFactory.setValidateConnection(true);
        // 手动触发连接池初始化
        lettuceConnectionFactory.afterPropertiesSet();

        log.info("connected to redis sentinel,node info: {}", sentinelConfiguration.getSentinels());
        return lettuceConnectionFactory;
    }

    /**
     * 应用关闭时触发
     */
    @PreDestroy
    public void shutdown() {
        if (null != clientResources) {
            clientResources.shutdown(100, 100, TimeUnit.MILLISECONDS);
        }
    }

    /**
     * 1.使用自定义lettuce连接池
     * 2.声明序列化方式
     *
     * @param connectionFactory .
     * @return .
     */
    @Bean
    @SuppressWarnings("all")
    public RedisTemplate<String, Object> redisTemplate(@Qualifier("lettuceConnectionFactory") RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);

        // 配置String的序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        // 配置Json的序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        // Key采用String的序列化方式
        redisTemplate.setKeySerializer(stringRedisSerializer);
        // Hash的key也采用String的序列化方式
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        // Value序列化方式采用jackson
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        // Hash的value序列化方式采用jackson
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * 1.使用自定义lettuce连接池
     * 2.声明序列化方式
     *
     * @param connectionFactory .
     * @return .
     */
    @Bean
    public StringRedisTemplate stringRedisTemplate(@Qualifier("lettuceConnectionFactory") RedisConnectionFactory connectionFactory) {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

        stringRedisTemplate.setConnectionFactory(connectionFactory);
        stringRedisTemplate.setKeySerializer(stringRedisSerializer);
        stringRedisTemplate.setValueSerializer(stringRedisSerializer);

        stringRedisTemplate.afterPropertiesSet();
        return stringRedisTemplate;
    }
}
