package com.yonge.cooleshow.biz.dal.support;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Objects;
import java.util.concurrent.*;

/**
 * 分布式竞争锁
 *
 * @author hgw
 */
public class DistributedLock {
    private final static Logger log = LoggerFactory.getLogger(DistributedLock.class);

    private final RedissonClient redissonClient;

    private static final long DEFAULT_TIMEOUT = 60L;// key过期时间默认1分钟后

    private static final TimeUnit DEFAULT_TIME_UNIT = TimeUnit.SECONDS;//默认过期时间单位-秒钟

    private DistributedLock(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    public static DistributedLock of(RedissonClient redissonClient) {
        return new DistributedLock(redissonClient);
    }

    /**
     * 分布式锁-默认锁60秒
     *
     * @param lockName lockKey
     * @param runnable 任务
     * @return true 锁成功  false 锁失败
     */
    public boolean runIfLockCanGet(final String lockName, Runnable runnable) {
        return runIfLockCanGet(lockName, runnable, DEFAULT_TIMEOUT, DEFAULT_TIME_UNIT);
    }

    /**
     * 分布式锁
     *
     * @param lockName lockKey
     * @param runnable 任务
     * @param timeout  超时时间
     * @param unit     超时时间单位
     * @return true 锁成功  false 锁失败
     */
    public boolean runIfLockCanGet(final String lockName, Runnable runnable, final long timeout, TimeUnit unit) {
        RLock lock = redissonClient.getLock(lockName);
        if (Objects.isNull(lock)) {
            log.info("runIfLockCanGet lock is null lockName : {}", lockName);
            return false;
        }
        try {
            if (lock.tryLock(0, timeout, unit)) {
                if (Objects.nonNull(runnable)) {
                    runnable.run();
                }
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            log.error("runIfLockCanGet error lockName : {}", lockName, e);
            throw new RuntimeException("runIfLockCanGet error lockName :" + lockName, e);
        } finally {
            unlock(lock);
        }
    }

    /**
     * 分布式锁-异步
     *
     * @param lockName lockKey
     * @param callable 任务
     * @param timeout  超时时间
     * @param unit     超时时间单位
     * @return Future 异步任务
     */
    public <T> Future<T> callIfLockCanGet(final String lockName, Callable<T> callable, final long timeout, TimeUnit unit) {
        RLock lock = redissonClient.getLock(lockName);
        if (Objects.isNull(lock)) {
            log.info("callIfLockCanGet lock is null lockName : {}", lockName);
            return null;
        }
        ExecutorService executor = Executors.newCachedThreadPool();
        try {
            if (lock.tryLock(0, timeout, unit)) {
                return executor.submit(callable);
            } else {
                return null;
            }
        } catch (Exception e) {
            log.error("callIfLockCanGet error lockKey {}", lockName);
            throw new RuntimeException("任务执行异常");
        } finally {
            executor.shutdown();
            unlock(lock);
        }
    }

    /**
     * 解锁
     */
    public void unlock(RLock lock) {
        if (lock.getHoldCount() != 0) {
            lock.unlock();
        }
    }

}
