package com.njzscloud.common.core.fastjson;

import cn.hutool.core.date.DatePattern;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.filter.Filter;
import com.alibaba.fastjson2.filter.ValueFilter;
import com.alibaba.fastjson2.reader.ObjectReader;
import lombok.extern.slf4j.Slf4j;

import java.io.InputStream;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;

import static com.alibaba.fastjson2.JSONReader.Feature.IgnoreCheckClose;

/**
 * <p>Fastjson 工具</p>
 * <p>已开启类型序列化/反序列化支持</p>
 */
@Slf4j
public class Fastjson {
    private static final JSONWriter.Context JSON_WRITER_CONTEXT;
    private static final JSONReader.Context JSON_READER_CONTEXT;

    static {
        JSON_WRITER_CONTEXT = new JSONWriter.Context(
            JSONWriter.Feature.WriteNulls, // 序列化输出空值字段
            JSONWriter.Feature.BrowserCompatible, // 在大范围超过JavaScript支持的整数，输出为字符串格式
            JSONWriter.Feature.WriteClassName, // 序列化时输出类型信息
            // JSONWriter.Feature.PrettyFormat, // 格式化输出
            JSONWriter.Feature.SortMapEntriesByKeys, // 对Map中的KeyValue按照Key做排序后再输出。在有些验签的场景需要使用这个Feature
            JSONWriter.Feature.WriteBigDecimalAsPlain, // 序列化BigDecimal使用toPlainString，避免科学计数法
            JSONWriter.Feature.WriteNullListAsEmpty, // 将List类型字段的空值序列化输出为空数组"[]"
            JSONWriter.Feature.WriteNullStringAsEmpty, // 	将String类型字段的空值序列化输出为空字符串""
            JSONWriter.Feature.WriteLongAsString // 将 Long 作为字符串输出
        );

        ZoneId zoneId = ZoneId.of("GMT+8");

        JSON_WRITER_CONTEXT.setZoneId(zoneId);
        JSON_WRITER_CONTEXT.setDateFormat(DatePattern.NORM_DATETIME_PATTERN);

        JSON_WRITER_CONTEXT.configFilter((ValueFilter) (object, name, value) -> {
            if (value != null) {
                Class<?> clazz = value.getClass();
                if (clazz == LocalDate.class) {
                    return DateTimeFormatter.ofPattern(DatePattern.PURE_DATE_PATTERN).withZone(zoneId).format((LocalDate) value);
                } else if (clazz == LocalTime.class) {
                    return DateTimeFormatter.ofPattern(DatePattern.NORM_TIME_PATTERN).withZone(zoneId).format((LocalTime) value);
                } else if (clazz == BigDecimal.class) {
                    return ((BigDecimal) value).toPlainString();
                }
            }
            return value;
        });
        JSON_READER_CONTEXT = new JSONReader.Context(
            JSONReader.Feature.IgnoreSetNullValue, // 忽略输入为null的字段
            JSONReader.Feature.SupportAutoType // 	支持自动类型，要读取带"@type"类型信息的JSON数据，需要显示打开SupportAutoType
        );
        JSON_READER_CONTEXT.setZoneId(zoneId);
        JSON_READER_CONTEXT.setDateFormat(DatePattern.NORM_DATETIME_PATTERN);
    }

    /**
     * 序列化为 JSON 字符串
     *
     * @param object 数据
     * @return String
     * @see JSON#toJSONString(Object, JSONWriter.Context)
     */
    public static String toJsonStr(Object object) {
        return JSON.toJSONString(object, JSON_WRITER_CONTEXT);
    }

    /**
     * 序列化为 JSON 字节数组
     *
     * @param object 数据
     * @return byte[]
     * @see JSON#toJSONBytes(Object, Filter...)
     */
    public static byte[] toJsonBytes(Object object) {
        return JSON.toJSONBytes(object, StandardCharsets.UTF_8, JSON_WRITER_CONTEXT);
    }

    /**
     * 反序列化
     *
     * @param json JSON 字符串
     * @param type 类型
     * @return T
     * @see JSON#parseObject(String, Class, JSONReader.Context)
     */
    @SuppressWarnings("unchecked")
    public static <T> T toBean(String json, Type type) {
        if (json == null || json.isEmpty()) {
            return null;
        }
        return toBean(JSONReader.of(json, JSON_READER_CONTEXT), type);
    }

    public static <T> T toBean(InputStream json, Type type) {
        if (json == null) {
            return null;
        }
        return toBean(JSONReader.of(json, StandardCharsets.UTF_8, JSON_READER_CONTEXT), type);

    }

    public static <T> T toBean(byte[] json, Type type) {
        if (json == null || json.length == 0) {
            return null;
        }
        return toBean(JSONReader.of(json, JSON_READER_CONTEXT), type);
    }

    @SuppressWarnings("unchecked")
    private static <T> T toBean(JSONReader reader, Type type) {
        ObjectReader<T> objectReader = JSON_READER_CONTEXT.getObjectReader(type);
        try {
            T object = objectReader.readObject(reader, type, null, 0);
            reader.handleResolveTasks(object);
            if (!reader.isEnd() && (JSON_READER_CONTEXT.getFeatures() & IgnoreCheckClose.mask) == 0) {
                throw new JSONException(reader.info("input not end"));
            }
            return object;
        } finally {
            reader.close();
        }
    }
}
