package com.sunyard.chsm.config;

import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.sunyard.chsm.model.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.IllegalFormatException;
import java.util.List;
import java.util.Optional;

/**
 * 全局controller异常处理
 *
 * @auther: ZS
 * @description:
 * @param:
 * @return:
 * @date: 2020/5/25 16:24
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionResolver {

    /**
     * 校验器
     */
    @ExceptionHandler(BindException.class)
    @ResponseBody
    public R<?> handleBindException(BindException e) {
        log.info("校验器异常", e);
        FieldError fieldError = e.getBindingResult().getFieldError();
        return R.error(400, fieldError.getDefaultMessage());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public R<?> validExceptionHandler(MethodArgumentNotValidException ex) {
        return Optional.of(ex)
                .map(MethodArgumentNotValidException::getFieldError)
                .map(FieldError::getDefaultMessage)
                .map(message -> R.error(400, message))
                .get();
    }

    private static final List<Class<? extends Exception>> EX_CLASS = Arrays.asList(
            IllegalArgumentException.class, IllegalStateException.class,
            IllegalFormatException.class,
            UnsupportedOperationException.class);

    @ExceptionHandler(Exception.class)
    public R<?> exceptionHandler(Exception ex, HttpServletRequest request) {
        InvalidFormatException ife = findException(InvalidFormatException.class, ex);
        if (ife != null) {
            return R.error(400, "参数异常, 不支持的值: " + ife.getValue());
        }
        String errorMessage = buildErrorMessage(ex);
        for (Class<? extends Exception> eClass : EX_CLASS) {
            Exception exception = findException(eClass, ex);
            if (exception == null) {
                continue;
            }
            log.warn("Validation failed -> {} ", errorMessage);
            return R.error(400, exception.getMessage());
        }
        log.error("request: {} - {}, 系统异常 -> ", request.getMethod(), request.getRequestURI(), ex);
        return R.error("系统异常");
    }

    public static <T extends Exception> T findException(Class<T> exClass, Throwable ex) {
        for (int i = 0; ex != null && i < 3; i++) {
            if (exClass.isAssignableFrom(ex.getClass())) {
                //noinspection unchecked
                return (T) ex;
            }
            ex = ex.getCause();
        }
        return null;
    }

    public static String buildErrorMessage(Throwable ex) {
        StringBuilder sb = new StringBuilder();
        sb.append(ex.getMessage());
        StackTraceElement[] stackTrace = ex.getStackTrace();
        int i = 0;
        for (StackTraceElement stackTraceElement : stackTrace) {
            if (i++ >= 5) {
                break;
            }
            sb.append("\n\t").append(stackTraceElement.toString());
        }
        return sb.toString();
    }

}
