package com.njcn.web.exception;

import cn.hutool.core.text.StrFormatter;
import cn.hutool.core.util.StrUtil;
//import com.alibaba.excel.exception.ExcelAnalysisException;
import com.njcn.common.pojo.annotation.OperateInfo;
import com.njcn.common.pojo.response.HttpResult;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.common.utils.HttpResultUtil;
import com.njcn.common.utils.LogUtil;
import com.njcn.common.utils.ReflectCommonUtil;
import com.njcn.web.annotation.ReturnMsg;
import com.njcn.web.service.ILogService;
import com.njcn.web.utils.ControllerUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.json.JSONException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.util.NestedServletException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 全局通用业务异常处理器
 *
 * @author hongawen
 * @version 1.0.0
 * @date 2021年04月20日 18:04
 */
@Slf4j
@AllArgsConstructor
@RestControllerAdvice
public class GlobalBusinessExceptionHandler {

    private final ILogService logService;

    /**
     * 捕获业务功能异常，通常为业务数据抛出的异常
     *
     * @param businessException 业务异常
     */
    @ExceptionHandler(BusinessException.class)
    public HttpResult<String> handleBusinessException(HttpServletRequest httpServletRequest, BusinessException businessException) {
        String operate = ReflectCommonUtil.getMethodDescribeByException(businessException);
        logService.recodeBusinessExceptionLog(businessException, httpServletRequest, businessException.getMessage());
        //判断方法上是否有自定义注解，做特殊处理
        Method method = ReflectCommonUtil.getMethod(businessException);
        if (!Objects.isNull(method)){
            if(method.isAnnotationPresent(ReturnMsg.class)){
                return HttpResultUtil.assembleResult(businessException.getCode(), null, StrFormatter.format("{}",businessException.getMessage()));
            }
        }
        return HttpResultUtil.assembleBusinessExceptionResult(businessException, null, operate);
    }


    /**
     * 空指针异常捕捉
     *
     * @param nullPointerException 空指针异常
     */
    @ExceptionHandler(NullPointerException.class)
    public HttpResult<String> handleNullPointerException(HttpServletRequest httpServletRequest, NullPointerException nullPointerException) {
        LogUtil.logExceptionStackInfo(CommonResponseEnum.NULL_POINTER_EXCEPTION.getMessage(), nullPointerException);
        logService.recodeBusinessExceptionLog(nullPointerException, httpServletRequest, CommonResponseEnum.NULL_POINTER_EXCEPTION.getMessage());
        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.NULL_POINTER_EXCEPTION, null, ReflectCommonUtil.getMethodDescribeByException(nullPointerException));
    }

    /**
     * 算数运算异常
     *
     * @param arithmeticException 算数运算异常，由于除数为0引起的异常
     */
    @ExceptionHandler(ArithmeticException.class)
    public HttpResult<String> handleArithmeticException(HttpServletRequest httpServletRequest, ArithmeticException arithmeticException) {
        LogUtil.logExceptionStackInfo(CommonResponseEnum.ARITHMETIC_EXCEPTION.getMessage(), arithmeticException);
        logService.recodeBusinessExceptionLog(arithmeticException, httpServletRequest, CommonResponseEnum.ARITHMETIC_EXCEPTION.getMessage());
        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.ARITHMETIC_EXCEPTION, null, ReflectCommonUtil.getMethodDescribeByException(arithmeticException));
    }

    /**
     * 类型转换异常捕捉
     *
     * @param classCastException 类型转换异常
     */
    @ExceptionHandler(ClassCastException.class)
    public HttpResult<String> handleClassCastException(HttpServletRequest httpServletRequest, ClassCastException classCastException) {
        LogUtil.logExceptionStackInfo(CommonResponseEnum.CLASS_CAST_EXCEPTION.getMessage(), classCastException);
        logService.recodeBusinessExceptionLog(classCastException, httpServletRequest, CommonResponseEnum.CLASS_CAST_EXCEPTION.getMessage());
        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.CLASS_CAST_EXCEPTION, null, ReflectCommonUtil.getMethodDescribeByException(classCastException));
    }


    /**
     * 索引下标越界异常捕捉
     *
     * @param indexOutOfBoundsException 索引下标越界异常
     */
    @ExceptionHandler(IndexOutOfBoundsException.class)
    public HttpResult<String> handleIndexOutOfBoundsException(HttpServletRequest httpServletRequest, IndexOutOfBoundsException indexOutOfBoundsException) {
        LogUtil.logExceptionStackInfo(CommonResponseEnum.INDEX_OUT_OF_BOUNDS_EXCEPTION.getMessage(), indexOutOfBoundsException);
        logService.recodeBusinessExceptionLog(indexOutOfBoundsException, httpServletRequest, CommonResponseEnum.INDEX_OUT_OF_BOUNDS_EXCEPTION.getMessage());
        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.INDEX_OUT_OF_BOUNDS_EXCEPTION, null, ReflectCommonUtil.getMethodDescribeByException(indexOutOfBoundsException));
    }

    /**
     * 前端请求后端，请求中参数的媒体方式不支持异常
     *
     * @param httpMediaTypeNotSupportedException 请求中参数的媒体方式不支持异常
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public HttpResult<String> httpMediaTypeNotSupportedExceptionHandler(HttpServletRequest httpServletRequest, HttpMediaTypeNotSupportedException httpMediaTypeNotSupportedException) {
        LogUtil.logExceptionStackInfo(CommonResponseEnum.HTTP_MEDIA_TYPE_NOT_SUPPORTED_EXCEPTION.getMessage(), httpMediaTypeNotSupportedException);
        // 然后提取错误提示信息进行返回
        logService.recodeBusinessExceptionLog(httpMediaTypeNotSupportedException, httpServletRequest, CommonResponseEnum.HTTP_MEDIA_TYPE_NOT_SUPPORTED_EXCEPTION.getMessage());
        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.HTTP_MEDIA_TYPE_NOT_SUPPORTED_EXCEPTION, null, ReflectCommonUtil.getMethodDescribeByException(httpMediaTypeNotSupportedException));
    }

    /**
     * 前端请求后端，参数校验异常捕捉
     * RequestBody注解参数异常
     *
     * @param methodArgumentNotValidException 参数校验异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public HttpResult<String> methodArgumentNotValidExceptionHandler(HttpServletRequest httpServletRequest, MethodArgumentNotValidException methodArgumentNotValidException) {
        // 从异常对象中拿到allErrors数据
        String messages = methodArgumentNotValidException.getBindingResult().getAllErrors()
                .stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining("；"));
        // 然后提取错误提示信息进行返回
        LogUtil.njcnDebug(log, "参数校验异常，异常为：{}", messages);
        logService.recodeBusinessExceptionLog(methodArgumentNotValidException, httpServletRequest, CommonResponseEnum.METHOD_ARGUMENT_NOT_VALID_EXCEPTION.getMessage());
        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.METHOD_ARGUMENT_NOT_VALID_EXCEPTION, messages, ControllerUtil.getMethodArgumentNotValidException(methodArgumentNotValidException));
    }

    /**
     * 前端请求后端，参数校验异常捕捉
     * PathVariable注解、RequestParam注解参数异常
     *
     * @param constraintViolationException 参数校验异常
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public HttpResult<String> constraintViolationExceptionExceptionHandler(HttpServletRequest httpServletRequest, ConstraintViolationException constraintViolationException) {
        String exceptionMessage = constraintViolationException.getMessage();
        StringBuilder messages = new StringBuilder();
        if (exceptionMessage.indexOf(StrUtil.COMMA) > 0) {
            String[] tempMessage = exceptionMessage.split(StrUtil.COMMA);
            Stream.of(tempMessage).forEach(message -> {
                messages.append(message.substring(message.indexOf(StrUtil.COLON) + 2)).append(';');
            });
        } else {
            messages.append(exceptionMessage.substring(exceptionMessage.indexOf(StrUtil.COLON) + 2));
        }
        // 然后提取错误提示信息进行返回
        LogUtil.njcnDebug(log, "参数校验异常，异常为：{}", messages);
        logService.recodeBusinessExceptionLog(constraintViolationException, httpServletRequest, CommonResponseEnum.METHOD_ARGUMENT_NOT_VALID_EXCEPTION.getMessage());
        List<ConstraintViolation<?>> constraintViolationList = new ArrayList<>(constraintViolationException.getConstraintViolations());
        ConstraintViolation<?> constraintViolation = constraintViolationList.get(0);
        Class<?> rootBeanClass = constraintViolation.getRootBeanClass();
        //判断校验参数异常捕获的根源是controller还是service处
        if (rootBeanClass.getName().endsWith("Controller")) {
            String methodName = constraintViolation.getPropertyPath().toString().substring(0, constraintViolation.getPropertyPath().toString().indexOf(StrUtil.DOT));
            return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.METHOD_ARGUMENT_NOT_VALID_EXCEPTION, messages.toString(), ReflectCommonUtil.getMethodDescribeByClassAndMethodName(rootBeanClass, methodName));
        } else {
            return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.METHOD_ARGUMENT_NOT_VALID_EXCEPTION, messages.toString(), ReflectCommonUtil.getMethodDescribeByException(constraintViolationException));
        }

    }


    /**
     * 索引下标越界异常捕捉
     *
     * @param illegalArgumentException 参数校验异常
     */
    @ExceptionHandler(IllegalArgumentException.class)
    public HttpResult<String> handleIndexOutOfBoundsException(HttpServletRequest httpServletRequest, IllegalArgumentException illegalArgumentException) {
        LogUtil.logExceptionStackInfo(CommonResponseEnum.ILLEGAL_ARGUMENT_EXCEPTION.getMessage(), illegalArgumentException);
        logService.recodeBusinessExceptionLog(illegalArgumentException, httpServletRequest, CommonResponseEnum.ILLEGAL_ARGUMENT_EXCEPTION.getMessage());
        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.ILLEGAL_ARGUMENT_EXCEPTION, illegalArgumentException.getMessage(), ReflectCommonUtil.getMethodDescribeByException(illegalArgumentException));
    }

//    /**
//     * 表格校验异常
//     *
//     * @param excelAnalysisException 表格校验异常
//     */
//    @ExceptionHandler(ExcelAnalysisException.class)
//    public HttpResult<String> handleExcelAnalysisException(HttpServletRequest httpServletRequest, ExcelAnalysisException excelAnalysisException) {
//        LogUtil.logExceptionStackInfo(CommonResponseEnum.ILLEGAL_ARGUMENT_EXCEPTION.getMessage(), excelAnalysisException);
//        //   logService.recodeBusinessExceptionLog(excelAnalysisException, httpServletRequest,CommonResponseEnum.ILLEGAL_ARGUMENT_EXCEPTION.getMessage());
//        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.ILLEGAL_ARGUMENT_EXCEPTION, excelAnalysisException.getCause().getMessage(), ReflectCommonUtil.getMethodDescribeByException(excelAnalysisException));
//    }


    /**
     * 未声明异常捕捉
     *
     * @param exception 未声明异常
     */
    @ExceptionHandler(Exception.class)
    public HttpResult<String> handleException(HttpServletRequest httpServletRequest, Exception exception) {
        //针对fallbackFactory降级异常特殊处理
        Exception tempException = exception;
        String exceptionCause = CommonResponseEnum.UN_DECLARE.getMessage();
        String code = CommonResponseEnum.UN_DECLARE.getCode();
        if (exception instanceof NestedServletException) {
            Throwable cause = exception.getCause();
            if (cause instanceof AssertionError) {
                if (cause.getCause() instanceof BusinessException) {
                    tempException = (BusinessException) cause.getCause();
                    BusinessException tempBusinessException = (BusinessException) cause.getCause();
                    exceptionCause = tempBusinessException.getMessage();
                    code = tempBusinessException.getCode();
                }
            }
        }
        LogUtil.logExceptionStackInfo(exceptionCause, tempException);
        logService.recodeBusinessExceptionLog(tempException, httpServletRequest, exceptionCause);
        //判断方法上是否有自定义注解，做特殊处理
        Method method = ReflectCommonUtil.getMethod(exception);
        if (!Objects.isNull(method)){
            if(method.isAnnotationPresent(ReturnMsg.class)){
                return HttpResultUtil.assembleResult(code, null, StrFormatter.format("{}",exceptionCause));
            }
        }
        return HttpResultUtil.assembleResult(code, null, StrFormatter.format("{}{}{}", ReflectCommonUtil.getMethodDescribeByException(tempException), StrUtil.C_COMMA, exceptionCause));
    }




    /**
     * json解析异常
     *
     * @param jsonException json参数
     */
    @ExceptionHandler(JSONException.class)
    public HttpResult<String> handleIndexOutOfBoundsException(HttpServletRequest httpServletRequest, JSONException jsonException) {
        LogUtil.logExceptionStackInfo(CommonResponseEnum.JSON_CONVERT_EXCEPTION.getMessage(), jsonException);
        logService.recodeBusinessExceptionLog(jsonException, httpServletRequest, CommonResponseEnum.JSON_CONVERT_EXCEPTION.getMessage());
        return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.JSON_CONVERT_EXCEPTION, jsonException.getMessage(), ReflectCommonUtil.getMethodDescribeByException(jsonException));
    }

}
