package com.cyksj.aspect;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.cyksj.common.annotation.Log;
import com.cyksj.common.util.Jsons;
import com.cyksj.common.util.StringUtil;
import com.cyksj.enums.BusinessStatus;
import com.cyksj.mapper.manage.cms.SysLogMapper;
import com.cyksj.model.entity.CmsUser;
import com.cyksj.model.entity.SysLog;
import com.cyksj.service.mange.cms.CmsUserManager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.HandlerMapping;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Map;

/*
 *项目名: yicang-admin
 *文件名: LogAspect
 *创建者: JavaZou
 *创建时间:2022/9/30 10:55
 */
@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
public class LogAspect {
	private final HttpServletRequest request;

	private final CmsUserManager cmsUserManager;

	private final SysLogMapper sysLogManager;

	/**
	 * 处理后完成
	 * 切入点
	 */
	@AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")
	public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult) {
		handleLog(joinPoint, controllerLog, null, jsonResult);
	}

	/**
	 * 异常处理
	 * 切入
	 */
	@AfterThrowing(pointcut = "@annotation(controllerLog)", throwing = "e")
	public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e) {
		handleLog(joinPoint, controllerLog, e, null);
	}

	private void handleLog(JoinPoint joinPoint, Log controllerLog, Exception e, Object jsonResult) {
		try {
			// 获取当前的用户
			CmsUser cmsUser = cmsUserManager.getUser(StpUtil.getLoginIdAsLong());
			SysLog sysLog = new SysLog();
			sysLog.setOperateIp(ServletUtil.getClientIP(request));
			sysLog.setOperateUrl(request.getRequestURI());
			sysLog.setStatus(BusinessStatus.SUCCESS.ordinal());
			sysLog.setOperateTime(DateTime.now().toString());
			//操作人
			if (cmsUser != null) {
				sysLog.setOperator(cmsUser.getNickname());
				sysLog.setAccount(cmsUser.getPhone());
			}
			if (e != null) {
				sysLog.setStatus(BusinessStatus.FAIL.ordinal());
				sysLog.setErrMsg(StrUtil.subSufByLength(e.getMessage(), 2000));
			}
			// 设置方法名称
			String className = joinPoint.getTarget().getClass().getName();
			String methodName = joinPoint.getSignature().getName();
			sysLog.setMethod(String.format("%s.%s", className, methodName));
			// 处理设置注解上的参数
			getControllerMethodDescription(joinPoint, controllerLog, sysLog, jsonResult);
			//请求方式
			sysLog.setRequestMethod(request.getMethod());
			sysLog.setOperateLocation(StringUtil.getRealAddressByIP(sysLog.getOperateIp()));
			sysLogManager.insert(sysLog);
		} catch (Exception exp) {
			// 记录本地异常日志
			log.error("==前置通知异常==");
			log.error("异常信息:{}", exp.getMessage());
		}
	}

	private void getControllerMethodDescription(JoinPoint joinPoint, Log log, SysLog sysLog, Object jsonResult) throws Exception {
		sysLog.setModule(log.module());
		sysLog.setBusinessType(log.businessType().ordinal());
		// 是否需要保存request，参数和值
		if (log.isSaveRequestData()) {
			// 获取参数的信息，传入到数据库中。
			setRequestValue(joinPoint, sysLog, log);
		}
		// 是否需要保存response，参数和值
		if (log.isSaveResponseData() && jsonResult != null) {
			sysLog.setJsonResult(StrUtil.subSufByLength(Jsons.toJson(jsonResult), 2000));
		}
	}

	/**
	 * 请求入参
	 */
	private void setRequestValue(JoinPoint joinPoint, SysLog sysLog, Log log) throws Exception {
		String requestMethod = log.businessType().name();
		if (HttpMethod.PUT.name().equals(requestMethod) || HttpMethod.POST.name().equals(requestMethod)) {
			String params = argsArrayToString(joinPoint.getArgs());
			sysLog.setOperateParams(StrUtil.subSufByLength(params, 2000));
		} else {
			Map<Object, Object> paramsMap = (Map<Object, Object>) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
			if (paramsMap.size() == 0) {
				Enumeration<String> parameterNames = request.getParameterNames();
				while (parameterNames.hasMoreElements()) {
					String key = parameterNames.nextElement();
					String value = request.getParameter(key);
					paramsMap.put(key, value);
				}
			}
			sysLog.setOperateParams(StrUtil.subSufByLength(paramsMap.toString(), 2000));
		}
	}

	/**
	 * 参数拼装
	 */
	private String argsArrayToString(Object[] paramsArray) {
		String params = "";
		if (paramsArray != null && paramsArray.length > 0) {
			for (Object o : paramsArray) {
				if (o != null && !isFilterObject(o)) {
					try {
						Object jsonObj = Jsons.toJson(o);
						params += jsonObj.toString() + " ";
					} catch (Exception e) {
					}
				}
			}
		}
		return params.trim();
	}

	/**
	 * 判断是否需要过滤的对象。
	 *
	 * @param o 对象信息。
	 * @return 如果是需要过滤的对象，则返回true；否则返回false。
	 */
	@SuppressWarnings("rawtypes")
	public boolean isFilterObject(final Object o) {
		Class<?> clazz = o.getClass();
		if (clazz.isArray()) {
			return clazz.getComponentType().isAssignableFrom(MultipartFile.class);
		} else if (Collection.class.isAssignableFrom(clazz)) {
			Collection collection = (Collection) o;
			for (Object value : collection) {
				return value instanceof MultipartFile;
			}
		} else if (Map.class.isAssignableFrom(clazz)) {
			Map map = (Map) o;
			for (Object value : map.entrySet()) {
				Map.Entry entry = (Map.Entry) value;
				return entry.getValue() instanceof MultipartFile;
			}
		}
		return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse
				|| o instanceof BindingResult;
	}
}
