package com.njzscloud.common.core.utils;

import cn.hutool.core.util.StrUtil;
import com.njzscloud.common.core.tuple.Tuple2;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;

public class CallerUtil {

    /**
     * 获取方法调用者的详细信息（类名、方法名、行号）
     *
     * @return 调用者信息封装类，若获取失败返回 null
     */
    public static Caller getCaller(int depth) {
        /*
        getStackTrace
        getCaller
        getCaller 的调用者
        getCaller 的调用者的调用者
         */
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();

        int i = 2 + depth;
        if (stackTrace.length < i + 1) {
            // 调用栈过短（理论上不可能，除非在 JVM 启动初期调用）
            return Caller.defaultCaller;
        }

        return resolveCaller(stackTrace, depth);
    }

    public static Caller resolveCaller(StackTraceElement[] stackTrace, int depth) {
        StackTraceElement callerElement = stackTrace[depth];
        // 解析栈元素的信息
        String className = callerElement.getClassName();    // 调用者类名（全限定名）
        String methodName = callerElement.getMethodName();// 调用者方法名
        int lineNumber = callerElement.getLineNumber();    // 调用者代码行号（-1 表示未知）
        String fileName = callerElement.getFileName();     // 调用者所在文件名（可能为 null）
        return new Caller(className, methodName, lineNumber, fileName);
    }

    public static Tuple2<String, Caller> printStackTrace(Throwable throwable) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        try (PrintStream printStream = new PrintStream(outputStream)) {
            // 3. 调用 printStackTrace，输出到 PrintStream（实际写入 ByteArrayOutputStream）
            throwable.printStackTrace(printStream);
            // 4. 将字节流转为字符串（默认 UTF-8 编码，兼容所有字符）
            String string = outputStream.toString(StandardCharsets.UTF_8);
            return Tuple2.of(string, resolveCaller(throwable.getStackTrace(), 0));
        } catch (Exception e) {
            // 极端情况下（如编码异常），返回简化信息
            return Tuple2.of(throwable.getMessage(), resolveCaller(throwable.getStackTrace(), 0));
        }
    }

    /**
     * 调用者信息封装类（用于结构化返回结果）
     *
     * @param className  Getter 方法 全限定类名（如：com.example.Test）
     * @param methodName 方法名（如：main）
     * @param lineNumber 调用行号
     * @param fileName   文件名（如：Test.java）
     */
    public record Caller(String className, String methodName, int lineNumber, String fileName) {
        public static Caller defaultCaller = new Caller();

        public Caller() {
            this("", "", -1, "");
        }

        @Override
        public String toString() {
            return StrUtil.format("调用者信息, 类名：{}、方法名：{}、行号：{}、文件名：{}",
                className, methodName, lineNumber, fileName);
        }
    }
}
