package com.ssyf.common.web.result;

import cn.hutool.core.util.StrUtil;
import com.ssyf.common.web.apicode.ApiCodeServer;
import com.ssyf.common.web.apicode.ApiCodeUser;
import com.ssyf.common.web.i18n.CustomMessageSource;
import com.ssyf.common.web.error.ServiceError;
import com.ssyf.common.web.error.ServiceException;
import com.ssyf.common.web.error.ServiceRuntimeException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
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.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * 全局异常拦截
 * @author huwei
 * @date 2022/7/5 9:50
 */
@Slf4j
@ResponseBody
@RestControllerAdvice
public class GlobalExceptionControllerAdvice {

    @Autowired
    private CustomMessageSource messageSource;

    /**
     * 未知异常处理
     */
    @ExceptionHandler(value = {Throwable.class, Exception.class, RuntimeException.class})
    public Result unknown(Throwable e) {
        log.error(new StringBuffer("未知异常: ").append(e.getMessage()).toString(), e);
        return i18n(ApiCodeServer.ERROR.getCode(), e.getMessage());
    }

    /**
     * 空指针异常
     */
    @ExceptionHandler(value = {NullPointerException.class})
    public Result nullPointException(NullPointerException e) {
        log.error(new StringBuffer("空指针异常: ").append(e.getMessage()).toString(), e);
        return i18n(ApiCodeServer.ERROR.getCode(), e.getMessage());
    }

    /**
     * 下标越界异常
     */
    @ExceptionHandler(value = {IndexOutOfBoundsException.class})
    public Result indexOutOfBoundsException(IndexOutOfBoundsException e) {
        log.error(new StringBuffer("下标越界异常: ").append(e.getMessage()).toString(), e);
        return i18n(ApiCodeServer.ERROR.getCode(), e.getMessage());
    }

    /**
     * 业务中断异常
     * @param e 异常对象
     * @return
     */
    @ExceptionHandler(value = {ServiceRuntimeException.class , ServiceException.class})
    public Result serviceInterruptionException(Exception e) {
        ServiceError error = (ServiceError) e;
        log.warn("业务异常:[{}]{}" ,error.getCode() ,e.getMessage());
        return i18n(error.getCode(), e.getMessage());
    }

    /**
     * 参数校验异常捕捉
     */
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public Object argsCheckException(MethodArgumentNotValidException exception) {
        StringBuilder sb = new StringBuilder();
        final List<FieldError> errorList = exception.getFieldErrors();
        for (FieldError curr : errorList) {
            sb.append(curr.getField()).append(curr.getDefaultMessage()).append(";");
        }
        return i18n(ApiCodeUser.PARAMETER_ERROR.getCode(),sb.toString());
    }

    /**
     * 绑定参数异常
     *
     * @param bindException 错误信息
     * @return 返回结果
     */
    @ExceptionHandler(value = {BindException.class})
    public Result bindException(BindException bindException) {
        StringBuffer errorMessage = new StringBuffer();
        List<ObjectError> objectErrorList = bindException.getAllErrors();
        objectErrorList.forEach(objectError -> {
            errorMessage.append(objectError.getDefaultMessage()).append("; ");
        });
        return i18n(ApiCodeUser.PARAMETER_ERROR.getCode(), "参数错误: " + errorMessage.toString());
    }

    /**
     * 缺少请求参数处理
     */
    @ExceptionHandler(value = {MissingServletRequestParameterException.class})
    public Result missingServletRequestParameterException(MissingServletRequestParameterException ex) {
        return Result.fail(ApiCodeUser.PARAMETER_ERROR.getCode(), new StringBuffer("缺少参数: ").append(ex.getParameterName()).toString());
    }

    /**
     * 违反约束处理
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public Result constraintViolationException(ConstraintViolationException e) {
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        Iterator<ConstraintViolation<?>> iter = violations.iterator();
        StringBuffer sb = new StringBuffer();
        while (iter.hasNext()) {
            ConstraintViolation<?> violation = iter.next();
            sb.append(violation.getMessage()).append(",");
        }
        return i18n(ApiCodeUser.PARAMETER_ERROR.getCode() ,sb.toString());
    }

    /**
     * 请求参数类型转换错误异常处理
     */
    @ExceptionHandler({MethodArgumentTypeMismatchException.class})
    public Result numberFormatException(MethodArgumentTypeMismatchException ex) {
        return i18n(ApiCodeUser.PARAMETER_ERROR.getCode() ,new StringBuffer("参数格式不匹配: ").append(ex.getName()).toString());
    }

    /**
     * 请求格式错误
     * @param ex
     * @return
     */
    @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
    public Result a(HttpRequestMethodNotSupportedException ex){
        ex.printStackTrace();
        return i18n(ApiCodeUser.PARAMETER_ERROR.getCode() ,new StringBuffer("请求格式错误：").append(ex.getMethod()).toString());
    }

    /**
     * 错误信息国际化处理
     * @param code
     * @param message
     * @return
     */
    private Result i18n(String code ,String message){
        String target = this.messageSource.getMessageDefaultNull(code ,null , LocaleContextHolder.getLocale());
        String result = StrUtil.isBlank(target) ? message : target;
        return Result.fail(code,result);
    }
}
