package com.yn.bftl.thirdparty.advice;

import com.alibaba.fastjson.JSON;
import com.yn.bftl.common.common.exception.YnceError;
import com.yn.bftl.thirdparty.common.exception.ExceptionDTO;
import com.yn.bftl.thirdparty.common.exception.YnceErrorException;
import com.yn.bftl.thirdparty.common.vo.ResponseVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.orm.ObjectOptimisticLockingFailureException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.Map;

/**
 * 处理Controller抛出的异常
 *
 * @author huabiao
 * @create 2020/06/20  09:16
 **/
@ControllerAdvice
@Slf4j
public class ExceptionAdvice {

    /**
     * 全部未知的异常
     *
     * @param e
     * @param request
     * @param response
     * @return
     */
    @ExceptionHandler()
    @ResponseBody
    public ResponseVO handleRuntimeException(RuntimeException e, HttpServletRequest request, HttpServletResponse response) {
        ExceptionDTO dto = new ExceptionDTO();
        dto.setMessage("服务异常");
        dto.setPath(request.getRequestURI());
        dto.setCode(String.valueOf(HttpStatus.BAD_REQUEST.value()));
        log.error(e.getMessage(), e);
        return ResponseVO.newInstanceException(dto);
    }

    /**
     * 业务错误
     *
     * @param e
     * @param request
     * @param response
     * @return
     */
    @ExceptionHandler(YnceErrorException.class)
    @ResponseBody
    public ResponseVO handleYnceErrorException(YnceErrorException e, HttpServletRequest request, HttpServletResponse response) {
        String[] str = e.getMessage().split("#");
        ExceptionDTO dto = new ExceptionDTO();
        String s = "";
        if (str.length > 2) {
            for (int i = 1; i < str.length; i++) {
                s = s + str[i] + "#";
            }
            s = s.substring(0, s.length() - 1);
        }
        if (s.equals("")) {
            dto.setMessage(str[1]);
        } else {
            dto.setMessage(s);
        }
        dto.setError(e.toString());
        dto.setPath(request.getRequestURI());
        dto.setCode(str[0]);
        return ResponseVO.newInstanceException(dto);
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public ResponseVO handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest request, HttpServletResponse response) {
        Map<String, String> errorMap = new HashMap<>(4);
        String fieldErrorMessage = null;
        String field = null;
        for (FieldError fieldError : e.getBindingResult().getFieldErrors()) {
            errorMap.put(fieldError.getField(), fieldError.getDefaultMessage());
            fieldErrorMessage = fieldError.getDefaultMessage();
            field = "(" + fieldError.getField() + ")";
        }
        ExceptionDTO dto = new ExceptionDTO();
        dto.setMessage(YnceError.YNCE_100001.getMsg() + field + ":" + fieldErrorMessage);
        dto.setError(JSON.toJSONString(errorMap));
        dto.setPath(request.getRequestURI());
        dto.setCode(YnceError.YNCE_100001.getStatus());
        return ResponseVO.newInstanceException(dto);
    }

    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public ResponseVO handleConstraintViolationException(ConstraintViolationException e, HttpServletRequest request, HttpServletResponse response) {
        Map<String, String> errorMap = new HashMap<>(4);
        String fieldErrorMessage = null;
        String field = null;
        for (ConstraintViolation<?> constraintViolation : e.getConstraintViolations()) {
            errorMap.put(constraintViolation.getPropertyPath().toString(), constraintViolation.getMessage());
            fieldErrorMessage = constraintViolation.getMessage();
            field = "(" + constraintViolation.getPropertyPath().toString() + ")";
        }
        ExceptionDTO dto = new ExceptionDTO();
        dto.setMessage(YnceError.YNCE_100001.getMsg() + field + ":" + fieldErrorMessage);
        dto.setError(JSON.toJSONString(errorMap));
        dto.setPath(request.getRequestURI());
        dto.setCode(YnceError.YNCE_100001.getStatus());
        return ResponseVO.newInstanceException(dto);
    }

    /**
     * 参数序列化失败
     *
     * @param e
     * @param request
     * @param response
     * @return
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseBody
    public ResponseVO handleHttpMessageNotReadableException(HttpMessageNotReadableException e, HttpServletRequest request, HttpServletResponse response) {
        log.error("参数序列化失败：" + e.getMessage());
        ExceptionDTO dto = new ExceptionDTO();
        dto.setMessage(YnceError.YNCE_100001.getMsg() + ":序列化失败");
        dto.setPath(request.getRequestURI());
        dto.setCode(YnceError.YNCE_100001.getStatus());
        return ResponseVO.newInstanceException(dto);
    }

    /**
     * @param e        异常
     * @param request  请求
     * @param response 返回
     * @return ResponseVO
     * @author huabiao
     * @create 2023/2/27  16:33
     */
    @ExceptionHandler(ObjectOptimisticLockingFailureException.class)
    @ResponseBody
    public ResponseVO handleStaleObjectStateException(ObjectOptimisticLockingFailureException e, HttpServletRequest request, HttpServletResponse response) {
        log.error("乐观锁异常：" + e.getMessage());
        ExceptionDTO dto = new ExceptionDTO();
        dto.setMessage(YnceError.YNCE_000000.getMsg());
        dto.setPath(request.getRequestURI());
        dto.setCode(YnceError.YNCE_000000.getStatus());
        return ResponseVO.newInstanceException(dto);
    }
}