package com.liquidnet.common.cache.redis.config;//package com.liquidnet.common.cache.redis.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
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 javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @author AnJiabin <anjiabin@zhengzai.tv>
 * @version V1.0
 * @Description: TODO
 * @class: RedisConfig
 * @Package com.liquidnet.common.cache.redis.config
 * @Copyright: LightNet @ Copyright (c) 2021
 * @date 2021/8/10 16:28
 */
@Slf4j
@Configuration
public class RedisConfig {

    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.password}")
    private String password;
    @Value("${spring.redis.lettuce.pool.max-active}")
    private int maxActive;
    @Value("${spring.redis.lettuce.pool.max-idle}")
    private int maxIdle;
    @Value("${spring.redis.lettuce.pool.min-idle}")
    private int minIdle;
    @Value("${spring.redis.lettuce.pool.max-wait}")
    private int maxWait;

    public static int defaultDb = 0;

    public static int totalDbs = 1;

    @Value("${spring.redis.dbs:${spring.redis.database}}")
    private List<Integer> dbs;


    public static Map<Integer, RedisTemplate<String, Object>> redisTemplateMap = new HashMap<>();

    @PostConstruct
    public void initRedisTemp() throws Exception {
        log.info("###### START 初始化 Redis 连接池 START ######");
        defaultDb = dbs.get(0);
        if(dbs.size()==2&&dbs.get(1)!=null){
            totalDbs = dbs.get(1);
            log.info("init totalDbs : {}",totalDbs);
            for (int i = 0;i < totalDbs; i++) {
                log.info("###### 正在加载Redis-db-" + i+ " ######");
                redisTemplateMap.put(i, getRedisTemplate(i));
            }
        }else{
            log.info("init defaultDb : {}",defaultDb);
            redisTemplateMap.put(defaultDb, getRedisTemplate(defaultDb));
        }
        log.info("###### END 初始化 Redis 连接池 END ######");
    }

    private RedisTemplate<String, Object> getRedisTemplate(int dbNo) {
        return getRedisTemplate(getDbFactory(dbNo));
    }

    private LettuceConnectionFactory getDbFactory(int dbNo){
        LettuceConnectionFactory factory = new LettuceConnectionFactory(getRedisConfig(dbNo), getClientConfig());
        factory.afterPropertiesSet();//必须初始化实例
        return factory;
    }

    private RedisStandaloneConfiguration getRedisConfig(int dbNo) {
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
        config.setHostName(host);
        config.setPort(port);
        config.setPassword(password);
        config.setDatabase(dbNo);
        return config;
    }

    private LettuceClientConfiguration getClientConfig() {
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        poolConfig.setMaxTotal(maxActive);
        poolConfig.setMaxIdle(maxIdle);
        poolConfig.setMinIdle(minIdle);
        poolConfig.setMaxWaitMillis(maxWait);
        return LettucePoolingClientConfiguration.builder().poolConfig(poolConfig).build();
    }

    private RedisTemplate<String, Object> getRedisTemplate(LettuceConnectionFactory factory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        setSerializer(redisTemplate);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    public RedisTemplate<String, Object> getRedisTemplateByDb(int db){
        return redisTemplateMap.get(db);
    }

    @Bean
    public RedisTemplate redisTemplate() {
        LettuceConnectionFactory factory = null;
        if(totalDbs==1){
            factory = getDbFactory(defaultDb);
        }else{
            factory = getDbFactory(totalDbs-1);
        }
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(factory);
        setSerializer(redisTemplate);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean
    public StringRedisTemplate stringRedisTemplate() {
        LettuceConnectionFactory factory = null;
        if(totalDbs==1){
            factory = getDbFactory(defaultDb);
        }else{
            factory = getDbFactory(totalDbs-1);
        }
        StringRedisTemplate redisTemplate = new StringRedisTemplate();
        redisTemplate.setConnectionFactory(factory);
        return redisTemplate;
    }

    private void setSerializer(RedisTemplate<String, Object> template) {
        Jackson2JsonRedisSerializer j2jrs = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        om.registerModule(new JavaTimeModule());
        om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
        j2jrs.setObjectMapper(om);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // key采用String的序列化方式
        template.setKeySerializer(stringRedisSerializer);
        // hash的key也采用String的序列化方式
        template.setHashKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson
        template.setValueSerializer(j2jrs);
        // hash的value序列化方式采用jackson
        template.setHashValueSerializer(j2jrs);
        template.afterPropertiesSet();
    }
}