package com.ruoyi.framework.config;

import java.nio.charset.Charset;
import java.util.regex.Pattern;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.filter.Filter;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.common.constant.Constants;

/**
 * Redis使用FastJson序列化
 * 
 * @author ruoyi
 */
public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T>
{
    public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");

    static final Filter AUTO_TYPE_FILTER = JSONReader.autoTypeFilter(Constants.JSON_WHITELIST_STR);

    private static final Pattern LONG_LITERAL = Pattern.compile("(\\d+)L(?=[,\\}\\]\\s])");

    private static final ObjectMapper JACKSON = new ObjectMapper()
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    private Class<T> clazz;

    public FastJson2JsonRedisSerializer(Class<T> clazz)
    {
        super();
        this.clazz = clazz;
    }

    @Override
    public byte[] serialize(T t) throws SerializationException
    {
        if (t == null)
        {
            return new byte[0];
        }
        return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET);
    }

    @Override
    public T deserialize(byte[] bytes) throws SerializationException
    {
        if (bytes == null || bytes.length <= 0)
        {
            return null;
        }
        String str = unwrapNestedJsonString(normalizeLegacyCollectionSyntax(new String(bytes, DEFAULT_CHARSET)));
        try
        {
            Object parsed = JSON.parseObject(str, clazz, AUTO_TYPE_FILTER, JSONReader.Feature.SupportAutoType);
            return castParsedValue(parsed);
        }
        catch (Exception ex)
        {
            try
            {
                JSONObject jsonObject = JSON.parseObject(str, JSONObject.class, AUTO_TYPE_FILTER,
                        JSONReader.Feature.SupportAutoType);
                if (jsonObject == null)
                {
                    return null;
                }
                return castParsedValue(jsonObject);
            }
            catch (Exception fallbackEx)
            {
                try
                {
                    return readWithJackson(str);
                }
                catch (Exception jacksonEx)
                {
                    throw new SerializationException("Could not deserialize: " + ex.getMessage(), ex);
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    private T readWithJackson(String str) throws Exception
    {
        if (clazz == Object.class || clazz == null)
        {
            return (T) JACKSON.readValue(str, Object.class);
        }
        return JACKSON.readValue(str, clazz);
    }

    /** 外层被 FastJSON 再包一层引号时解包 */
    private static String unwrapNestedJsonString(String str)
    {
        if (str == null)
        {
            return null;
        }
        String trimmed = str.trim();
        if (trimmed.length() >= 2 && trimmed.startsWith("\"") && trimmed.endsWith("\""))
        {
            try
            {
                String inner = JSON.parseObject(trimmed, String.class);
                if (inner != null && (inner.startsWith("{") || inner.startsWith("[")))
                {
                    return inner;
                }
            }
            catch (Exception ignored)
            {
                // keep original
            }
        }
        return str;
    }

    @SuppressWarnings("unchecked")
    private T castParsedValue(Object parsed)
    {
        if (parsed == null)
        {
            return null;
        }
        if (clazz.isInstance(parsed))
        {
            return clazz.cast(parsed);
        }
        if (parsed instanceof JSONObject)
        {
            JSONObject jsonObject = (JSONObject) parsed;
            String typeName = jsonObject.getString("@type");
            if (typeName != null)
            {
                try
                {
                    Class<?> targetType = Class.forName(typeName);
                    return (T) jsonObject.to(targetType);
                }
                catch (ClassNotFoundException ignored)
                {
                    // fall through
                }
            }
            if (clazz != Object.class)
            {
                return jsonObject.toJavaObject(clazz);
            }
        }
        return (T) parsed;
    }

    private static String normalizeLegacyCollectionSyntax(String str)
    {
        if (str == null || str.isEmpty())
        {
            return str;
        }
        return LONG_LITERAL.matcher(str
                .replace("LinkedHashSet[", "[")
                .replace("HashSet[", "[")
                .replace("TreeSet[", "[")
                .replace("Set[", "[")
                .replace("ArrayList[", "[")
                .replace("LinkedList[", "[")).replaceAll("$1");
    }
}
