package com.zhgd.redis.config;

import cn.hutool.core.lang.Assert;
import com.zhgd.jeecg.common.execption.OpenAlertException;
import com.zhgd.redis.annotation.ApiIdempotent;
import com.zhgd.redis.lock.RedisDistributedLock;
import com.zhgd.xmgl.security.JwtTokenProvider;
import com.zhgd.xmgl.util.MessageUtil;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Collections;

/**
 * @program: wisdomSite
 * @description: 分布式锁实现接口幂等性
 * @author: Mr.Peng
 * @create: 2021-07-26 17:03
 **/
@Aspect
@Component
public class LockCheckAspect {

    /** lua */
    private static final String RELEASE_LOCK_LUA_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private RedisDistributedLock redisDistributedLock;
    @Autowired
    private JwtTokenProvider jwtTokenProvider;


    // 增强带有CacheLock注解的方法
    @Pointcut("@annotation(com.zhgd.redis.annotation.ApiIdempotent)")
    public void pointCut(){}

    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable{

        // 使用分布式锁 机制-实现
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        ApiIdempotent lock = method.getAnnotation(ApiIdempotent.class);
        int lockSeconds = lock.expire();

       // HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);

        Assert.notNull(request, "request can not null");

        // 获取请求的凭证，本项目中使用的JWT,可对应修改
        String token =jwtTokenProvider.resolveToken(request);
        String requestURI = request.getRequestURI();

        String lockKey = getIdeKey(token, requestURI);
        String UUID = cn.hutool.core.lang.UUID.fastUUID().toString();
        try {
            //boolean success=redisDistributedLock.lock(lockKey,lockSeconds,1,1000);
            // 获取锁
            boolean success = redisTemplate.opsForValue().setIfAbsent(lockKey,UUID,lockSeconds,lock.timeUnit());
            if (!success){
                throw new OpenAlertException(MessageUtil.get("RepeatSubmitErr"));
            }
            Object result= joinPoint.proceed();
            return result;
        }finally {
            // 最后记得释放锁
            DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(RELEASE_LOCK_LUA_SCRIPT,Long.class);
            Long result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey),UUID);
            //redisDistributedLock.releaseLock(lockKey);
        }
    }

    private String getIdeKey(String token, String requestURI) {
        return token + requestURI;
    }
}
