package cc.smtweb.framework.core.mvc.scheduler.job;

import cc.smtweb.framework.core.cache.redis.RedisManager;
import cc.smtweb.framework.core.db.DbEngine;
import cc.smtweb.framework.core.mvc.controller.IBeanContext;
import cc.smtweb.framework.core.mvc.scheduler.AbstractJob;
import cc.smtweb.framework.core.mvc.scheduler.AbstractJobExecutor;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.locks.ReentrantLock;

@Slf4j
public class RedisJobExecutor extends AbstractJobExecutor implements Runnable {
    public static final int TIME_CHECK_SEC = 30;
    public static final int TIME_LOOK_SEC = 3 * TIME_CHECK_SEC;

    private ReentrantLock lock = new ReentrantLock();
    private RedisManager redisManager;

    public RedisJobExecutor(IBeanContext beanContext, ExecutorService executor, Long redisLockValue, DbEngine dbEngine, RedisManager redisManager) {
        super(beanContext, executor, redisLockValue, dbEngine);
        this.redisManager = redisManager;
    }

    @Override
    public boolean tryLock(AbstractJob job) {
        // redis检查， 同group需要排队等待执行\
        String key = job.getKey();

        boolean redisLocked = redisManager.setnx(key, redisLockValue, TIME_LOOK_SEC);

        if (!redisLocked) {
            Long oldLockedValue = redisManager.get(key, Long.class);
            if (!redisLockValue.equals(oldLockedValue)) {
                return false;
            }
        }

        return redisLocked;
    }

    @Override
    public void unlock(AbstractJob job) {
        redisManager.del(job.getKey());
    }

    @Override
    public void run() {
        if (lock.tryLock()) {
            try {
                // 定时更新redis锁定状态
                long now = System.currentTimeMillis();
                jobMap.forEach((key, value) -> {
                    long lastTime = value.getLastTime();

                    if (lastTime > 0 && now - lastTime >= TIME_CHECK_SEC) {
                        if (redisManager.set(key, redisLockValue, TIME_LOOK_SEC)) {
                            value.updateLastTime();
                        } else {
                            log.error("update redis lock failed, key: " + key);
                        }
                    }
                });
            } finally {
                lock.unlock();
            }
        }
    }
}
