package com.sifang.modules.config;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import redis.clients.jedis.JedisPool;

@Aspect
@Component
public class SyncAspect implements Ordered {

    private static Logger logger = LoggerFactory.getLogger(SyncAspect.class);

    private static final boolean enabled = true;

    private static final long rand = System.currentTimeMillis();

    @Qualifier("lock")
    @Autowired
    JedisPool jedisPool2;

    private static final ThreadLocal<DLock> lock = new ThreadLocal<>();

    @Override
    public int getOrder() {
        return 1;
    }

    @Around("@annotation(sync)")
    public Object sync(ProceedingJoinPoint pjp, Sync sync) throws Throwable {
        if (!enabled) {
            return pjp.proceed();
        }

        DLock dLock = lock.get();
        if (dLock != null) {
            logger.info("lock already granted, just go ahead.");
            return pjp.proceed();
        }

        String value = sync.value();
        dLock = new DLock(value + "_" + getSyncKey(pjp.getArgs()) + "_" + rand, jedisPool2);
        long t = System.currentTimeMillis();
        try {
            boolean locked = dLock.lock();
            if (locked) {
                lock.set(dLock);
                logger.info("===============================lock ok: {},c: {}", dLock, (System.currentTimeMillis() - t));
                return pjp.proceed();
            }
            logger.warn("===============================lock failed: {},c: {}", dLock, (System.currentTimeMillis() - t));
            throw new RuntimeException("unable to get lock");
        } finally {
            boolean unlocked = dLock.unlock();
            if (!unlocked) {
                logger.warn("===============================unlock failed: {},c: {}", dLock, (System.currentTimeMillis() - t));
            } else {
                logger.info("===============================unlock ok: {},c: {}", dLock, (System.currentTimeMillis() - t));
            }
            lock.remove();
        }
    }

    private String getSyncKey(Object[] args) {
        if (args != null && args.length > 0) {
            for (Object arg : args) {
                if (arg instanceof KeyProvider) {
                    return ((KeyProvider) arg).getKey();
                }
            }
        }
        return "GLOBAL_KEY";
    }
}
