package com.persagy.dmp.auth.aspect;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.persagy.dmp.common.model.response.CommonResult;
import com.persagy.dmp.common.utils.JsonHelper;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.util.LinkedList;
import java.util.List;

/**
 * @author: yaoll
 * @date: 2020-09-22
 * @verison: 1.0
 */
@Slf4j
@Aspect
@Component
@EnableAspectJAutoProxy
public class RequestLogAspect {

	private static final ThreadLocal<LogData> DATA_STORAGE = new ThreadLocal<>();

	/** @Pointcut("execution(* *(..))") */
	@Pointcut("execution(public * com.persagy.dmp..*.controller.*.*(..))")

	public void controllerPoint() {
	}

	@Before("controllerPoint()")
	public void doBefore(JoinPoint joinPoint) {
		ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		if (null == attributes) {
			log.warn("获取请求参数失败!!");
			return;
		}
		HttpServletRequest request = attributes.getRequest();
		if (StrUtil.isBlank(request.getContentType())
				|| (StrUtil.isNotBlank(request.getContentType())
				&& !request.getContentType().contains("application/json"))){
			return;
		}
		LogData data = initFlag(request);
		DATA_STORAGE.set(data);
		log.info("{} REQUEST URL      : {} {} {} {}", data.intFlag, data.method, data.url, data.remoteIp, data.remotePort);
		log.info("{} REQUEST ARGS     : {} ", data.intFlag, JsonHelper.toJsonStrQuietly(prepare(joinPoint)));
	}

	/**
	 * 准备数据
	 * @param joinPoint
	 * @return
	 */
	public static List<Object> prepare(JoinPoint joinPoint) {
		Object[] args = joinPoint.getArgs();
		List<Object> list = new LinkedList<>();
		if(ArrayUtil.isEmpty(args)) {
			return list;
		}
		for (Object arg : args) {
			if (arg instanceof ServletRequest ||
					arg instanceof ServletResponse ||
					arg instanceof MultipartFile) {
				continue;
			}
			list.add(arg);
		}
		return list;
	}

	@AfterReturning(returning = "ret", pointcut = "controllerPoint()")
	public void doAfterReturning(Object ret) {
		if (null== DATA_STORAGE.get()){
			return;
		}
		LogData data = DATA_STORAGE.get();
		String result = null;
		if (ret != null) {
			if (ret instanceof CommonResult) {
				result = ((CommonResult) ret).getResult();
			} else {
				result = ret.getClass().getName();
			}
		}
		log.info("{} REQUEST DURATION : {} {} {}", data.intFlag, System.currentTimeMillis() - data.timestamp, result, data.url);
		DATA_STORAGE.remove();
	}

	@AfterThrowing(throwing = "ex", pointcut = "controllerPoint()")
	public void doAfterThrowing(Throwable ex) {
		if (null== DATA_STORAGE.get()){
			return;
		}
		LogData data = DATA_STORAGE.get();
		log.error("{} REQUEST EXCEPTION: {} {} {}", data.intFlag, System.currentTimeMillis() - data.timestamp, data.url, ex);
		DATA_STORAGE.remove();
	}

	/**
	 * 初始化日志记录
	 * @param request
	 * @return
	 */
	private static LogData initFlag(HttpServletRequest request) {
		LogData data = new LogData();
		// 请求IP
		data.remoteIp = ServletUtil.getClientIP(request, null);
		// 请求端口
		data.remotePort = request.getRemotePort();
		data.timestamp = System.currentTimeMillis();
		data.method = request.getMethod();
		String uri = request.getRequestURI();
		String queryString = request.getQueryString();
		data.url = StrUtil.isBlank(queryString) ? uri : StrUtil.concat(true, uri, "?", queryString);
		data.intFlag = IdUtil.fastSimpleUUID();
		DATA_STORAGE.set(data);
		return data;
	}

	/***
	 * Description: 日志数据实体
	 * @author : lijie
	 * @date :2021/8/18 10:36
	 * Update By lijie 2021/8/18 10:36
	 */
	private static class LogData {
		private String intFlag;
		private Long timestamp;
		private String url;
		private String method;
		private String remoteIp;
		private Integer remotePort;
	}
}