package com.junmp.jyzb.boot.core.handle;

import cn.hutool.core.util.StrUtil;
import com.junmp.v2.auth.api.exception.AuthException;
import com.junmp.v2.common.bean.response.ApiRes;
import com.junmp.v2.common.constant.SymbolConstant;
import com.junmp.v2.common.exception.base.ServiceException;
import com.junmp.v2.validator.api.exception.ParamValidateException;
import com.junmp.v2.validator.api.exception.ValidatorExceptionEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ValidationException;
import java.util.List;

/**
 * <pre>
 *  TODO: 未实现
 * 描述：全局异常处理器
 * 版本：1.0.0
 * 日期：2021/6/21 3:29 下午
 * 作者：ningzp@junmp.com.cn
 * <br>修改记录
 * <br>修改日期        修改人          修改内容
 *
 * </pre>
 */
@Slf4j
@ControllerAdvice
public class GlobalExceptionHandler {


    /**
     * 请求参数缺失异常
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseBody
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiRes missingParam(MissingServletRequestParameterException e) {
        String parameterName = e.getParameterName();
        String parameterType = e.getParameterType();
        log.error("请求参数异常>>[" + parameterName + "][" + parameterType + "]");
        return ApiRes.failure(ValidatorExceptionEnum.MISSING_SERVLET_REQUEST_PARAMETER_EXCEPTION);
    }


    /**
     * HttpMessageConverter转化异常，一般为json解析异常
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseBody
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiRes httpMessageNotReadable(HttpMessageNotReadableException httpMessageNotReadableException) {
        log.error("参数格式传递异常，错误信息为：{}", httpMessageNotReadableException.getMessage());
        return ApiRes.failure(ValidatorExceptionEnum.HTTP_MESSAGE_CONVERTER_ERROR);
    }


    /**
     * 拦截不支持媒体类型异常
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    @ResponseBody
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiRes httpMediaTypeNotSupport(HttpMediaTypeNotSupportedException httpMediaTypeNotSupportedException) {
        log.error("参数格式传递异常，错误信息为：{}", httpMediaTypeNotSupportedException.getMessage());
        return ApiRes.failure(ValidatorExceptionEnum.HTTP_MEDIA_TYPE_NOT_SUPPORT);
    }


    /**
     * 不受支持的http method
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseBody
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiRes methodNotSupport(HttpServletRequest request) {
        String httpMethod = request.getMethod().toUpperCase();
        log.error("不支持的http请求方法>>" + httpMethod);
        return ApiRes.failure(ValidatorExceptionEnum.HTTP_METHOD_NOT_SUPPORT);
    }


    /**
     * 404找不到资源
     */
    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseBody
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiRes notFound(NoHandlerFoundException e) {
        return ApiRes.failure(ValidatorExceptionEnum.NOT_FOUND);
    }


    /**
     * 请求参数校验失败，拦截 @Valid 校验失败的情况
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiRes methodArgumentNotValidException(MethodArgumentNotValidException e) {
        String bindingResult = getArgNotValidMessage(e.getBindingResult());
        log.error("绑定异常>>" + bindingResult);
        return ApiRes.failure(ValidatorExceptionEnum.VALIDATED_RESULT_ERROR.getCode(),
                StrUtil.format(ValidatorExceptionEnum.VALIDATED_RESULT_ERROR.getMessage(),bindingResult));
    }


    /**
     * 请求参数校验失败，拦截 @Validated 校验失败的情况
     * <p>
     * 两个注解 @Valid 和 @Validated 区别是后者可以加分组校验，前者没有分组校验
     */
    @ExceptionHandler(BindException.class)
    @ResponseBody
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiRes bindException(BindException e) {
        String bindingResult = getArgNotValidMessage(e.getBindingResult());
        log.error("绑定异常>>" + bindingResult);
        return ApiRes.failure(ValidatorExceptionEnum.VALIDATED_RESULT_ERROR);
    }

    /**
     * 拦截 @TableUniqueValue 里抛出的异常
     */
    @ExceptionHandler(ValidationException.class)
    @ResponseBody
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiRes bindException(ValidationException e) {
        if (e.getCause() instanceof ParamValidateException) {
            ParamValidateException paramValidateException = (ParamValidateException) e.getCause();
            return ApiRes.failure(paramValidateException.getCode(), paramValidateException.getMessage());
        }
        return ApiRes.failure(e.getMessage());
    }

    /**
     * 拦截全校校验一类的异常
     */
    @ExceptionHandler(AuthException.class)
    //@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public ApiRes authError(AuthException authException) {
        log.error(">授权异常>>" + authException.getMessage());
        if (null != authException) {
            // 默认响应前端json
            return ApiRes.failure(authException.getCode(), authException.getMessage());
        } else {
            return ApiRes.failure("");
        }
    }


    /**
     * 拦截业务代码抛出的异常
     */
    @ExceptionHandler(ServiceException.class)
    //@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public ApiRes businessError(ServiceException e) {
        log.error("业务异常，错误信息为：{}", e.getMessage());
        return ApiRes.failure(e.getCode(), e.getMessage());
    }

    /**
     * 拦截未知的运行时异常
     */
    @ExceptionHandler(Throwable.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ResponseBody
    public ApiRes serverError(Throwable e) {
        log.error("服务器运行异常", e);
        return ApiRes.failure(e.getMessage());
    }


    /**
     * 获取请求参数不正确的提示信息
     * <p>
     * 多个信息，拼接成用逗号分隔的形式
     */
    private String getArgNotValidMessage(BindingResult bindingResult) {
        if (bindingResult == null) {
            return "";
        }
        StringBuilder stringBuilder = new StringBuilder();

        //多个错误用逗号分隔
        List<ObjectError> allErrorInfos = bindingResult.getAllErrors();
        for (ObjectError error : allErrorInfos) {
            stringBuilder.append(SymbolConstant.COMMA).append(error.getDefaultMessage());
        }

        //最终把首部的逗号去掉
        return StrUtil.removePrefix(stringBuilder.toString(), SymbolConstant.COMMA);
    }

}
