package com.weather.common.exception;

import com.weather.vo.ApiResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingRequestHeaderException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
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.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolationException;
import java.util.List;

/**
 * @author : fengyuhao
 * @desc : 类描述: 全局异常拦截处理器
 * 1.处理自定义异常
 * 2.未知异常统一返回服务器错误
 * 3.已经catch到的异常不会被捕获
 * 4.异常的体系结构中，哪个异常与目标方法抛出的异常血缘关系越紧密，就会被哪个捕捉到。
 * @update : 修改说明
 * @date : 2023/7/28-15:46

 * @see ExceptionHandler ：统一处理某一类异常，从而能够减少代码重复率和复杂度
 * @see ResponseStatus ：可以将某种异常映射为HTTP状态码 成功则Status Code: 200
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    private static final String TOKEN_NAME="token";

    /**
     * 400 - Bad Request
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({ApiException.class, HttpMessageNotReadableException.class, MissingServletRequestParameterException.class, BindException.class,
            ServletRequestBindingException.class, MethodArgumentNotValidException.class, ConstraintViolationException.class})
    public ApiResult handleHttpMessageNotReadableException(Exception e) {
        log.error("自定义 参数解析失败", e);
        if (e instanceof BindException) {
            return ApiResult.error(((BindException) e).getAllErrors().get(0).getDefaultMessage());
        }
        if (e instanceof MethodArgumentNotValidException) {

            List<ObjectError> allErrors = ((MethodArgumentNotValidException) e).getBindingResult().getAllErrors();

            return ApiResult.error(allErrors.get(0).getDefaultMessage());

        }else if(e instanceof HttpMessageNotReadableException){
            return ApiResult.error(e.getMessage());

        }else if(e instanceof MissingRequestHeaderException){
            MissingRequestHeaderException exception = (MissingRequestHeaderException) e;
            if (!StringUtils.isEmpty(exception.getHeaderName())&&exception.getHeaderName().equals(TOKEN_NAME)){
                return ApiResult.errorToken();
            }


        }

        return ApiResult.error(e.getMessage());
    }
    /**
     * 拦截所有运行时的全局异常
     */
    @ExceptionHandler(RuntimeException.class)
    @ResponseBody
    public ApiResult runtimeException(RuntimeException e) {
        log.error("运行时异常", e);
        // 返回 JOSN
        String message="运行时异常";
        if (StringUtils.isNotEmpty(e.getMessage())){
            message=e.getMessage();
        }
        return ApiResult.error(message);
    }


    /**
     * 405 - Method Not Allowed
     * 带有@ResponseStatus注解的异常类会被ResponseStatusExceptionResolver 解析
     */
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ApiResult handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        log.error("不支持当前请求方法", e);
        return ApiResult.error("不支持当前请求方法");
    }

    /**
     * 其他全局异常在此捕获
     *

     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Throwable.class)
    public ApiResult handleException(Throwable e) {
        log.error("自定义 服务运行异常", e);

        return ApiResult.error("自定义异常处理-"+e.getMessage());
    }

    /**
     * 系统异常捕获处理
     */
    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ApiResult exception(Exception e) {
        log.error(e.getMessage(), e);
        return ApiResult.error("系统异常");
    }

}
