package com.ecep.contract.config;

import java.time.Duration;

import org.hibernate.Hibernate;
import org.hibernate.proxy.HibernateProxy;
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.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.ecep.contract.model.IdentityEntity;
import com.ecep.contract.util.HibernateProxyUtils;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.DeserializationConfig;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;

/**
 * Redis缓存配置类，用于配置Redis的缓存管理器和序列化器
 */
@Configuration
@EnableCaching
public class RedisCacheConfig {

        /**
         * 配置RedisTemplate，使用与JacksonConfig相同的ObjectMapper以确保Hibernate代理对象被正确序列化
         */
        @Bean
        public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory,
                        ObjectMapper objectMapper) {
                RedisTemplate<Object, Object> template = new RedisTemplate<>();
                template.setConnectionFactory(redisConnectionFactory);

                // 设置键的序列化器
                template.setKeySerializer(new StringRedisSerializer());
                template.setHashKeySerializer(new StringRedisSerializer());

                // 使用GenericJackson2JsonRedisSerializer并配置为使用我们自定义的ObjectMapper
                // 这个ObjectMapper已经包含了处理Hibernate代理对象的序列化器
                GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(
                                objectMapper);
                template.setValueSerializer(jackson2JsonRedisSerializer);
                template.setHashValueSerializer(jackson2JsonRedisSerializer);

                return template;
        }

        /**
         * 配置RedisCacheManager，使用与JacksonConfig相同的ObjectMapper以确保Hibernate代理对象被正确序列化
         * 并启用类型信息存储以解决LinkedHashMap无法转换为具体类型的问题
         */
      // @Bean
        public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory, ObjectMapper objectMapper) {
                // 创建ObjectMapper的副本，而不是直接修改注入的实例
                ObjectMapper cacheObjectMapper = objectMapper.copy();

                // 配置ObjectMapper副本以保留类型信息（仅用于Redis缓存）
                // 使用activateDefaultTyping代替已弃用的enableDefaultTyping
                cacheObjectMapper.activateDefaultTyping(
                                cacheObjectMapper.getPolymorphicTypeValidator(),
                                ObjectMapper.DefaultTyping.NON_FINAL,
                                JsonTypeInfo.As.PROPERTY);

                // 确保我们的HibernateProxyDeserializer被正确注册
                // 创建一个新的模块来注册自定义反序列化器
                SimpleModule proxyModule = new SimpleModule("CacheHibernateProxyModule");
                // 添加代理对象序列化器，只输出ID字段
                proxyModule.addSerializer(HibernateProxy.class, new HibernateProxySerializer());
                proxyModule.addDeserializer(HibernateProxy.class, new HibernateProxyDeserializer());
                // 使用BeanSerializerModifier来处理IdentityEntity类型，避免递归调用
                proxyModule.setSerializerModifier(new BeanSerializerModifier() {
                        @Override
                        public JsonSerializer<?> modifySerializer(SerializationConfig config, BeanDescription beanDesc,
                                        JsonSerializer<?> serializer) {
                                Class<?> beanClass = beanDesc.getBeanClass();
                                if (beanClass.isPrimitive()) {
                                        return serializer;
                                }

                                System.out.println("modifySerializer:" + beanDesc.getBeanClass() + ", serializer:"
                                                + serializer);
                                return serializer;
                        }
                });
                proxyModule.setDeserializerModifier(new BeanDeserializerModifier() {
                        @Override
                        public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config,
                                        BeanDescription beanDesc,
                                        JsonDeserializer<?> deserializer) {
                                Class<?> beanClass = beanDesc.getBeanClass();
                                if (beanClass.isPrimitive()) {
                                        return deserializer;
                                }

                                System.out.println("modifyDeserializer:" + beanDesc.getBeanClass() + ", deserializer:"
                                                + deserializer);
                                return deserializer;
                        }
                });
                cacheObjectMapper.registerModule(proxyModule);

                // 创建Redis缓存配置，使用包含Hibernate代理处理的ObjectMapper副本
                RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                                .entryTtl(Duration.ofHours(6)) // 设置缓存过期时间为6小时
                                .serializeKeysWith(
                                                RedisSerializationContext.SerializationPair
                                                                .fromSerializer(new StringRedisSerializer()))
                                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(
                                                new GenericJackson2JsonRedisSerializer(cacheObjectMapper)))
                                .disableCachingNullValues(); // 不缓存null值

                // 创建Redis缓存管理器，为所有缓存名称配置相同的序列化策略以确保类型安全反序列化
                return RedisCacheManager.builder(redisConnectionFactory)
                                .cacheDefaults(cacheConfiguration)
                                .build();
        }
}