package com.gci.boot.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gci.boot.properties.GciRedisCacheProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.*;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

@EnableCaching
@Configuration
@EnableConfigurationProperties(GciRedisCacheProperties.class)
public class RedisConfig {
    @Autowired
    private GciRedisCacheProperties redisCacheProperties;

    /**
     * RedisTemplate配置
     *
     * @param lettuceConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory) {
        // 设置序列化
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        // 配置redisTemplate
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        RedisSerializer<?> stringSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringSerializer);// key序列化
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// value序列化
        redisTemplate.setHashKeySerializer(stringSerializer);// Hash key序列化
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);// Hash value序列化
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * 缓存配置管理器
     *
     * @param factory
     * @return
     */
    @Bean
    public CacheManager cacheManager(LettuceConnectionFactory factory) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        //设置默认的缓存过期时间
        if(redisCacheProperties.getDefaultTtl() > 0){
            config = config.entryTtl(Duration.ofMillis(redisCacheProperties.getDefaultTtl()));
        }
        if(StringUtils.hasText(redisCacheProperties.getDefaultKeyPrefix())){
            config = config.prefixCacheNameWith(redisCacheProperties.getDefaultKeyPrefix());
        }
        config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager
                // 写入加锁
                .builder(RedisCacheWriter.lockingRedisCacheWriter(factory))
                // 使用默认缓存配置
                .cacheDefaults(config)
                .transactionAware();
        /* 自定义配置缓存的超时时间*/
        if(redisCacheProperties.getKeys().size()>0){
            Map<String, RedisCacheConfiguration> map = new HashMap<>();
            for(Map.Entry<String, GciRedisCacheProperties.KeyConfig> e : redisCacheProperties.getKeys().entrySet()){
                RedisCacheConfiguration keyConfig = RedisCacheConfiguration.defaultCacheConfig();
                if(e.getValue().getTtl()>0){
                    keyConfig = keyConfig.entryTtl(Duration.ofMillis(e.getValue().getTtl()));
                }
                if(!e.getValue().isCacheNullValues()){
                    keyConfig = keyConfig.disableCachingNullValues();
                }
                if(StringUtils.hasText(e.getValue().getPrefix())){
                    keyConfig = keyConfig.prefixCacheNameWith(e.getValue().getPrefix());
                }
                map.put(e.getKey(), keyConfig);
            }
            builder.withInitialCacheConfigurations(map);
        }
        RedisCacheManager cacheManager = builder.build();
        return cacheManager;
    }
}
