package com.qmth.boot.redis.cache;

import com.qmth.boot.core.cache.config.CacheAutoConfiguration;
import com.qmth.boot.core.cache.config.CacheProperties;
import com.qmth.boot.core.cache.service.CacheConfigRegistration;
import com.qmth.boot.core.cache.service.CustomizeCacheConfiguration;
import com.qmth.boot.redis.constant.RedisConstant;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.lang.Nullable;

import javax.validation.constraints.NotNull;
import java.time.Duration;

@Configuration
@AutoConfigureBefore(CacheAutoConfiguration.class)
@ComponentScan("com.qmth.boot.redis")
public class RedisCacheAutoConfiguration {

    private static final Logger log = LoggerFactory.getLogger(RedisCacheAutoConfiguration.class);

    @Bean
    @Primary
    public CacheManager cacheManager(@NotNull RedisConnectionFactory redisConnectionFactory,
            @NotNull RedisTemplate<String, Object> redisTemplate, @NotNull CacheProperties cacheProperties,
            @Nullable CustomizeCacheConfiguration customizeCacheConfiguration) {
        //默认设置
        final RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(buildRedisCacheConfig(redisTemplate, cacheProperties.isAllowNullValue(),
                        cacheProperties.getExpireAfterWrite()));
        //自定义缓存设置
        if (customizeCacheConfiguration != null) {
            customizeCacheConfiguration.register(new CacheConfigRegistration() {

                @Override
                public CacheConfigRegistration setCacheConfig(String name, Duration expireAfterWrite) {
                    log.info("Customize cache configuration: name={}, expireAfterWrite={}", name,
                            expireAfterWrite.toString());
                    builder.withCacheConfiguration(name,
                            buildRedisCacheConfig(redisTemplate, cacheProperties.isAllowNullValue(), expireAfterWrite));
                    return this;
                }
            });
        }
        log.info("Redis cache manager inited.");
        return builder.build();
    }

    private RedisCacheConfiguration buildRedisCacheConfig(RedisTemplate<String, Object> redisTemplate,
            boolean allowNullValue, Duration expireAfterWrite) {
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                //固定key格式与前缀
                .computePrefixWith(key -> RedisConstant.CACHE_PREFIX + RedisConstant.KEY_SEPARATOR + key
                        + RedisConstant.KEY_SEPARATOR)
                //序列化方法
                .serializeKeysWith(
                        RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getStringSerializer()))
                .serializeValuesWith(
                        RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()));
        //禁止空值保存
        if (!allowNullValue) {
            config = config.disableCachingNullValues();
        }
        //写入失效时长大于0毫秒时才启用
        if (expireAfterWrite.toMillis() > 0) {
            config = config.entryTtl(expireAfterWrite);
        }
        return config;
    }
}
