package com.njzscloud.common.sichen.dispatcher;

import cn.hutool.core.thread.ThreadUtil;
import com.njzscloud.common.sichen.support.*;
import lombok.extern.slf4j.Slf4j;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@Slf4j
public class SichenScheduler {

    private static final int SCAN_TASK_PERIOD = 5000;
    private final Map<Long, List<TaskHandle>> todolist = new ConcurrentHashMap<>();
    private final TaskStore taskStore;
    private final Thread scanThread;
    private final Thread dripThread;

    public SichenScheduler(TaskStore taskStore) {
        log.info("任务调度器启动中...");
        this.taskStore = taskStore;
        scanThread = new Thread(this::scanTask);
        scanThread.setName("任务扫描线程");
        scanThread.start();
        dripThread = new Thread(this::drip);
        dripThread.setName("计时器线程");
        dripThread.start();
        log.info("任务调度器已启动");
    }

    public void stop() {
        log.info("正在停止任务调度器...");
        ThreadUtil.interrupt(scanThread, true);
        ThreadUtil.interrupt(dripThread, true);
        log.info("任务调度器已停止");
    }

    private void scanTask() {
        long cost = 0;
        while (true) {
            if (cost < SCAN_TASK_PERIOD) {
                try {
                    TimeUnit.MILLISECONDS.sleep(SCAN_TASK_PERIOD - System.currentTimeMillis() % 1000);
                } catch (InterruptedException e) {
                    log.info("任务扫描线程被中断");
                    return;
                }
            }
            long now = System.currentTimeMillis();
            long soon = now + SCAN_TASK_PERIOD;

            try {
                List<TaskInfo> taskInfos = taskStore.load(now / 1000, soon / 1000);
                if (taskInfos != null && !taskInfos.isEmpty()) {
                    schedule(now / 1000, soon / 1000, taskInfos);
                }
            } catch (Exception e) {
                log.error("任务调度失败", e);
            }

            cost = System.currentTimeMillis() - now;
        }
    }

    private void schedule(long now, long soon, List<TaskInfo> taskInfos) {
        try {
            List<TaskInfo> updateList = new LinkedList<>();
            for (TaskInfo taskInfo : taskInfos) {
                try {
                    long timing = taskInfo.getCriticalTiming();
                    if (timing == now) {
                        Cable.execute(new TaskHandle(taskInfo));
                    } else if (timing > now) {
                        List<TaskHandle> tasks = todolist.computeIfAbsent(timing % 60, k -> new LinkedList<>());
                        tasks.add(new TaskHandle(taskInfo));
                    }
                    long criticalTiming = timing;
                    for (int i = 0; ; i++) {
                        timing = TaskUtil.computedNextTiming(now + i, taskInfo);
                        if (timing != 0 && timing > now && timing < soon) {
                            if (criticalTiming == timing) {
                                continue;
                            }
                            List<TaskHandle> tasks = todolist.computeIfAbsent(timing % 60, k -> new LinkedList<>());
                            tasks.add(new TaskHandle(taskInfo));
                            criticalTiming = timing;
                        } else {
                            criticalTiming = timing;
                            break;
                        }
                    }
                    updateList.add(new TaskInfo(taskInfo).setCriticalTiming(criticalTiming));
                } catch (Exception e) {
                    log.error("任务调度失败", e);
                }
            }
            taskStore.update(updateList);
        } catch (Exception e) {
            log.error("任务调度失败", e);
        }
    }

    private void drip() {
        while (true) {
            try {
                TimeUnit.MILLISECONDS.sleep(1000 - System.currentTimeMillis() % 1000);
            } catch (InterruptedException e) {
                log.info("计时器线程被中断");
                return;
            }

            long now = System.currentTimeMillis() / 1000;

            for (int i = 0; i < 2; i++) {
                List<TaskHandle> tasks = todolist.remove(now % 60 + i);
                if (tasks == null || tasks.isEmpty()) {
                    continue;
                }
                Cable.execute(tasks);
            }
        }
    }
}
