package com.easyagents.document.core.mineru;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.easyagents.core.util.StringUtil;
import com.easyagents.document.core.exception.DocumentParseException;

/**
 * MinerU JSON 兼容解析工具。
 *
 * <p>部分部署返回的结构化字段会被额外包装成 JSON 字符串，
 * 例如 {@code middle_json="{}"} 或整个响应体直接返回
 * {@code "{\"results\":{...}}"}。该工具负责统一拆包，
 * 让上层模块无需重复处理这些兼容分支。</p>
 *
 * @author Codex
 * @since 2026-04-17
 */
final class MineruJsonSupport {

    private static final int MAX_JSON_UNWRAP_DEPTH = 4;
    private static final int PREVIEW_LIMIT = 160;

    private MineruJsonSupport() {
    }

    /**
     * 解析 JSON 文本并要求最终结果为对象。
     *
     * @param text JSON 文本
     * @param context 错误上下文
     * @return JSON 对象
     */
    static JSONObject parseObject(String text, String context) {
        Object value = parseValue(text, context);
        if (value instanceof JSONObject) {
            return (JSONObject) value;
        }
        throw unexpectedType(context, "JSONObject", value);
    }

    /**
     * 规范化任意 JSON 值。
     *
     * @param value 原始值
     * @param context 错误上下文
     * @return 规范化后的 JSON 值
     */
    static Object normalizeValue(Object value, String context) {
        if (value == null) {
            return null;
        }
        if (value instanceof JSONObject || value instanceof JSONArray) {
            return value;
        }
        if (value instanceof CharSequence) {
            String text = value.toString().trim();
            if (!StringUtil.hasText(text)) {
                return null;
            }
            return parseValue(text, context);
        }
        try {
            return unwrapNestedJsonString(JSON.parse(JSON.toJSONString(value)), context);
        } catch (Exception exception) {
            throw new DocumentParseException(
                "Failed to normalize MinerU JSON value: " + context + ", preview=" + preview(String.valueOf(value)),
                exception
            );
        }
    }

    /**
     * 在值看起来像 JSON 时才尝试拆包，否则保留原始值。
     *
     * @param value 原始值
     * @param context 错误上下文
     * @return 规范化后的值或原值
     */
    static Object normalizeValueIfJsonLike(Object value, String context) {
        if (!(value instanceof CharSequence)) {
            return normalizeValue(value, context);
        }
        String text = value.toString().trim();
        if (!looksLikeJson(text) && !(text.startsWith("\"") && text.endsWith("\""))) {
            return value;
        }
        return normalizeValue(value, context);
    }

    /**
     * 将任意 JSON 值转换为对象。
     *
     * @param value 原始值
     * @param context 错误上下文
     * @return JSON 对象
     */
    static JSONObject asObject(Object value, String context) {
        Object normalized = normalizeValue(value, context);
        if (normalized == null) {
            return null;
        }
        if (normalized instanceof JSONObject) {
            return (JSONObject) normalized;
        }
        throw unexpectedType(context, "JSONObject", normalized);
    }

    /**
     * 将任意 JSON 值转换为数组。
     *
     * @param value 原始值
     * @param context 错误上下文
     * @return JSON 数组
     */
    static JSONArray asArray(Object value, String context) {
        Object normalized = normalizeValue(value, context);
        if (normalized == null) {
            return null;
        }
        if (normalized instanceof JSONArray) {
            return (JSONArray) normalized;
        }
        throw unexpectedType(context, "JSONArray", normalized);
    }

    /**
     * 解析 JSON 文本并自动拆解被双层包装的字符串值。
     *
     * @param text JSON 文本
     * @param context 错误上下文
     * @return 解析结果
     */
    static Object parseValue(String text, String context) {
        String trimmed = text == null ? null : text.trim();
        if (!StringUtil.hasText(trimmed)) {
            throw new DocumentParseException("MinerU JSON payload is empty: " + context);
        }
        try {
            return unwrapNestedJsonString(JSON.parse(trimmed), context);
        } catch (DocumentParseException exception) {
            throw exception;
        } catch (Exception exception) {
            throw new DocumentParseException(
                "Failed to parse MinerU JSON payload: " + context + ", preview=" + preview(trimmed),
                exception
            );
        }
    }

    private static Object unwrapNestedJsonString(Object value, String context) {
        Object current = value;
        for (int depth = 0; depth < MAX_JSON_UNWRAP_DEPTH; depth++) {
            if (!(current instanceof String)) {
                return current;
            }
            String text = ((String) current).trim();
            if (!looksLikeJson(text)) {
                return current;
            }
            try {
                current = JSON.parse(text);
            } catch (Exception exception) {
                throw new DocumentParseException(
                    "Failed to unwrap MinerU nested JSON string: " + context + ", preview=" + preview(text),
                    exception
                );
            }
        }
        return current;
    }

    private static boolean looksLikeJson(String text) {
        if (!StringUtil.hasText(text)) {
            return false;
        }
        char first = text.charAt(0);
        return first == '{' || first == '[';
    }

    private static DocumentParseException unexpectedType(String context, String expectedType, Object actualValue) {
        String actualType = actualValue == null ? "null" : actualValue.getClass().getSimpleName();
        return new DocumentParseException(
            "MinerU JSON payload type mismatch: " + context + ", expected=" + expectedType + ", actual=" + actualType
        );
    }

    private static String preview(String text) {
        if (text == null) {
            return "";
        }
        String normalized = text.replace('\n', ' ').replace('\r', ' ');
        if (normalized.length() <= PREVIEW_LIMIT) {
            return normalized;
        }
        return normalized.substring(0, PREVIEW_LIMIT) + "...";
    }
}
