package com.njcn.common.utils;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.CharUtil;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author hongawen
 * @version 1.0.0
 * @date 2021年04月21日 09:16
 */
public class ExceptionUtil {

    private static final String PACKAGE_PREFIX = "com.njcn";

    private static final String CONTROLLER = "controller";

    /**
     * 遍历出当前异常在本服务中栈堆信息----异常发生最初位置
     * 本服务所有的包名均需要以com.njcn为前缀
     * 该前缀不需要做配置管理，通常项目创建之初约定完便可。
     */
    public static String getExceptionServerStackInfo(Exception exception) {
        StackTraceElement[] stackTrace = exception.getStackTrace();
        List<StackTraceElement> originalStackTraceElements = Arrays.asList(stackTrace);
        //遍历出本服务的栈堆信息
        List<StackTraceElement> stackTraceElements = originalStackTraceElements.stream().filter(stackInfo -> stackInfo.getClassName().contains(PACKAGE_PREFIX)).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(stackTraceElements)) {
            return stackTraceElements.get(0).toString();
        } else if (CollectionUtil.isNotEmpty(originalStackTraceElements)) {
            return originalStackTraceElements.get(0).toString();
        }
        return "未找到当前异常的堆栈信息";
    }


    /**
     * 遍历出当前异常在本服务中栈堆信息----堆栈的所有信息
     * 本服务所有的包名均需要以com.njcn为前缀
     * 该前缀不需要做配置管理，通常项目创建之初约定完便可。
     */
    public static List<String> getAllExceptionServerStackInfo(Exception exception) {
        StackTraceElement[] stackTrace = exception.getStackTrace();
        List<StackTraceElement> originalStackTraceElements = Arrays.asList(stackTrace);
        //遍历出本服务的栈堆信息
        List<StackTraceElement> stackTraceElements = originalStackTraceElements.stream().filter(stackInfo -> stackInfo.getClassName().contains(PACKAGE_PREFIX)).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(stackTraceElements)) {
            return stackTraceElements.stream().map(StackTraceElement::toString).collect(Collectors.toList());
        } else if (CollectionUtil.isNotEmpty(originalStackTraceElements)) {
            return originalStackTraceElements.stream().map(StackTraceElement::toString).collect(Collectors.toList());
        }
        List<String> noStackInfos = new ArrayList<>();
        noStackInfos.add("未找到当前异常的堆栈信息");
        return noStackInfos;
    }

    /**
     * 遍历出当前异常堆栈的所有信息
     * 本服务所有的包名均需要以com.njcn为前缀
     * 该前缀不需要做配置管理，通常项目创建之初约定完便可。
     */
    public static List<String> getAllExceptionStackInfo(Exception exception) {
        StackTraceElement[] stackTrace = exception.getStackTrace();
        List<StackTraceElement> originalStackTraceElements = Arrays.asList(stackTrace);
        if (CollectionUtil.isNotEmpty(originalStackTraceElements)) {
            return originalStackTraceElements.stream().map(StackTraceElement::toString).collect(Collectors.toList());
        }
        List<String> noStackInfos = new ArrayList<>();
        noStackInfos.add("未找到当前异常的堆栈信息");
        return noStackInfos;
    }

    /**
     * 遍历异常信息栈，获取信息栈中第一个出现的controller以及方法名称
     * 用于反射获取注解内容，比如方法名，操作类型，操作严重度
     * <p>
     * 实际情况可能会出现没有controller入口的情况导致的异常，比如网关gateway内所有请求
     */
    public static List<String> getFirstControllerAndMethodName(Exception exception) {
        List<String> controllerInfo = new ArrayList<>();
        StackTraceElement[] stackTraces = exception.getStackTrace();
        List<StackTraceElement> stackTraceElements = Stream.of(stackTraces)
                .filter(stackInfo -> stackInfo.getClassName().contains(PACKAGE_PREFIX) && stackInfo.getClassName().contains(CONTROLLER))
                .collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(stackTraceElements)) {
            StackTraceElement stackTraceElement = stackTraceElements.get(0);
            String stackInfo = stackTraceElement.toString();

            String packageInfo = stackInfo.substring(stackInfo.indexOf(PACKAGE_PREFIX));
            packageInfo = packageInfo.substring(0, packageInfo.indexOf("("));
            String controllerName = packageInfo.substring(0, packageInfo.lastIndexOf(CharUtil.DOT));
            String methodName = packageInfo.substring(packageInfo.lastIndexOf(CharUtil.DOT) + 1);
            if(methodName.indexOf('$')>0){
                methodName = methodName.substring(0, methodName.indexOf('$'));
            }
            Collections.addAll(controllerInfo, controllerName, methodName);
        }
        return controllerInfo;
    }


}
