package com.crm.rely.backend.aspect;

import com.crm.rely.backend.configuration.LocaleMessage;
import com.crm.rely.backend.core.constant.Constants;
import com.crm.rely.backend.exception.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.NoHandlerFoundException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * controller异常捕获
 */
@ControllerAdvice
public class ControllerExceptionAdvice {
    private static final String CODE = "code";
    private static final String MSG = "msg";
    private static Boolean isDev = null;
    @Autowired
    private LocaleMessage localeMessage;
    @Autowired
    private ControllerAop controllerAop;
    /**
     * 获取配置 用于判断系统是否开发环境 若是开发环境这将异常完全抛到页面 无需打开日志查看
     */
    @Autowired
    private Environment environment;

    /**
     * service运行时异常处理 自动捕获到controller的所有异常 并进行处理返回前端
     *
     * @param e 异常
     * @return 返回到页面的处理结果
     */
    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public Map controllerExceptionHandler(Exception e) throws IOException {
        if (isDev()) {
            e.printStackTrace();
        }
        int code = Constants.FAILED_CODE;
        String msg = null;
        //判断是否404 500异常
        if (e instanceof NoHandlerFoundException) {
            code = Constants.FAILED_CODE_404;
            msg = e.getMessage();
        } else if (e instanceof ServletException) {
            code = Constants.SYSTEM_ERROR_CODE;
            msg = e.getMessage();
        }
        if (e instanceof HttpMessageNotReadableException) {
            code = Constants.FAILED_CODE;
            msg = Constants.PARAMETER_ERROR;
        } else if (e instanceof RuntimeException) {
            code = Constants.SYSTEM_ERROR_CODE;
            msg = Constants.SYSTEM_ERROR;
       }
//        else if (e instanceof PayValidatedException) {
//            msg = e.getMessage();
//            //ISO-8859-1
//            HttpServletResponse response = getServletResponse();
//            msg = new String(msg.getBytes("UTF-8"), "iso-8859-1");
//
//            //跳转到支付-支付验证错误页面
//            response.sendRedirect("/pay/pay-validated-fail.html?error=" + msg);
//            return null;
//        }

        //判断登录异常 抛出登录过期 登录错误等 否则判断是否逻辑异常 如果异常 直接取到填写的结果
        if (e instanceof LoginException) {
            code = Constants.UNLOGIN_CODE;
            msg = e.getMessage();

        } else if (e instanceof ServiceException || e instanceof ControllerException||e instanceof PayValidatedException ||e instanceof DataNotFoundException) {
            msg = e.getMessage();
            if (e instanceof ServiceException) {
                ServiceException serviceException = (ServiceException) e;
                if (serviceException.getCode() > 0) {
                    code = serviceException.getCode();
                }
            }
        }

        if (e instanceof RuntimeException) {
            code = Constants.SYSTEM_ERROR_CODE;
        }

        Map map = new HashMap(2);
        map.put(CODE, code);
        map.put(MSG, localeMessage.getMessage(msg, msg));
        controllerAop.clear();
        return map;
    }

    /**
     * 捕获验证异常 即通过validation验证框架中异常 controller的body接收model中有验证注解 其他无需处理
     *
     * @param e
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    public Map validateErrorHandler(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();

        String errorMsg = Constants.SYSTEM_ERROR;
        if (bindingResult.hasErrors()) {
            List<FieldError> errorList = bindingResult.getFieldErrors();
            if (isDev()) {
                /**
                 * 在开发环境中返回具体到那个字段 那个注解未通过 方便调试
                 */
//                errorMsg = errorList.get(0).getField() + " 字段错误，错误原因:" + errorList.get(0).getDefaultMessage();
                final String errorFormat = "%s字段错误，错误原因:%s\n";
                StringBuffer errorMsgSB = new StringBuffer(errorList.size());
                for (FieldError error : errorList) {
                    errorMsgSB.append(String.format(errorFormat, error.getField(), error.getDefaultMessage()));
                }
                errorMsg = errorMsgSB.toString();
            } else {
                errorMsg = errorList.get(0).getDefaultMessage();
            }
        }
        Map map = new HashMap(2);
        map.put(CODE, Constants.FAILED_CODE);
        map.put(MSG, errorMsg);
        controllerAop.clear();
        return map;
    }

    /**
     * 判断是否是本地环境
     *
     * @return
     */
    private boolean isDev() {
        if (isDev == null) {
            if (environment.getActiveProfiles() == null || environment.getActiveProfiles().length <= 0) {
                isDev = false;
            }
            for (String value : environment.getActiveProfiles()) {
                if (value.equals("dev")) {
                    isDev = true;
                    return isDev;
                }
            }
        }

        if (isDev == null) {
            isDev = false;
        }

        return isDev;
    }

    private HttpServletResponse getServletResponse() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
    }
}
