package com.njzscloud.common.sichen.support;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.njzscloud.common.core.tuple.Tuple2;
import com.njzscloud.common.core.utils.GroupUtil;
import com.njzscloud.common.sichen.SysTaskEntity;
import com.njzscloud.common.sichen.SysTaskService;
import com.njzscloud.common.sichen.contant.ScheduleType;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@RequiredArgsConstructor
public class TaskStore implements BeanPostProcessor {
    private final Map<String, Tuple2<Object, Method>> fn = new HashMap<>();
    private final List<TaskInfo> tasks = new LinkedList<>();
    private final SysTaskService sysTaskService;


    public Tuple2<Object, Method> getFn(String fname) {
        return fn.get(fname);
    }

    public List<TaskInfo> load(long now, long soon) {
        // log.info("加载任务：{}", DateUtil.formatDateTime(new Date(soon * 1000)));
        List<TaskInfo> l1 = sysTaskService.list(Wrappers.<SysTaskEntity>lambdaQuery()
                        .lt(SysTaskEntity::getCriticalTiming, soon)
                        .eq(SysTaskEntity::getDisabled, false)
                )
                .stream()
                .map(it -> BeanUtil.copyProperties(it, TaskInfo.class))
                .collect(Collectors.toList());
        List<TaskInfo> l2 = tasks.stream()
                .filter(it -> it.getCriticalTiming() <= soon)
                .collect(Collectors.toList());
        l2.addAll(l1);
        return l2;
    }

    public void update(List<TaskInfo> tasks) {
        if (tasks == null || tasks.isEmpty()) return;
        Map<Long, TaskInfo> map = GroupUtil.k_o(tasks, TaskInfo::getId);

        ArrayList<SysTaskEntity> list = new ArrayList<>();
        for (TaskInfo task : this.tasks) {
            TaskInfo taskInfo = map.get(task.getId());
            if (taskInfo == null) {
                list.add(new SysTaskEntity()
                        .setId(task.getId())
                        .setCriticalTiming(task.getCriticalTiming())
                        .setDisabled(task.getCriticalTiming() <= 0 ? true : null)
                );
                continue;
            }
            task.setCriticalTiming(taskInfo.getCriticalTiming());
        }
        if (list.isEmpty()) return;
        sysTaskService.updateBatchById(list);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> clazz = bean.getClass();
        for (Method method : clazz.getDeclaredMethods()) {
            Task task = method.getAnnotation(Task.class);
            if (task != null) {
                String value = task.value();
                String fname = StrUtil.isBlank(value) ? method.getName() : value;
                if (fn.containsKey(fname)) {
                    log.warn("任务执行函数重复：{}", fname);
                    continue;
                } else {
                    fn.put(fname, Tuple2.create(bean, method));
                    log.info("任务执行函数：{}", fname);
                }

                int period = task.period();
                String cron = task.cron();
                if (StrUtil.isNotBlank(cron) || period > 0) {
                    String memo_ = task.memo();
                    String memo = StrUtil.isBlank(memo_) ? clazz.getCanonicalName() + "#" + method.getName() : memo_;
                    String scheduleConf = StrUtil.isNotBlank(cron) ? cron : String.valueOf(period);
                    ScheduleType scheduleType = StrUtil.isNotBlank(cron) ? ScheduleType.Cron : ScheduleType.Fixed;
                    tasks.add(new TaskInfo()
                            .setId(IdUtil.getSnowflakeNextId())
                            .setMemo(memo)
                            .setFn(fname)
                            .setScheduleType(scheduleType)
                            .setScheduleConf(scheduleConf)
                            .setCriticalTiming(TaskUtil.computedNextTiming(scheduleType, scheduleConf))
                    );
                    log.info("添加任务：{}", memo);
                }
            }
        }
        return bean;
    }
}
