package cn.sh.stc.sict.cloud.common.security.component;

import cn.sh.stc.sict.cloud.common.core.constant.Constant;
import cn.sh.stc.sict.cloud.common.core.exception.BusinessException;
import cn.sh.stc.sict.cloud.common.core.util.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
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.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import java.util.stream.Collectors;

/**
 * @Description 全局异常处理器
 * @Author
 * @Date
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandlerResolver {

    /**
     * 全局异常.
     *
     * @param e the e
     * @return R
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.OK)
    public R handleGlobalException(HttpServletRequest request,Exception e) {
        log.error("全局异常信息==[ " + request.getRequestURI() + "] 接口出现错误");
        log.error("全局异常信息 ex={}", e.getMessage(), e);
        R<Object> objectR = R.builder()
                .code(Constant.BYTE_NO)
                .msg(e.getLocalizedMessage())
                .build();
        if (e instanceof HttpRequestMethodNotSupportedException) {
            objectR.setMsg("Request method is not supported");
        } else if (e instanceof HttpMessageNotReadableException
                || e instanceof HttpMediaTypeNotSupportedException
                || e instanceof MissingServletRequestParameterException) {
            objectR.setMsg("Request parameters are not supported");
        }

        return objectR;
    }

    /**
     * Business Exception
     * <p>
     * 业务逻辑异常处理
     *
     * @param exception
     * @return R
     */
    @ExceptionHandler({BusinessException.class})
    @ResponseStatus(HttpStatus.OK)
    public R handleBodyValidException(BusinessException exception) {
        R<Object> objectR = R.builder()
                .code(Constant.BYTE_NO)
                .bizCode(exception.getCode())
                .msg(exception.getMessage())
                .build();

        return objectR;
    }

    /**
     * AccessDeniedException
     *
     * @param e the e
     * @return R
     */
    @ExceptionHandler(AccessDeniedException.class)
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public R handleAccessDeniedException(AccessDeniedException e) {
        String msg = SpringSecurityMessageSource.getAccessor()
                .getMessage("AbstractAccessDecisionManager.accessDenied"
                        , e.getMessage());
        log.error("拒绝授权异常信息 ex={}", msg, e);
        return R.builder()
                .msg(msg)
                .code(Constant.BYTE_NO)
                .build();
    }

    /**
     * validation Exception
     * <p>
     * 请求的 JSON 参数在请求体内的参数校验
     *
     * @param exception
     * @return R
     */
    @ExceptionHandler({MethodArgumentNotValidException.class, BindException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public R handleBodyValidException(MethodArgumentNotValidException exception) {
        FieldError fieldError = exception.getBindingResult().getFieldErrors().get(0);
        log.error("参数绑定异常,ex = 字段[{}] {}", fieldError.getField(), fieldError.getDefaultMessage());
        return R.builder()
                .msg(fieldError.getField() + " " + fieldError.getDefaultMessage())
                .code(Constant.BYTE_NO)
                .build();
    }

    /**
     * 请求的 URL 参数检验
     *
     * @param e 异常信息
     * @return 返回提示信息
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(ConstraintViolationException.class)
    public R handleBindException2(ConstraintViolationException e) {
        String str = e.getConstraintViolations().stream().limit(1).map(x -> x.getMessage()).collect(Collectors.joining());
        log.error("url 参数绑定异常,ex = {}", str);
        return R.builder()
                .msg(str)
                .code(Constant.BYTE_NO)
                .build();
    }
}
