package cn.chnmuseum.party.common.mvc;

import cn.chnmuseum.party.common.enums.RESPONSE_CODE_ENUM;
import cn.chnmuseum.party.common.enums.RESULT_INFO_ENUM;
import com.google.common.base.Splitter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
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 org.springframework.web.multipart.support.MissingServletRequestPartException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.sql.SQLIntegrityConstraintViolationException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 全局异常处理
 *
 * @author Danny Lee
 * @date 2019/8/1
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionAdvisor {

    /**
     * 捕获全局异常,处理所有不可知的异常
     *
     * @param exception 异常对象
     */
    @ExceptionHandler(Exception.class)
    public Object handleException(Exception exception) {
        log.error("\r\n ************** 操作出现异常:{}", ExceptionUtils.getStackTrace(exception));
        Class<?> eClass = exception.getClass();
        cn.chnmuseum.party.common.validator.HttpResult httpResult = new cn.chnmuseum.party.common.validator.HttpResult();
        if (eClass.equals(HttpRequestMethodNotSupportedException.class)) {
            addResCodeToMap(RESPONSE_CODE_ENUM.METHOD_NOT_SUPPORTED, httpResult);
        } else if (eClass.equals(HttpMediaTypeNotAcceptableException.class)) {
            addResCodeToMap(RESPONSE_CODE_ENUM.MEDIA_TYPE_NOT_ACCEPT, httpResult);
        } else if (eClass.equals(HttpMediaTypeNotSupportedException.class)) {
            addResCodeToMap(RESPONSE_CODE_ENUM.MEDIA_TYPE_NOT_SUPPORTED, httpResult);
        } else if (eClass.equals(ConversionNotSupportedException.class)) {
            addResCodeToMap(RESPONSE_CODE_ENUM.SERVER_ERROR, httpResult);
        } else if (eClass.equals(HttpMessageNotWritableException.class)) {
            addResCodeToMap(RESPONSE_CODE_ENUM.SERVER_ERROR, httpResult);
        } else {
            addResCodeToMap(RESPONSE_CODE_ENUM.SERVER_ERROR, httpResult);
        }
        return httpResult;
    }

    @ExceptionHandler(BindException.class)
    @ResponseBody
    public cn.chnmuseum.party.common.validator.HttpResult handle(BindException ex) {
        String message = ex.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(" \n "));
        //        BindingResult bindingResult = ex.getBindingResult();
        //        StringBuilder errMsg = new StringBuilder(bindingResult.getFieldErrors().size() * 16);
        //        errMsg.append("Invalid request:");
        //        for (int i = 0; i < bindingResult.getFieldErrors().size(); i++) {
        //            if (i > 0) {
        //                errMsg.append(",");
        //            }
        //            FieldError error = bindingResult.getFieldErrors().get(i);
        //            errMsg.append(error.getField()).append(":").append(error.getDefaultMessage());
        //        }

//        String message1 = Splitter.onPattern("\r\n|\n|\r").trimResults().splitToList(ex.getLocalizedMessage()).get(1);
//        if (message1 != null) {
//            String substring = message1.substring(message1.indexOf("on field"), message1.indexOf("rejected value"));
//            if (substring.toUpperCase().contains("REMARKS")) {
//                return new cn.chnmuseum.party.common.validator.HttpResult("400", "备注字数超过" + message1, "");
//            }
//        }
        return new cn.chnmuseum.party.common.validator.HttpResult("400", message, "");
    }

    //处理请求参数格式错误 @RequestParam上validate失败后抛出的异常是javax.validation.ConstraintViolationException
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public String constraintViolationExceptionHandler(ConstraintViolationException e) {
        return e.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining());
    }

    /**
     * 处理请求参数格式错误 @RequestBody上validate失败后抛出的异常是MethodArgumentNotValidException异常。
     *
     * @param exception 错误信息集合
     * @return 错误信息
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public cn.chnmuseum.party.common.validator.HttpResult validationBodyException(MethodArgumentNotValidException exception) {
        BindingResult result = exception.getBindingResult();
        String msg = null;
        if (result.hasErrors()) {
            List<ObjectError> errors = result.getAllErrors();
            for (ObjectError objectError : errors) {
                FieldError fieldError = (FieldError) objectError;
                if (fieldError.getDefaultMessage() != null) {
                    log.info("Data check failure : object{" + fieldError.getObjectName() + "},field{" + fieldError.getField() +
                            "},errorMessage{" + fieldError.getDefaultMessage() + "}");
                    msg = fieldError.getDefaultMessage();
                }
            }
        }
        return new cn.chnmuseum.party.common.validator.HttpResult("400", msg);
    }

    @ExceptionHandler(DataAccessException.class)
    public cn.chnmuseum.party.common.validator.HttpResult dataAccessException(DataAccessException exception) {
        return new cn.chnmuseum.party.common.validator.HttpResult("400", exception.getLocalizedMessage());
    }

    @ExceptionHandler(DuplicateKeyException.class)
    @ResponseBody
    public cn.chnmuseum.party.common.validator.HttpResult duplicateKeyException(DuplicateKeyException e) {
        String localizedMessage = e.getLocalizedMessage();
        String message = Splitter.on(System.lineSeparator()).trimResults().splitToList(localizedMessage).get(1);
        String substring = message.substring(message.indexOf("Exception:"));
        if (substring.toUpperCase().contains("NAME")) {
            return new cn.chnmuseum.party.common.validator.HttpResult("400", "名称已存在,请修改名称", "");
        } else if (substring.toUpperCase().contains("CODE")) {
            return new cn.chnmuseum.party.common.validator.HttpResult("400", "编码已存在", "");
        }
        return new cn.chnmuseum.party.common.validator.HttpResult("400", message, "");
    }

    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    @ResponseBody
    public cn.chnmuseum.party.common.validator.HttpResult sqlIntegrityConstraintViolationException(SQLIntegrityConstraintViolationException e) {
        String localizedMessage = e.getLocalizedMessage();
        String message = Splitter.on(System.lineSeparator()).trimResults().splitToList(localizedMessage).get(1);
        String substring = message.substring(message.indexOf("Exception:"));
        if (substring.toUpperCase().contains("NAME")) {
            return new cn.chnmuseum.party.common.validator.HttpResult("400", "名称已存在,请修改名称", "");
        } else if (substring.toUpperCase().contains("CODE")) {
            return new cn.chnmuseum.party.common.validator.HttpResult("400", "编码已存在", "");
        }
        return new cn.chnmuseum.party.common.validator.HttpResult("400", message, "");
    }

    /**
     * 参数类型转换错误
     *
     * @param exception 错误
     * @return 错误信息
     */
    @ExceptionHandler(HttpMessageConversionException.class)
    public cn.chnmuseum.party.common.validator.HttpResult httpMessageConversionException(HttpMessageConversionException exception) {
        log.error(exception.getCause().getLocalizedMessage());
        return new cn.chnmuseum.party.common.validator.HttpResult("400", exception.getCause().getLocalizedMessage());
    }

    /**
     * 捕获业务异常
     *
     * @param exception 自定义接口异常类
     * @return 返回的异常信息map
     */
    @ExceptionHandler(InterfaceException.class)
    public cn.chnmuseum.party.common.validator.HttpResult handleInterfaceException(InterfaceException exception) {
        cn.chnmuseum.party.common.validator.HttpResult httpResult = new cn.chnmuseum.party.common.validator.HttpResult();
        httpResult.setResultCode(exception.getErrorCode());
        httpResult.setMessage(exception.getErrorMsg());
        return httpResult;
    }

    /**
     * 捕获Shiro无权限异常 未授权异常
     *
     * @param exception 无权限异常
     * @return 返回的异常信息map
     */
    @ExceptionHandler(UnauthorizedException.class)
    public Object handleUnauthorizedException(UnauthorizedException exception) {
        cn.chnmuseum.party.common.validator.HttpResult httpResult = new cn.chnmuseum.party.common.validator.HttpResult();
        // 无权限
        addResCodeToMap(RESPONSE_CODE_ENUM.ACCOUNT_AUTHENTICATION_FAILED, httpResult);
        return httpResult;
    }

    /**
     * 捕获Shiro无权限异常 未认证异常
     *
     * @param exception 无权限异常
     * @return 返回的异常信息map
     */
    @ExceptionHandler(UnauthenticatedException.class)
    public Object handleUnauthenticatedException(UnauthenticatedException exception) {
        cn.chnmuseum.party.common.validator.HttpResult httpResult = new cn.chnmuseum.party.common.validator.HttpResult();
        // 无权限
        addResCodeToMap(RESPONSE_CODE_ENUM.ACCOUNT_AUTHENTICATION_FAILED, httpResult);
        return httpResult;
    }

    @ExceptionHandler(MissingServletRequestPartException.class)
    public Object handleServletRequestPartException(MissingServletRequestPartException exception) {
        Map<String, Object> map = new LinkedHashMap<>();
        map.put(RESULT_INFO_ENUM.RESULT_CODE.getKey(), "400");
        map.put(RESULT_INFO_ENUM.RESULT_MSG.getKey(), "请选择文件后点击上传");
        return map;
    }

    /**
     * 添加异常信息到map中
     *
     * @param responseCodeEnum 错误响应编码枚举类对象
     * @param httpResult       响应对象
     */
    private void addResCodeToMap(RESPONSE_CODE_ENUM responseCodeEnum, cn.chnmuseum.party.common.validator.HttpResult httpResult) {
        httpResult.setResultCode(responseCodeEnum.getResultCode());
        httpResult.setMessage(responseCodeEnum.getMessage());
    }
}