package com.younike.common.annotation;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.younike.common.bean.APIResult;
import com.younike.common.bean.ApiResultCode;
import com.younike.common.utils.api.ApiUtils;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;


/**
 * 拦截请求的url和时间 如果设定的期间内有相同的参数 则不允许请求接口
 * 2021-8-13 Another
 */
@Aspect
@Component
public class DuplicateSubmitAspect {

	private static final Logger log = LoggerFactory.getLogger(DuplicateSubmitAspect.class);
	@Autowired(required = false)
	private HttpServletRequest request;
 	HashMap<String, Long> accessMap = Maps.newHashMap();

	private Long safeTime = 2000L;
	private String checkDoubleClick = "1";

	@Pointcut("@annotation(com.younike.common.annotation.ISubmit)")
	public void token() {
	}

	@Around("token()")
	public Object checkToken(ProceedingJoinPoint jp) throws Throwable {

 		Method mth = getJpMethod(jp);
		String token = request.getParameter("token");

		String path = "";
		if(jp.getTarget().getClass().isAnnotationPresent(RequestMapping.class)) {
			if(jp.getTarget().getClass().getAnnotation(RequestMapping.class).value().length>0) {
				path+=jp.getTarget().getClass().getAnnotation(RequestMapping.class).value()[0];
			}
		}
		if(mth.isAnnotationPresent(RequestMapping.class)) {
			if(mth.getAnnotation(RequestMapping.class).value().length>0) {
				path+=mth.getAnnotation(RequestMapping.class).value()[0];
			}
		}
		log.info("请求url"+path);
		if(Objects.equals(checkDoubleClick, "1")&&isDoubleClick(token,path,request)) {
			APIResult result = new APIResult(ApiResultCode.Fail.getCode(), "老哥，点慢点，我顶不住");
			return result;
		}
		Object proceed = jp.proceed(jp.getArgs());
		return proceed;
	}

	/**
	 * 获取切面执行的方法
	 *
	 * @param jp
	 * @return
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 */
	@SuppressWarnings("rawtypes")
	private Method getJpMethod(JoinPoint jp) throws SecurityException, NoSuchMethodException {
		Class[] parameterTypes = new Class[jp.getArgs().length];
		Object[] args = jp.getArgs();
		for (int i = 0; i < args.length; i++) {
			if (args[i] != null) {
				parameterTypes[i] = args[i].getClass();
			} else {
				parameterTypes[i] = null;
			}
		}
		String methodName = jp.getSignature().getName();
		Method[] mts = jp.getTarget().getClass().getDeclaredMethods();
		for (Method m : mts) {
			if (m.getName().equals(methodName)) {
				return m;
			}
		}
		return null;
	}

	private synchronized Boolean isDoubleClick(String token,String url,HttpServletRequest request) {
		HashMap<String, Object> par = Maps.newHashMap();
		ApiUtils.setRequestPar(request, par);
		String parJson = JSONObject.toJSONString(par);
		Long last = accessMap.get(url+"_"+token+parJson);
		log.info("时间"+last);
		log.info("请求参数"+parJson);
		if(last!=null) {
			if(System.currentTimeMillis()-last<=safeTime) {
				//放到map中 key是url+请求的参数 value是当前的时间戳
				accessMap.put(url+"_"+token+parJson, System.currentTimeMillis());
				//每次清空多余的数据 避免内存溢出
				cleanAccessMap();
				return true;
			}
		}
		//每次清空多余的数据 避免内存溢出
		cleanAccessMap();
		//放到map中 key是url+请求的参数 value是当前的时间戳
		accessMap.put(url+"_"+token+parJson, System.currentTimeMillis());
		return false;
	}

	private void cleanAccessMap() {
		if(accessMap.size()>10000) {
			List<String> delKeyList = Lists.newArrayList();
			for(String key:accessMap.keySet()) {
				if(System.currentTimeMillis()-accessMap.get(key)>600000) {//清空10分钟前的
					delKeyList.add(key);
				}
			}
			for(String key:delKeyList) {
				accessMap.remove(key);
			}
		}
	}
}
