package com.red.circle.other.app.framework;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import com.red.circle.component.redis.annotation.aspect.TaskCacheLockAspect;
import com.red.circle.component.redis.props.RedisConfigProperties;
import com.red.circle.component.redis.service.RedisService;
import com.red.circle.tool.core.text.StringUtils;
import io.lettuce.core.internal.HostAndPort;
import io.lettuce.core.resource.ClientResources;
import io.lettuce.core.resource.DnsResolvers;
import io.lettuce.core.resource.MappingSocketAddressResolver;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
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.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
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.serializer.GenericToStringSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
@EnableConfigurationProperties({RedisConfigProperties.class})
public class RedisConfig {

    public RedisConfig() {
    }

    @Bean
    @Primary
    @ConditionalOnMissingBean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate var2 = new RedisTemplate();
        var2.setValueSerializer(new GenericToStringSerializer(Object.class));
        var2.setKeySerializer(new StringRedisSerializer());
        var2.setHashKeySerializer(new StringRedisSerializer());
        var2.setConnectionFactory(connectionFactory);
        return var2;
    }

    @Bean
    @ConditionalOnMissingBean
    public KeyGenerator simpleKeyGenerator() {
        return (var0, var1, var2) -> {
            StringBuilder var3 = new StringBuilder();
            var3.append(var0.getClass().getSimpleName());
            var3.append(".");
            var3.append(var1.getName());
            var3.append("[");
            Object[] var4 = var2;
            int var5 = var2.length;

            for(int var6 = 0; var6 < var5; ++var6) {
                Object var7 = var4[var6];
                var3.append(var7.toString());
            }

            var3.append("]");
            return var3.toString();
        };
    }

    @Bean
    @ConditionalOnMissingBean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory, RedisConfigProperties redisConfigProperties) {
        return new RedisCacheManager(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory), this.getRedisCacheConfigurationWithTtl(1800), this.getRedisCacheConfigurationMap(redisConfigProperties));
    }

    private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap(RedisConfigProperties redisConfigProperties) {
        HashMap var2 = new HashMap(16);
        redisConfigProperties.getInitExpiry().forEach((var2x, var3) -> {
            var2.put(var2x, this.getRedisCacheConfigurationWithTtl(var3));
        });
        return var2;
    }

    private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
        Jackson2JsonRedisSerializer var2 = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper var3 = new ObjectMapper();
        var3.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        var3.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        //var2.setObjectMapper(var3);
        RedisCacheConfiguration var4 = RedisCacheConfiguration.defaultCacheConfig();
        var4 = var4.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(var2)).entryTtl(Duration.ofSeconds((long)seconds));
        return var4;
    }

    @Bean
    @Primary
    @ConditionalOnMissingBean
    public RedisService redisService(RedisTemplate<String, Object> redisTemplate) {
        return new RedisService(redisTemplate);
    }

    @Bean
    public TaskCacheLockAspect taskCacheLockAspect(RedisTemplate<String, Object> redisTemplate) {
        return new TaskCacheLockAspect(redisTemplate.opsForValue());
    }

    @Bean
    public LettuceConnectionFactory redisConnectionFactory(RedisProperties redisProperties) {
        MappingSocketAddressResolver var2 = MappingSocketAddressResolver.create(DnsResolvers.UNRESOLVED, (var1) -> {
            return HostAndPort.of(redisProperties.getHost(), var1.getPort());
        });
        ClientResources var3 = ClientResources.builder().socketAddressResolver(var2).build();
        LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder var4 = LettucePoolingClientConfiguration.builder().poolConfig(this.getPoolConfig(redisProperties.getLettuce().getPool())).clientResources(var3);
        if (Objects.nonNull(redisProperties.getLettuce().getShutdownTimeout())) {
            var4.shutdownTimeout(redisProperties.getLettuce().getShutdownTimeout());
        }

        if (Objects.nonNull(redisProperties.getTimeout())) {
            var4.commandTimeout(redisProperties.getTimeout());
        }

        if (StringUtils.isNotBlank(redisProperties.getClientName())) {
            var4.clientName(redisProperties.getClientName());
        }

        RedisStandaloneConfiguration var6 = new RedisStandaloneConfiguration();
        var6.setDatabase(redisProperties.getDatabase());
        var6.setHostName(redisProperties.getHost());
        var6.setPort(redisProperties.getPort());
        var6.setPassword(redisProperties.getPassword());
        LettucePoolingClientConfiguration var5 = redisProperties.getSsl().isEnabled() ? var4.useSsl().disablePeerVerification().build() : var4.build();
//        LettucePoolingClientConfiguration var5 = var4.build();
        return new LettuceConnectionFactory(var6, var5);
    }

    private GenericObjectPoolConfig<?> getPoolConfig(RedisProperties.Pool properties) {
        GenericObjectPoolConfig var2 = new GenericObjectPoolConfig();
        var2.setMaxTotal(properties.getMaxActive());
        var2.setMaxIdle(properties.getMaxIdle());
        var2.setMinIdle(properties.getMinIdle());
        if (properties.getTimeBetweenEvictionRuns() != null) {
            var2.setTimeBetweenEvictionRuns(properties.getTimeBetweenEvictionRuns());
        }

        if (properties.getMaxWait() != null) {
            var2.setMaxWait(properties.getMaxWait());
        }

        return var2;
    }

}
