package tech.easyflow.autoconfig.license;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

/**
 * License payload 的 canonical JSON 序列化器。
 */
public final class LicenseCanonicalJsonSerializer {

    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    private LicenseCanonicalJsonSerializer() {
    }

    /**
     * 将 JSON 节点序列化为 canonical JSON 字符串。
     *
     * @param node JSON 节点
     * @return canonical JSON
     */
    public static String serialize(JsonNode node) {
        try {
            return OBJECT_MAPPER.writeValueAsString(sortNode(node));
        } catch (JsonProcessingException e) {
            throw new IllegalArgumentException("无法序列化 canonical JSON", e);
        }
    }

    /**
     * 递归排序 JSON 节点。
     *
     * @param node JSON 节点
     * @return 排序后的对象
     */
    static Object sortNode(JsonNode node) {
        if (node == null || node.isNull()) {
            return null;
        }
        if (node.isObject()) {
            TreeMap<String, Object> sorted = new TreeMap<>();
            ObjectNode objectNode = (ObjectNode) node;
            Iterator<Map.Entry<String, JsonNode>> fields = objectNode.fields();
            while (fields.hasNext()) {
                Map.Entry<String, JsonNode> entry = fields.next();
                sorted.put(entry.getKey(), sortNode(entry.getValue()));
            }
            return sorted;
        }
        if (node.isArray()) {
            List<Object> items = new ArrayList<>();
            ArrayNode arrayNode = (ArrayNode) node;
            for (JsonNode item : arrayNode) {
                items.add(sortNode(item));
            }
            return items;
        }
        if (node.isTextual()) {
            return node.textValue();
        }
        if (node.isBoolean()) {
            return node.booleanValue();
        }
        if (node.isIntegralNumber()) {
            return node.bigIntegerValue();
        }
        if (node.isFloatingPointNumber()) {
            return node.decimalValue();
        }
        return node.asText();
    }
}
