package com.seer.teach.teacher.common.utils;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * JSON数据差异比较工具类
 *
 * @author Lingma
 * @since 2025-11-27
 */
@Slf4j
public class JsonDiffUtils {

    private static final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 比较两个JSON字符串的差异
     *
     * @param beforeJson 修改前的JSON数据
     * @param afterJson  修改后的JSON数据
     * @return 差异信息的Map，key为字段名，value为[修改前值 -> 修改后值]的字符串
     */
    public static Map<String, String> diff(String beforeJson, String afterJson) {
        Map<String, String> differences = new HashMap<>();

        // 如果任一JSON为空，则无法比较
        if (!StringUtils.hasText(beforeJson) && !StringUtils.hasText(afterJson)) {
            return differences;
        }

        try {
            // 解析JSON
            JsonNode beforeNode = StringUtils.hasText(beforeJson) ? objectMapper.readTree(beforeJson) : objectMapper.createObjectNode();
            JsonNode afterNode = StringUtils.hasText(afterJson) ? objectMapper.readTree(afterJson) : objectMapper.createObjectNode();

            // 比较两个节点
            compareNodes("", beforeNode, afterNode, differences);

        } catch (Exception e) {
            log.error("比较JSON数据时发生错误", e);
        }

        return differences;
    }

    /**
     * 递归比较两个JSON节点
     *
     * @param fieldName    字段名
     * @param beforeNode   修改前的节点
     * @param afterNode    修改后的节点
     * @param differences  差异信息集合
     */
    private static void compareNodes(String fieldName, JsonNode beforeNode, JsonNode afterNode, Map<String, String> differences) {
        // 跳过时间相关字段
        if (isTimeField(fieldName)) {
            return;
        }

        // 处理null节点
        if (beforeNode == null && afterNode == null) {
            return;
        }

        // 如果其中一个节点为null
        if (beforeNode == null || afterNode == null) {
            differences.put(fieldName, formatValue(beforeNode) + " -> " + formatValue(afterNode));
            return;
        }

        // 如果两个节点都为空，无需比较
        if (beforeNode.isNull() && afterNode.isNull()) {
            return;
        }
        if(StringUtils.hasText(fieldName) && ("sort".equalsIgnoreCase(fieldName) || "isChecked".equalsIgnoreCase(fieldName))){
            return;
        }
        // 如果节点类型不同，直接记录差异
        if (beforeNode.getNodeType() != afterNode.getNodeType()) {
            differences.put(fieldName, formatValue(beforeNode) + " -> " + formatValue(afterNode));
            return;
        }

        // 根据节点类型进行比较
        switch (beforeNode.getNodeType()) {
            case OBJECT:
                compareObjectNodes(fieldName, beforeNode, afterNode, differences);
                break;
            case ARRAY:
                compareArrayNodes(fieldName, beforeNode, afterNode, differences);
                break;
            default:
                compareValueNodes(fieldName, beforeNode, afterNode, differences);
                break;
        }
    }

    /**
     * 比较对象节点
     */
    private static void compareObjectNodes(String fieldName, JsonNode beforeNode, JsonNode afterNode, Map<String, String> differences) {
        // 处理null节点
        if (beforeNode == null || afterNode == null) {
            return;
        }

        // 获取所有字段名
        Iterator<String> fieldNames = beforeNode.fieldNames();
        while (fieldNames.hasNext()) {
            String childFieldName = fieldNames.next();
            if(StringUtils.hasText(childFieldName) && ("sort".equalsIgnoreCase(childFieldName) || "isChecked".equalsIgnoreCase(childFieldName))){
                continue;
            }
            String fullFieldName = fieldName.isEmpty() ? childFieldName : fieldName + "." + childFieldName;
            compareNodes(fullFieldName, beforeNode.get(childFieldName), afterNode.get(childFieldName), differences);
        }

        // 检查新增的字段
        fieldNames = afterNode.fieldNames();
        while (fieldNames.hasNext()) {
            String childFieldName = fieldNames.next();
            if(StringUtils.hasText(childFieldName) && ("sort".equalsIgnoreCase(childFieldName) || "isChecked".equalsIgnoreCase(childFieldName))){
                continue;
            }
            String fullFieldName = fieldName.isEmpty() ? childFieldName : fieldName + "." + childFieldName;
            if (beforeNode.get(childFieldName) == null && afterNode.get(childFieldName) != null) {
                compareNodes(fullFieldName, beforeNode.get(childFieldName), afterNode.get(childFieldName), differences);
            }
        }
    }

    /**
     * 比较数组节点
     */
    private static void compareArrayNodes(String fieldName, JsonNode beforeNode, JsonNode afterNode, Map<String, String> differences) {
        // 处理null节点
        if (beforeNode == null && afterNode == null) {
            return;
        }

        // 简化处理：直接比较数组内容
        String beforeValue = formatValue(beforeNode);
        String afterValue = formatValue(afterNode);

        if (!beforeValue.equals(afterValue)) {
            differences.put(fieldName, beforeValue + " -> " + afterValue);
        }
    }

    /**
     * 比较值节点
     */
    private static void compareValueNodes(String fieldName, JsonNode beforeNode, JsonNode afterNode, Map<String, String> differences) {
        // 处理null节点
        if (beforeNode == null && afterNode == null) {
            return;
        }

        String beforeValue = formatValue(beforeNode);
        String afterValue = formatValue(afterNode);

        // 如果值不同，记录差异
        if (!beforeValue.equals(afterValue)) {
            differences.put(fieldName, beforeValue + " -> " + afterValue);
        }
    }

    /**
     * 格式化节点值
     */
    private static String formatValue(JsonNode node) {
        if (node == null || node.isNull()) {
            return "";
        }

        switch (node.getNodeType()) {
            case STRING:
                return "\"" + node.asText() + "\"";
            case NUMBER:
                return node.asText();
            case BOOLEAN:
                return String.valueOf(node.asBoolean());
            case NULL:
                return "";
            default:
                return node.toString();
        }
    }

    /**
     * 判断是否为时间相关字段
     */
    private static boolean isTimeField(String fieldName) {
        return fieldName != null && ( fieldName.equals("createTime")
                || fieldName.equals("updateBy")
                || fieldName.equals("createBy")
                || fieldName.equals("updateTime"));
    }
}