package cn.superdesk.uniorder.common.lock.zookeeper.interceptor;


import cn.superdesk.uniorder.common.lock.zookeeper.annotation.DistributedLock;
import cn.superdesk.uniorder.common.lock.zookeeper.config.ZookeeperConfigurer;
import lombok.extern.slf4j.Slf4j;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;


/**
 * @Description: Java类作用描述
 * @Author: linjinyu
 * @CreateDate: 2020/12/3 下午4:06
 * @UpdateUser: linjinyu
 * @UpdateDate: 2020/12/3 下午4:06
 * @UpdateRemark: 修改内容
 * @Version: 1.0
 */
@Slf4j
@Aspect
@Component
public class DistributedLockAspect {

	@Autowired
	private CuratorFramework    curatorFramework;
	@Autowired
	private ZookeeperConfigurer zookeeperConfigurer;

	/**
	 * 切点定义 指定注解
	 */
	@Pointcut("@annotation(cn.superdesk.uniorder.common.lock.zookeeper.annotation.DistributedLock)")
	public void distributedLockAspect() {
	}

	@Around(value = "distributedLockAspect()")
	public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {


		log.info("DistributedLockAspect -> doAround@Around {}", Thread.currentThread().getName());

		// target class
		Class<?> targetClass = proceedingJoinPoint.getTarget().getClass();
		// method name
		String methodName = proceedingJoinPoint.getSignature().getName();
		// parameter types
		Class<?>[] parameterTypes = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod().getParameterTypes();
		// method
		Method method = targetClass.getMethod(methodName, parameterTypes);
		// arguments
		Object[] arguments = proceedingJoinPoint.getArgs();

		return lock(proceedingJoinPoint, method, arguments);
	}

	private Object lock(ProceedingJoinPoint proceedingJoinPoint, Method method, Object[] arguments) throws Throwable {

		DistributedLock annotation = method.getAnnotation(DistributedLock.class);

		String lockName = parseKey(annotation, method, arguments);


		/****************************************** 试验1 存在问题 就是如果获取不到锁 就是丢弃 start ******************************************/
//		InterProcessMutex mutex = new InterProcessMutex(curatorFramework, zookeeperConfigurer.getLockPath() + lockName);
//
//		// 获取 锁资源
//		boolean flag = mutex.acquire(0, TimeUnit.SECONDS);
//		if (flag) {
//
//			log.info("DistributedLockAspect -> doAround@Around -> lock 锁资源 Y {}", Thread.currentThread().getName());
//
//			return proceedingJoinPoint.proceed();
//		}
//
//		log.info("DistributedLockAspect -> doAround@Around -> lock 锁资源 N {}", Thread.currentThread().getName());
//
//		return null;
		/****************************************** 试验1 存在问题 就是如果获取不到锁 就是丢弃   end ******************************************/

		/****************************************** 试验2 start ******************************************/

//		final InterProcessMutex mutex = new InterProcessMutex(curatorFramework, zookeeperConfigurer.getLockPath() + lockName);
//
//		if (tryLock(mutex)) {
//			return proceedingJoinPoint.proceed();
//		} else {
//			waitForLock(mutex);
//		}
		/****************************************** 试验2 start ******************************************/


		/****************************************** 试验3 改进试验1 start ******************************************/

		Object proceed = null;

		InterProcessMutex mutex = new InterProcessMutex(curatorFramework, zookeeperConfigurer.getLockPath() + lockName);

		// 获取锁 限时等待 3ms 如果又有一个新线程进来 好像是会等待 mark by linjinyu at 20201203
		boolean flag = mutex.acquire(3, TimeUnit.MILLISECONDS);

		if (flag) {

			try {

				log.info("DistributedLockAspect -> doAround@Around -> lock 锁资源 Y {}", Thread.currentThread().getName());

				// 处理业务逻辑
				proceed = proceedingJoinPoint.proceed();
				// 释放锁
				mutex.release();
			} catch (Exception e) {
				log.info("DistributedLockAspect -> 发生异常@Around -> unlock 锁资源 N {}", Thread.currentThread().getName());
				mutex.release();
				throw e;
			}
		} else {
			log.info("DistributedLockAspect -> doAround@Around -> 没有获取到lock 锁资源 N {}", Thread.currentThread().getName());
		}


		return proceed;
		/****************************************** 试验3 改进试验1   end ******************************************/
	}

//	private boolean waitForLock(InterProcessMutex mutex) {
//
//		try {
//
//			Thread.sleep(200);
//
//			if (tryLock(mutex)) {
//				return true;
//			} else {
//				waitForLock(mutex);
//			}
//
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//		}
//
//
//		return false;
//
//	}
//
//	private boolean tryLock(InterProcessMutex mutex) {
//
//		try {
//
//			// 获取 锁资源
//			boolean flag = mutex.acquire(0, TimeUnit.SECONDS);
//			if (flag) {
//
//				log.info("DistributedLockAspect -> doAround@Around -> lock 锁资源 Y {}", Thread.currentThread().getName());
//				return true;
//
//			}
//
//		} catch (Exception e) {
//
//		}
//
//		return false;
//	}
//
//	private void unlock(String lockName) {
//
//		try {
//			InterProcessMutex mutex = new InterProcessMutex(curatorFramework, zookeeperConfigurer.getLockPath() + lockName);
//
//			mutex.release();
//		} catch (Exception e) {
//			e.printStackTrace();
//		}
//
//	}

	/**
	 * 得到 lockName
	 *
	 * @param annotation
	 * @param method
	 * @param args
	 * @return
	 */
	private String parseKey(DistributedLock annotation, Method method, Object[] args) {

		LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();

		String[] paraNameArr = u.getParameterNames(method);

		ExpressionParser          parser  = new SpelExpressionParser();
		StandardEvaluationContext context = new StandardEvaluationContext();

		for (int i = 0; i < paraNameArr.length; ++i) {
			context.setVariable(paraNameArr[i], args[i]);
		}

		String lockName = annotation.name();
		if (lockName.contains("#")) {
			lockName = parser.parseExpression(lockName).getValue(context, String.class);
		}

		log.info("LOCK NAME : {}", lockName);

		return lockName;
	}


	/**
	 * 执行完请求可以做的
	 * pointcut = "distributedLockAspect()"
	 *
	 * @param result
	 * @throws Throwable
	 */
	@AfterReturning(value = "distributedLockAspect()", returning = "result")
	public void doAfterReturning(Object result) throws Throwable {

//		String lockName = "ME20201125192109752587";
//		InterProcessMutex mutex   = new InterProcessMutex(curatorFramework, zookeeperConfigurer.getLockPath() + lockName);
//
//		mutex.release();

		log.info("DistributedLockAspect -> doAfterReturning@AfterReturning {}", " endPoint... DONE \n");
	}

	@AfterThrowing(value = "distributedLockAspect()", throwing = "ex")
	public void afterThrowing(Throwable ex) {

		throw new RuntimeException(ex);
	}
}
