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

import cc.smtweb.framework.core.common.SwMap;
import cc.smtweb.framework.core.db.DbEngine;
import cc.smtweb.framework.core.mvc.controller.IBeanContext;
import cc.smtweb.framework.core.util.DateUtil;
import cc.smtweb.framework.core.util.JsonUtil;
import lombok.Getter;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;

public abstract class AbstractJobExecutor {
    @Getter
    private IBeanContext beanContext;
    private ExecutorService executor;
    protected Long redisLockValue;
    private DbEngine dbEngine;
    protected final Map<String, AbstractJobQueue> jobMap = new HashMap<>();

    public AbstractJobExecutor(IBeanContext beanContext, ExecutorService executor, Long redisLockValue, DbEngine dbEngine) {
        this.beanContext = beanContext;
        this.executor = executor;
        this.redisLockValue = redisLockValue;
        this.dbEngine = dbEngine;
    }

    public abstract boolean tryLock(AbstractJob job);

    public abstract void unlock(AbstractJob job);

    // 执行任务
    public void execute(final AbstractJobQueue jobQueue, final AbstractJob job) {
        this.executor.submit(() -> {
            if (tryLock(job)) {
                try {
                    jobQueue.execute(job);
                } finally {
                    unlock(job);
                }
            }
        });
    }

    void saveBreakPoint(AbstractJob job, SchedulerPoint schedulerPoint) {
        if (schedulerPoint != null && schedulerPoint.changed) {
            String value = JsonUtil.encodeString(schedulerPoint.value);
            long now = DateUtil.nowDateTimeLong();
            if (schedulerPoint.insert) {
                dbEngine.update("insert into sw_user.sch_break_point(bp_id, bp_job_name, bp_machine_id, bp_value, bp_create_time, bp_last_time) values(?,?,?,?,?,?)",
                        dbEngine.nextId(), job.getName(), this.redisLockValue, value, now, now);
            } else {
                dbEngine.update("update sw_user.sch_break_point set bp_machine_id=?, bp_value=?, bp_last_time=? where bp_job_name=?",
                        this.redisLockValue, value, now, job.getName());
            }
        }
    }

    SchedulerPoint loadBreakPoint(AbstractJob job) {
        String value = dbEngine.queryString("select bp_value from sw_user.sch_break_point where bp_job_name=?", job.getName());

        SchedulerPoint result;
        if (value != null) {
            SwMap swMap = JsonUtil.parse(value, SwMap.class);
            result = new SchedulerPoint(swMap);
        } else {
            result = new SchedulerPoint();
        }

        return result;
    }

    AbstractJobQueue initJobQueue(AbstractJob job) {
        String key = job.getKey();

        AbstractJobQueue jobQueue = jobMap.get(key);
        if (jobQueue == null) {
            jobQueue = job.createJobQueue(this);
            jobMap.put(key, jobQueue);
        }

        return jobQueue;
    }
}
