package com.kingdee.eas.custom.integration.base.util;

import com.kingdee.eas.custom.integration.base.dto.ApiResult;

import java.lang.reflect.Method;
import java.util.Map;

/**
 * JSON serialization utility, no third-party dependencies.
 */
public final class JsonUtils {

    private JsonUtils() {
    }

    /**
     * Serialize arbitrary object to JSON string.
     * Dispatches to type-specific serializers.
     */
    @SuppressWarnings("unchecked")
    public static String toJson(Object value) {
        if (value == null) {
            return "null";
        }
        if (value instanceof String) {
            return (String) value;
        }
        if (value instanceof ApiResult) {
            return toJson((ApiResult<?>) value);
        }
        if (value instanceof Map) {
            return toJson((Map<String, Object>) value);
        }
        return beanToJson(value);
    }

    /**
     * Serialize ApiResult to JSON string.
     */
    public static String toJson(ApiResult<?> result) {
        StringBuilder sb = new StringBuilder("{");
        sb.append("\"code\":").append(result.getCode());
        sb.append(",\"message\":\"").append(escape(result.getMessage())).append("\"");
        sb.append(",\"data\":").append(valueToJson(result.getData()));
        sb.append(",\"timestamp\":").append(result.getTimestamp());
        sb.append("}");
        return sb.toString();
    }

    /**
     * Serialize Map to JSON string.
     */
    public static String toJson(Map<String, Object> map) {
        StringBuilder sb = new StringBuilder("{");
        boolean first = true;
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            if (!first) {
                sb.append(",");
            }
            first = false;
            sb.append("\"").append(entry.getKey()).append("\":");
            sb.append(valueToJson(entry.getValue()));
        }
        sb.append("}");
        return sb.toString();
    }

    @SuppressWarnings("unchecked")
    private static String valueToJson(Object value) {
        if (value == null) {
            return "null";
        }
        if (value instanceof String) {
            return "\"" + escape((String) value) + "\"";
        }
        if (value instanceof Number || value instanceof Boolean) {
            return value.toString();
        }
        if (value instanceof Map) {
            return toJson((Map<String, Object>) value);
        }
        return beanToJson(value);
    }

    /**
     * Reflectively serialize a POJO to JSON using its getters.
     */
    private static String beanToJson(Object bean) {
        if (bean == null) {
            return "null";
        }
        StringBuilder sb = new StringBuilder("{");
        boolean first = true;
        Method[] methods = bean.getClass().getMethods();
        for (Method m : methods) {
            String name = m.getName();
            if (m.getParameterCount() != 0 || m.getReturnType() == Void.TYPE) {
                continue;
            }
            if (name.startsWith("get") && name.length() > 3) {
                // skip getClass
                if ("getClass".equals(name)) {
                    continue;
                }
                String propName = Character.toLowerCase(name.charAt(3)) + name.substring(4);
                try {
                    Object val = m.invoke(bean);
                    if (!first) {
                        sb.append(",");
                    }
                    first = false;
                    sb.append("\"").append(propName).append("\":").append(valueToJson(val));
                } catch (Exception ignored) {
                }
            } else if (name.startsWith("is") && name.length() > 2
                    && (m.getReturnType() == Boolean.TYPE || m.getReturnType() == Boolean.class)) {
                String propName = Character.toLowerCase(name.charAt(2)) + name.substring(3);
                try {
                    boolean val = (Boolean) m.invoke(bean);
                    if (!first) {
                        sb.append(",");
                    }
                    first = false;
                    sb.append("\"").append(propName).append("\":").append(val);
                } catch (Exception ignored) {
                }
            }
        }
        sb.append("}");
        return sb.toString();
    }

    private static String escape(String s) {
        if (s == null) {
            return "";
        }
        StringBuilder sb = new StringBuilder(s.length());
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            switch (c) {
                case '"':
                    sb.append("\\\"");
                    break;
                case '\\':
                    sb.append("\\\\");
                    break;
                case '\n':
                    sb.append("\\n");
                    break;
                case '\r':
                    sb.append("\\r");
                    break;
                case '\t':
                    sb.append("\\t");
                    break;
                default:
                    sb.append(c);
            }
        }
        return sb.toString();
    }
}
