package com.brframework.commondistributed.core;

import com.brframework.commondistributed.annotation.DistributedLock;
import com.brframework.commondistributed.exception.LockedException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.annotation.Order;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.SimpleEvaluationContext;
import org.springframework.stereotype.Component;

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

/**
 * @Author xu
 * @Date 2018/2/23 0023 上午 11:14
 * 分布式锁注解实现
 */
@Component
@Aspect
@Slf4j
@Order(value = Integer.MIN_VALUE)  //确保最早执行
public class DistributedLockAspect {
    private ExpressionParser parser = new SpelExpressionParser();
    private ParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
    @Autowired
    private RedissonClient redissonClient;

    /**
     * @see DistributedLock
     * @param pjp
     * @return
     * @throws Throwable
     */
    @Around("@annotation(com.brframework.commondistributed.annotation.DistributedLock)")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable {

        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();

        Method method = methodSignature.getMethod();

        Object[] args = pjp.getArgs();
        String[] argsName = discoverer.getParameterNames(method);
        EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();

        if(argsName != null && argsName.length != 0){
            for (int i = 0; i < argsName.length; i++) {
                context.setVariable(argsName[i], args[i]);
            }
        }

        DistributedExpressionRootObject expressionRootObject =
                new DistributedExpressionRootObject(method, args, pjp.getTarget());


        DistributedLock lockAnnotation = method.getAnnotation(DistributedLock.class);
        if(lockAnnotation == null){
            //当在接口的实现类中使用DistributedLock注解是会出现拿不到的问题
            method = pjp.getTarget().getClass().getMethod(method.getName(), method.getParameterTypes());
            lockAnnotation = method.getAnnotation(DistributedLock.class);
        }

        boolean waitFor = lockAnnotation.waitFor();

        String key = parser.parseExpression(lockAnnotation.key()).getValue(context, expressionRootObject, String.class);
        log.info("生成分布式锁Key={}", key);
        RLock lock = redissonClient.getLock(key);

        if(lock.isLocked() && !waitFor){
            //出现锁占用，不等待，并且需要抛出异常
            if(lockAnnotation.throwException()){
                throw new LockedException();
            } else {
                log.info("{}锁生效，当前执行忽略", key);
            }

        }

        //加锁，为避免死锁，60秒后将自动解锁
        lock.lock(60, TimeUnit.SECONDS);
        try {
            return pjp.proceed();
        } finally {
            //解锁
            lock.unlock();
        }
    }



}
