package com.clx.performance.config.exception;

import cn.dev33.satoken.exception.NotLoginException;
import com.msl.common.enums.ResultCodeEnum;
import com.msl.common.exception.ServiceSystemException;
import com.msl.common.result.Result;
import com.msl.user.enums.result.AuthorityResultEnum;
import com.msl.user.exception.LogoutSignException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

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

@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    @Autowired
    private RequestMappingHandlerMapping handlerMapping;

    @ExceptionHandler(value = ServiceSystemException.class)
    public Result exceptionHandler(ServiceSystemException e) {

        log.info("ServiceSystemException异常， code:{}, msg:{}, detail:{}", e.getResultEnum().getCode(), e.getResultEnum().getMsg(), e.getDetail(),e.getException());

        if (e.getException() != null){
            log.info("【ExceptionHandler】全局自定义业务异常捕获", e.getResultEnum());
        }
        return Result.fail(e.getResultEnum(), e.getDetail());
    }

    @ExceptionHandler(value = LogoutSignException.class)
    public Result exceptionHandler(LogoutSignException e) {

        log.info("======特殊异常，捕获到此异常，请将踢出登陆=====");

        if (e.getException() != null){
            log.info("【ExceptionHandler】全局自定义业务异常捕获", e.getResultEnum());
        }
        return Result.fail(AuthorityResultEnum.LOGOUT_SIGN);
    }

    @ExceptionHandler(value = Exception.class)
    public Result exceptionHandler(Exception e) {
        log.info("【ExceptionHandler】全局通用异常捕获", e);
        return Result.fail(ResultCodeEnum.FAIL.getMsg());
    }

    /**
     * token相关异常
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(NotLoginException.class)
    public Result<String> handlerNotLoginException(NotLoginException e) {
        log.info("NotLoginException异常-->{}", e.getMessage());
        // 判断场景值，定制化异常信息
        String message = e.getMessage();
        switch (e.getType()) {
            case NotLoginException.NOT_TOKEN:
                message = NotLoginException.NOT_TOKEN_MESSAGE;
                break;
            case NotLoginException.INVALID_TOKEN:
                message = NotLoginException.INVALID_TOKEN_MESSAGE;
                break;
            case NotLoginException.TOKEN_TIMEOUT:
                message = NotLoginException.TOKEN_TIMEOUT_MESSAGE;
                break;
            case NotLoginException.BE_REPLACED:
                message = NotLoginException.BE_REPLACED_MESSAGE;
                break;
            case NotLoginException.KICK_OUT:
                message = NotLoginException.KICK_OUT_MESSAGE;
                break;
            default:
                message = NotLoginException.DEFAULT_MESSAGE;
                break;
        }
        return Result.fail(AuthorityResultEnum.TOKEN_ERROR, message);
    }

    /**
     * 统一处理请求参数校验(普通传参)
     */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result<String> handleConstraintViolationException(ConstraintViolationException e) {
        StringBuilder sb = new StringBuilder();
        Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
        for (ConstraintViolation<?> violation : violations) {
//            Path path = violation.getPropertyPath();
//            String[] pathArr = path.toString().split("\\.");
//            sb.append(pathArr[1]).append(violation.getMessage()).append(",");
            sb.append(violation.getMessage()).append(",");
        }
        String errMsg = sb.substring(0, sb.length() - 1);
        return Result.fail(ResultCodeEnum.ILLEGAL_PARAMETER, errMsg);
    }

    /**
     * 统一处理请求参数校验(被@RequestBody注解的实体对象传参)
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result<String> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        StringBuilder sb = new StringBuilder();
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        for (FieldError error : fieldErrors) {
//            sb.append(error.getField()).append(error.getDefaultMessage()).append(",");
            sb.append(error.getDefaultMessage()).append(",");
        }
        String errMsg = sb.substring(0, sb.length() - 1);
        return Result.fail(ResultCodeEnum.ILLEGAL_PARAMETER, errMsg);
    }

    /**
     * 统一处理请求参数校验(普通实体对象传参)
     */
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result<String> handleBindException(BindException e) {
        StringBuilder sb = new StringBuilder();
        List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
        for (FieldError error : fieldErrors) {
//            sb.append(error.getField()).append(error.getDefaultMessage()).append(",");
            sb.append(error.getDefaultMessage()).append(",");
        }
        String errMsg = sb.substring(0, sb.length() - 1);
        return Result.fail(ResultCodeEnum.ILLEGAL_PARAMETER, errMsg);
    }

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    @ResponseStatus(HttpStatus.OK)
    public Result<String> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
        log.info("请求方式错误-->{}", e.getMessage());
        // 获取当前请求的方法
        String requestMethod = request.getMethod();
        // 获取接口支持的请求方式
        List<String> supportedMethods = getSupportedMethods(request);
        String supportedMethodsString = String.join(", ", supportedMethods);

        return Result.fail(ResultCodeEnum.ILLEGAL_METHOD, "请求方法错误,不支持请求方式:"+requestMethod+",请使用正确的请求方式:"+supportedMethodsString);
    }

    private List<String> getSupportedMethods(HttpServletRequest request) {
        RequestMappingInfo info = handlerMapping.getHandlerMethods().keySet().stream()
                .filter(mappingInfo -> mappingInfo.getPatternsCondition().getMatchingCondition(request) != null)
                .findFirst()
                .orElse(null);

        if (info != null) {
            RequestMethodsRequestCondition condition = info.getMethodsCondition();
            return condition.getMethods().stream()
                    .map(Enum::toString)
                    .collect(Collectors.toList());
        }

        return null;
    }

}
