package com.agent.management.common;

import java.text.DecimalFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 日志脱敏工具：在写入 llm.log 等调试日志前，对内容中的大向量数字数组进行简写，
 * 避免单条日志被数百/数千个浮点数撑满。
 *
 * <p>仅对"纯数字数组"生效（如 embedding 向量），不影响其他 JSON 结构。
 * 采用字符扫描而非单一长正则，避免在大向量上发生 StackOverflowError。</p>
 */
public final class LogRedactor {

    /** 小于该元素数的数组原样输出，避免误伤短数组（如坐标 [1,2] 或 logits 摘要） */
    private static final int ABBREVIATE_THRESHOLD = 8;

    /** 取头几个数字作为预览 */
    private static final int PREVIEW_COUNT = 3;

    private static final Pattern NUMBER_TOKEN = Pattern.compile("-?\\d+(?:\\.\\d+)?(?:[eE][+-]?\\d+)?");
    private static final DecimalFormat PREVIEW_FMT = new DecimalFormat("0.####");

    private LogRedactor() {
    }

    /**
     * 对纯数字数组进行简写：[0.1,0.2,...] → [N 个浮点数已省略> 头部样本: ...]。
     * 其他内容保持不变。
     */
    public static String redactVectors(String text) {
        if (text == null || text.isEmpty()) {
            return text;
        }
        StringBuilder out = new StringBuilder(text.length());
        int i = 0;
        int len = text.length();
        while (i < len) {
            char c = text.charAt(i);
            if (c == '[') {
                int end = findNumericArrayEnd(text, i + 1);
                if (end > i) {
                    String inner = text.substring(i + 1, end);
                    int count = countNumbers(inner);
                    if (count >= ABBREVIATE_THRESHOLD) {
                        out.append("[<").append(count).append(" 个浮点数已省略>")
                                .append(buildPreview(inner)).append("]");
                        i = end + 1;
                        continue;
                    }
                }
            }
            out.append(c);
            i++;
        }
        return out.toString();
    }

    /**
     * 从 start 开始扫描，若 [start..end) 之间仅包含数字数组合法字符且能匹配到 ']'，
     * 返回 ']' 的索引；否则返回 -1。允许内部不再出现 '['（否则视为非纯数字数组）。
     */
    private static int findNumericArrayEnd(String text, int start) {
        int i = start;
        int len = text.length();
        while (i < len) {
            char c = text.charAt(i);
            if (c == ']') {
                return i;
            }
            if (!isNumberArrayChar(c)) {
                return -1;
            }
            i++;
        }
        return -1;
    }

    private static boolean isNumberArrayChar(char c) {
        if ((c >= '0' && c <= '9') || c == '.' || c == '-' || c == '+'
                || c == 'e' || c == 'E' || c == ',' || c == ' ') {
            return true;
        }
        return c == '\t' || c == '\n' || c == '\r';
    }

    private static int countNumbers(String inner) {
        Matcher m = NUMBER_TOKEN.matcher(inner);
        int count = 0;
        while (m.find()) {
            count++;
        }
        return count;
    }

    private static String buildPreview(String inner) {
        StringBuilder sb = new StringBuilder(" 头部样本: ");
        Matcher m = NUMBER_TOKEN.matcher(inner);
        int taken = 0;
        while (m.find() && taken < PREVIEW_COUNT) {
            if (taken > 0) sb.append(", ");
            try {
                sb.append(PREVIEW_FMT.format(Double.parseDouble(m.group())));
            } catch (NumberFormatException ignore) {
                sb.append(m.group());
            }
            taken++;
        }
        return sb.toString();
    }
}
