package com.gci.boot.common.log.aspect;

import com.gci.boot.common.log.annotation.AddSysLog;
import com.gci.boot.common.util.JsonUtil;
import com.gci.boot.common.util.SpringContextUtil;
import com.gci.boot.constant.CommonConstant;
import com.gci.boot.modules.system.entity.SysLog;
import com.gci.boot.modules.system.entity.SysUser;
import com.gci.boot.modules.system.service.ISysLogService;
import com.gci.boot.modules.system.service.ISysUserService;
import com.gci.boot.modules.system.vo.LoginUser;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Aspect
@Slf4j
@RequiredArgsConstructor
public class SysLogAspect {
	private final ISysLogService sysLogService;
	private final ISysUserService sysUserService;

	@Pointcut("@annotation(com.gci.boot.common.log.annotation.AddSysLog)")
	public void logPointCut() { 
		
	}

	@Around("logPointCut()")
	public Object around(ProceedingJoinPoint point) throws Throwable {
		LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
		long beginTime = System.currentTimeMillis();
		//执行方法
		Object result = point.proceed();
		//执行时长(毫秒)
		long time = System.currentTimeMillis() - beginTime;
		if(sysUser == null) {
			sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
		}
		//保存日志
		try {
			saveSysLog(point, time, sysUser);
		} catch (Exception e) {
			log.error("写入系统日志失败", e);
		}

		return result;
	}

	private void saveSysLog(ProceedingJoinPoint joinPoint, long time, LoginUser sysUser) {
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		Method method = signature.getMethod();

		SysLog sysLog = new SysLog();
		AddSysLog sysLogAnno = method.getAnnotation(AddSysLog.class);
		if(sysLogAnno != null){
			//注解上的描述,操作日志内容
			sysLog.setLogContent(sysLogAnno.value());
			sysLog.setLogType(sysLogAnno.logType());
			sysLog.setLogSource(sysLogAnno.logSource());
		}

		//请求的方法名
		String className = joinPoint.getTarget().getClass().getName();
		String methodName = signature.getName();
		sysLog.setMethod(className + "." + methodName + "()");
		
		
		//设置操作类型
		if (sysLog.getLogType() == CommonConstant.LOG_TYPE_OP) {
			sysLog.setOperateType(getOperateType(methodName, sysLogAnno.operateType()));
		}

		//获取request
		HttpServletRequest request = SpringContextUtil.getHttpServletRequest();
		//请求的参数
		sysLog.setRequestParam(getReqestParams(request,joinPoint));

		//设置IP地址
		sysLog.setIp(getIpAddr(request));

		sysLog.setRequestUrl(request.getRequestURI());

		sysLog.setRequestType(request.getMethod());

		//获取登录用户信息
		if(sysUser!=null){
			sysLog.setUserid(sysUser.getUsername());
			sysLog.setUsername(sysUser.getRealname());
		}else{
			if(StringUtils.isNotBlank(sysLog.getRequestParam())) {
				List<Map> maps = JsonUtil.json2List(sysLog.getRequestParam(), Map.class);
				if (maps != null && maps.size() > 0) {
					Object username = maps.get(0).get("username");
					if (username != null) {
						sysLog.setUserid(username.toString());
						SysUser user = sysUserService.getByUsername(sysLog.getUserid());
						if(user != null){
							sysLog.setUsername(user.getRealname());
						}
					}
				}
			}
		}
		//耗时
		sysLog.setCostTime(time);
		sysLog.setCreateTime(new Date());
		//保存系统日志
		sysLogService.save(sysLog);
	}
	/**
	 * 获取操作类型
	 */
	private int getOperateType(String methodName,int operateType) {
		if (operateType > 0) {
			return operateType;
		}
        if (methodName.startsWith("list") || methodName.startsWith("page")) {
        	return CommonConstant.OPERATE_TYPE_QUERY;
		}
        if (methodName.startsWith("add") || methodName.startsWith("insert") || methodName.startsWith("save")) {
        	return CommonConstant.OPERATE_TYPE_INSERT;
		}
        if (methodName.startsWith("edit") || methodName.startsWith("update")) {
        	return CommonConstant.OPERATE_TYPE_UPDATE;
		}
        if (methodName.startsWith("delete") || methodName.startsWith("remove") ) {
        	return CommonConstant.OPERATE_TYPE_DELETE;
		}
        if (methodName.startsWith("import")) {
        	return CommonConstant.OPERATE_TYPE_IMPORT;
		}
        if (methodName.startsWith("export")) {
        	return CommonConstant.OPERATE_TYPE_EXPORT;
		}
		return CommonConstant.OPERATE_TYPE_QUERY;
	}

	/**
	* @Description: 获取请求参数
	* @author: scott
	* @date: 2020/4/16 0:10
	* @param request:  request
	* @param joinPoint:  joinPoint
	* @Return: java.lang.String
	*/
	private String getReqestParams(HttpServletRequest request, JoinPoint joinPoint) {
		String httpMethod = request.getMethod();
		String params = "";
		if ("POST".equals(httpMethod) || "PUT".equals(httpMethod) || "PATCH".equals(httpMethod)) {
			// HttpServletRequest类型转Json异常
			Object[] paramsArray = Arrays.stream(joinPoint.getArgs())
					.filter(p -> !p.getClass().equals(ShiroHttpServletRequest.class)).toArray();

			params = JsonUtil.object2Json(paramsArray);
		} else {
			MethodSignature signature = (MethodSignature) joinPoint.getSignature();
			Method method = signature.getMethod();
			// 请求的方法参数值
			Object[] args = joinPoint.getArgs();
			// 请求的方法参数名称
			LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
			String[] paramNames = u.getParameterNames(method);
			if (args != null && paramNames != null) {
				for (int i = 0; i < args.length; i++) {
					params += "  " + paramNames[i] + ": " + args[i];
				}
			}
		}
		return params;
	}

	public static String getIpAddr(HttpServletRequest request) {
		String ip = null;
		try {
			ip = request.getHeader("x-forwarded-for");
			if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
				ip = request.getHeader("Proxy-Client-IP");
			}
			if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
				ip = request.getHeader("WL-Proxy-Client-IP");
			}
			if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
				ip = request.getHeader("HTTP_CLIENT_IP");
			}
			if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
				ip = request.getHeader("HTTP_X_FORWARDED_FOR");
			}
			if (StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
				ip = request.getRemoteAddr();
			}
		} catch (Exception e) {
			log.error("IP ERROR ", e);
		}
		return ip;
	}
}