/**
 * 
 */
package org.springblade.core.aop;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import org.springblade.core.constant.AppConstant;
import org.springblade.core.log.event.ErrorLogPublisher;
import org.springblade.core.log.properties.LogMessageConvert;
import org.springblade.core.log.properties.PlatomixLogProperties;
import org.springblade.core.log.utils.INetUtil;
import org.springblade.core.secure.utils.WebUtil;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.api.ResultCode;
import org.springblade.core.tool.exception.RpcException;
import org.springblade.core.tool.exception.ServiceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
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.NoHandlerFoundException;

import lombok.extern.slf4j.Slf4j;

import java.util.Optional;

/**
 * @author wookvn
 *	Controller全局异常捕获
 */
@Slf4j
@RestControllerAdvice(basePackages = AppConstant.BASE_PACKAGES)
public class GlobalExceptionAdvice {
	
	@Autowired
	private PlatomixLogProperties logProperties;

	/**
	 * 	校验框架异常
	 * @param ex
	 * @return
	 */
	@ExceptionHandler(BindException.class)
	public Object handleBindException(BindException ex) {
		return invoke(ex.getAllErrors().get(0).getDefaultMessage(), ex, true);
	}
	
	/**
	 * 	参数校验异常处理
	 * @param ex
	 * @return
	 */
	@ExceptionHandler(MethodArgumentNotValidException.class)
	public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
		return invoke(ex.getBindingResult().getAllErrors().stream().findFirst().get().getDefaultMessage(), ex, false);
	}
	
	/**
	 * 	请求方式异常处理
	 * @param ex
	 * @return
	 */
	@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
	public Object handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) {
		return invoke("HTTP请求方式错误~", ex, false);
	}
	
	/**
	 * 
	 * @param ex
	 * @return
	 */
	@ExceptionHandler(NoHandlerFoundException.class)
	public Object handleNoHandlerFoundException(NoHandlerFoundException ex) {
		return invoke("找不到请求地址~", ex, false);
	}
	

	/**
	 *  业务异常
	 * @param ex
	 * @return
	 */
	@ExceptionHandler(ServiceException.class)
	public Object handleServiceException(ServiceException ex) {
		return invoke(ex.getMessage(), ex, false);
	}
	
	/**
	 * RPC异常
	 * @param ex
	 * @return
	 */
	@ExceptionHandler(RpcException.class)
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	public Object handle(RpcException ex) {
		return invoke(ex.getMessage(), ex, false);
	}
	
	/**
	 * 	全局异常
	 * @param ex
	 * @return
	 */
	@ExceptionHandler(Exception.class)
	public Object handleException(Exception ex) {
		if (logProperties.getErrorLogEnabled()) {
			//发送服务异常事件
			ErrorLogPublisher.publishEvent(ex, INetUtil.getPath(WebUtil.getRequest().getRequestURI()));
		}
		return invoke("系统开小差啦,请稍后重试~", ex, true);
	}
	
	/**
	 * 
	 * @param errMsg
	 * @param ex
	 * @return
	 */
	private Object invoke(String errMsg, Exception ex, boolean logStackTrace) {
		if(logStackTrace) {
			log.error(ex.getMessage(), ex);
		}else {
			log.error(ex.getMessage());
		}
		//错误描述转换
		Optional<LogMessageConvert> logMessageConvertOptional = logProperties.getMsgConvertList().stream().filter(item -> StrUtil.containsAnyIgnoreCase(ex.getMessage(), item.getContains().stream().toArray(String[]::new))).findFirst();
		R<String> failRet = R.fail(ResultCode.FAILURE, logMessageConvertOptional.isPresent() ? logMessageConvertOptional.get().getDisplay() : errMsg);
		if(logProperties.getApiDetailInfoEnabled()) {
			failRet.setData(logProperties.getApiDetailInfoBase64Enabled() ? Base64.encode(ex.getMessage()) : ex.getMessage());
		}
		return failRet;
	}
	
}
