package com.cyksj.web;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import cn.hutool.core.lang.Validator;
import com.cyksj.common.exception.BusinessRuntimeException;
import com.cyksj.common.util.StringUtil;
import com.cyksj.dto.Result;
import com.cyksj.enums.GatewayApiCode;
import com.cyksj.enums.GatewayResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

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

/**
 * @description 异常全局拦截
 * @author chan
 * @date 2021-03-22 9:51 下午
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    @Autowired
    protected HttpServletRequest request;

    private final static List<String> token = List.of("token无效", "token已过期");

    @ExceptionHandler(value = {Exception.class})
    public Result<String> handleNoteException(Exception e) {
        StringBuilder builder = new StringBuilder(32);
        builder.append(request.getRequestURI()).append(" -- > ");

        StackTraceElement[] trace = e.getStackTrace();
        log.info("{}",e);
        for (StackTraceElement element : trace) {
            if (StringUtil.startsWith(element.getClassName(), "com.cyksj")) {
                builder.append(element.getFileName())
                        .append("[").append(element.getLineNumber()).append("]")
                        .append("&");
            }
        }
        builder.deleteCharAt(builder.length() - 1);

        String message = e.getMessage();
        String value = builder.toString();
        GatewayApiCode apiCode = null;
        if (e instanceof BusinessRuntimeException) {
            BusinessRuntimeException ee = (BusinessRuntimeException) e;
            String code = ee.getCode();
            if (Validator.isNumber(code)) {
                if (GatewayApiCode.CMS_TOKEN_VALID.getCode().equals(Integer.parseInt(code))) {
                    apiCode = GatewayApiCode.CMS_TOKEN_VALID;
                }
            }
        }
        if (e instanceof NotLoginException) {
            //未登录
            NotLoginException ee = (NotLoginException) e;
            if (token.contains(ee.getMessage())) {
                apiCode = GatewayApiCode.CMS_TOKEN_VALID;
            } else if ("token已被踢下线".equals(ee.getMessage())) {
                apiCode = GatewayApiCode.CMS_TOKEN_OUT_LINE;
            } else {
                apiCode = GatewayApiCode.CMS_USER_NO_LOGIN;
            }
        } else if (e instanceof NotRoleException) {
            // 如果是角色异常
            NotRoleException ee = (NotRoleException) e;
            apiCode = GatewayApiCode.CMS_USER_NO_ROLE;
        } else if (e instanceof NotPermissionException) {
            // 如果是权限异常
            NotPermissionException ee = (NotPermissionException) e;
            apiCode = GatewayApiCode.CMS_USER_NO_PERMISSION;
        }
        log.error("全局错误处理: \n Cause: {} \n Value: {}", message, value);
        if (apiCode != null) {
            return GatewayResponse.FAIL.newBuilder().buildGatewayCode(apiCode).toResult();
        }
        return GatewayResponse.FAIL.newBuilder().setMsg(message).toResult(value);
    }

    @ExceptionHandler(value = ConstraintViolationException.class)
    public Result<String> ConstraintViolationException(ConstraintViolationException ex) {
        String message = "";
        // 使用TreeSet是为了让输出的内容有序输出(默认验证的顺序是随机的)
        Set<String> errorInfoSet = new TreeSet<String>();
        Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
        if (!violations.isEmpty()) {
            for (ConstraintViolation<?> item : violations) {
                System.out.println(item.getPropertyPath());
                // 遍历错误字段信息
                errorInfoSet.add(item.getMessage());
            }

            StringBuilder sbf = new StringBuilder();
            for (String errorInfo : errorInfoSet) {
                sbf.append(errorInfo);
                sbf.append(",");
            }
            message = sbf.substring(0, sbf.length() - 1);
        }

        log.error("错误字段 信息: \n Value: {}", message);

        return GatewayResponse.FAIL.newBuilder().buildGatewayCode(GatewayApiCode.PARAMETER_MISSING_ERROR).setMsg(message).toResult();
    }

    @ExceptionHandler(BindException.class)
    public Result<String> BindException(BindException bindingResult) {

        // 验证参数信息是否有效
        if (bindingResult.hasErrors()) {
            // 获取错误字段信息集合
            List<FieldError> fieldErrorList = bindingResult.getFieldErrors();

            // 使用TreeSet是为了让输出的内容有序输出(默认验证的顺序是随机的)
            Set<String> errorInfoSet = new TreeSet<String>();
            for (FieldError fieldError : fieldErrorList) {
                // 遍历错误字段信息
                errorInfoSet.add(fieldError.getDefaultMessage());
            }

            StringBuilder sbf = new StringBuilder();
            for (String errorInfo : errorInfoSet) {
                sbf.append(errorInfo);
                sbf.append(",");
            }
            String message = sbf.substring(0, sbf.length() - 1);

            log.error("验证参数信息是否有效 错误信息: \n Value: {}", message);

            return GatewayResponse.FAIL.newBuilder().buildGatewayCode(GatewayApiCode.PARAMETER_MISSING_ERROR).setMsg(message).toResult();

        }
        return GatewayResponse.SUCCESS.newBuilder().buildGatewayCode(GatewayApiCode.SUCCESS).setMsg("参数校验通过").toResult();
    }
}