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.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 org.springframework.util.StringUtils;

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: AbstractRedisConfig
 * @Package com.liquidnet.common.cache.redis.config
 * @Copyright: LightNet @ Copyright (c) 2021
 * @date 2021/10/20 18:21
 */
@Slf4j
public abstract class AbstractRedisConfig {
    public int defaultDb = 0;

    public int totalDbs = 1;

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

    abstract String getHost();
    abstract int getPort();
    abstract String getPassword();
    abstract int getMaxActive();
    abstract int getMaxIdle();
    abstract int getMinIdle();
    abstract int getMaxWait();
    abstract List<Integer> getDbs();

    @PostConstruct
    public void initRedisTemp() throws Exception {
        log.info("############################################################");
        log.info("###### START 初始化 Redis "+this.getClass().getSimpleName()+"连接池 START ######");
        if(StringUtils.isEmpty(getHost())||getHost().equalsIgnoreCase("null")){
            log.info("无配置，不需要初始化！");
            return;
        }
        defaultDb = this.getDbs().get(0);
        if(this.getDbs().size()==2&&this.getDbs().get(1)!=null){
            totalDbs = this.getDbs().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(this.getHost());
        config.setPort(this.getPort());
        config.setPassword(this.getPassword());
        config.setDatabase(dbNo);
        return config;
    }

    private LettuceClientConfiguration getClientConfig() {
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        poolConfig.setMaxTotal(this.getMaxActive());
        poolConfig.setMaxIdle(this.getMaxIdle());
        poolConfig.setMinIdle(this.getMinIdle());
        poolConfig.setMaxWaitMillis(this.getMaxWait());
        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;
//    }

    public StringRedisTemplate getStringRedisTemplate(){
        if(StringUtils.isEmpty(getHost())||getHost().equalsIgnoreCase("null")){
            log.info("无配置，不需要初始化！");
            return null;
        }
        LettuceConnectionFactory factory = null;
        if(totalDbs==1){
            factory = getDbFactory(defaultDb);
        }else{
            factory = getDbFactory(totalDbs-1);
        }
        StringRedisTemplate redisTemplate = new StringRedisTemplate();
        redisTemplate.setConnectionFactory(factory);
        redisTemplate.afterPropertiesSet();
        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();
    }
}
