package com.config;

import org.springframework.cache.CacheManager;
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.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.Duration;
import java.util.Collections;
import org.springframework.cache.annotation.EnableCaching;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.databind.jsontype.BasicPolymorphicTypeValidator;
import com.fasterxml.jackson.databind.jsontype.PolymorphicTypeValidator;

import java.io.IOException;

@Configuration
@EnableCaching  // 启用缓存
public class CacheConfig {

    /**
     * 配置支持类型信息的 ObjectMapper
     */
    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();

        // 注册 Java 8 日期时间支持
        mapper.registerModule(new JavaTimeModule());

        // 禁用将日期序列化为时间戳
        mapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

        // 注册达梦 JDBC 安全序列化器 — 拦截 dm.jdbc.* 类，直接返回 null 防止循环引用 StackOverflow
        SimpleModule dmSafeModule = new SimpleModule("dm-safe");
        dmSafeModule.setSerializerModifier(new BeanSerializerModifier() {
            @Override
            public JsonSerializer<?> modifySerializer(SerializationConfig config,
                                                       BeanDescription beanDesc,
                                                       JsonSerializer<?> serializer) {
                if (beanDesc.getBeanClass().getName().startsWith("dm.jdbc.")) {
                    return new JsonSerializer<Object>() {
                        @Override
                        public void serialize(Object value, JsonGenerator gen,
                                            SerializerProvider serializers) throws IOException {
                            gen.writeNull();
                        }

                        @Override
                        public void serializeWithType(Object value, JsonGenerator gen,
                                            SerializerProvider serializers, TypeSerializer typeSer) throws IOException {
                            gen.writeNull();
                        }
                    };
                }
                return serializer;
            }
        });
        mapper.registerModule(dmSafeModule);

        // 启用类型信息，解决 LinkedHashMap 转换问题
        PolymorphicTypeValidator ptv = BasicPolymorphicTypeValidator.builder()
                .allowIfSubType("com.common.entity.")    // 允许你的实体类包
                .allowIfSubType("java.util.ArrayList")   // 允许 ArrayList
                .allowIfSubType("java.util.LinkedList")  // 允许 LinkedList
                .allowIfBaseType("java.util.List")       // 允许 List 接口
                .allowIfBaseType("java.lang.Object")     // 允许 Object 类型
                .build();

        // 激活默认类型信息
        mapper.activateDefaultTyping(
                ptv,
                ObjectMapper.DefaultTyping.NON_FINAL,
                com.fasterxml.jackson.annotation.JsonTypeInfo.As.PROPERTY
        );
        return mapper;
    }


    /**
     * Redis 缓存配置
     */
    @Bean
    public RedisCacheConfiguration redisCacheConfiguration() {
        ObjectMapper mapper = objectMapper();
        GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(mapper);

        return RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(30))
                .disableCachingNullValues()
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(serializer));
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        ObjectMapper mapper = objectMapper();
        GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(mapper);

        RedisCacheConfiguration deviceConfig = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1))
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(serializer));

        RedisCacheConfiguration collectTaskConfig = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofHours(1))
                .serializeKeysWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair
                        .fromSerializer(serializer));

        return RedisCacheManager.builder(factory)
                .cacheDefaults(redisCacheConfiguration())
                .withInitialCacheConfigurations(Collections.singletonMap(
                        "device", deviceConfig
                ))
                .withInitialCacheConfigurations(Collections.singletonMap(

                        "collectTask",collectTaskConfig
                ))
                .transactionAware()
                .build();
    }
}