package com.persagy.aspects;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.persagy.dmp.common.context.AppContext;
import com.persagy.dmp.common.helper.SpringHelper;
import com.persagy.dmp.common.model.response.CommonResult;
import com.persagy.utils.JacksonMapper;
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.net.InetAddress;
import java.net.UnknownHostException;
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> dataStorage = new ThreadLocal<>();

	@Pointcut("execution(public * com.persagy.dmp..*.controller.*.*(..))")
	//@Pointcut("execution(* *(..))")
	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();
		LogData data = initFlag(request);
		dataStorage.set(data);
		log.info("{} REQUEST URL      : {} {} {} {}", data.intFlag, data.method, data.url, data.remoteIp, data.remotePort);
		log.info("{} REQUEST ARGS     : {} ", data.intFlag, JacksonMapper.toSimpleJson(prepare(joinPoint)));
	}

	public static List<Object> prepare(JoinPoint joinPoint) {
		Object[] args = joinPoint.getArgs();
		List<Object> list = new LinkedList<>();
		if (args != null && args.length > 0) {
			for (Object arg : args) {
				if (arg instanceof ServletRequest) {
					continue;
				}
				if (arg instanceof ServletResponse) {
					continue;
				}
				if (arg instanceof MultipartFile) {
					continue;
				}
				list.add(arg);
			}
		}
		return list;
	}

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

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

	private static LogData initFlag(HttpServletRequest request) {
		LogData data = new LogData();
		// 请求IP
		data.remoteIp = getIpAddr();
		// 请求端口
		data.remotePort = request.getRemotePort();
		data.timestamp = System.currentTimeMillis();
		data.method = request.getMethod();
		String requestURI = request.getRequestURI();
		String queryString = request.getQueryString();
		data.url = requestURI + (queryString == null ? "" : "?" + queryString);
		data.intFlag = IdUtil.fastSimpleUUID();
		dataStorage.set(data);
		return data;
	}

	/**
	 * Description:获取ip地址
	 *
	 * @return String
	 * @author lijie
	 * @since 2018年10月22日: 下午4:57:26
	 * Update By lijie 2018年10月22日: 下午4:57:26
	 */
	public static String getIpAddr() {
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		String ip = request.getHeader("x-forwarded-for");
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
			if (ip.equals("127.0.0.1")) {
				// 根据网卡取本机配置的IP
				try {
					ip = InetAddress.getLocalHost().getHostAddress();
				} catch (UnknownHostException e) {
					log.error("获取本机IP信息失败:", e);
				}
			}
		}
		// 对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按照','分割
		if (ip != null && ip.length() > 15) {
			if (ip.indexOf(",") > 0) {
				ip = ip.substring(0, ip.indexOf(","));
			}
		}
		return ip;
	}
	/***
	 * 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;
	}
}