package com.njzscloud.common.sichen.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.njzscloud.common.core.ex.Exceptions;
import com.njzscloud.common.mp.support.PageParam;
import com.njzscloud.common.mp.support.PageResult;
import com.njzscloud.common.sichen.contant.ScheduleType;
import com.njzscloud.common.sichen.contant.TaskLogLevel;
import com.njzscloud.common.sichen.mapper.TaskMapper;
import com.njzscloud.common.sichen.pojo.entity.TaskEntity;
import com.njzscloud.common.sichen.pojo.param.ModifyTaskParam;
import com.njzscloud.common.sichen.pojo.param.SearchTaskParam;
import com.njzscloud.common.sichen.pojo.param.SearchTaskResult;
import com.njzscloud.common.sichen.support.TaskInfo;
import com.njzscloud.common.sichen.support.TaskStore;
import com.njzscloud.common.sichen.util.TaskUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * 定时任务表
 */
@Slf4j
public class DbTaskService extends ServiceImpl<TaskMapper, TaskEntity> implements TaskStore, IService<TaskEntity> {

    /**
     * 修改
     */
    public void modify(ModifyTaskParam modifyTaskParam) {
        WLOCK.lock();
        try {
            TaskEntity oldData = this.getById(modifyTaskParam.getId());
            Assert.notNull(oldData, () -> Exceptions.clierr("任务不存在，无法修改"));
            ScheduleType scheduleType = modifyTaskParam.getScheduleType();
            String scheduleConf = modifyTaskParam.getScheduleConf();

            Long criticalTiming = null;
            if (scheduleType != null && StrUtil.isNotBlank(scheduleConf)) {
                if (scheduleType != oldData.getScheduleType() || scheduleConf.equals(oldData.getScheduleConf())) {
                    criticalTiming = TaskUtil.computedNextTiming(scheduleType, scheduleConf);
                    Boolean disabled = modifyTaskParam.getDisabled();
                    disabled = disabled == null ? oldData.getDisabled() : disabled;
                    modifyTaskParam.setDisabled(scheduleType != ScheduleType.Manually && (criticalTiming <= 0 || disabled));
                }
            } else {
                modifyTaskParam.setScheduleType(null);
                modifyTaskParam.setScheduleConf(null);
            }

            TaskEntity taskEntity = BeanUtil.copyProperties(modifyTaskParam, TaskEntity.class)
                .setCriticalTiming(criticalTiming);
            this.updateById(taskEntity);
        } finally {
            WLOCK.unlock();
        }
    }

    /**
     * 删除
     */
    @Transactional(rollbackFor = Exception.class)
    public void del(List<Long> ids) {
        WLOCK.lock();
        try {
            this.removeBatchByIds(ids);
        } finally {
            WLOCK.unlock();
        }
    }

    /**
     * 详情
     */
    public SearchTaskResult detail(Long id) {
        TaskEntity taskEntity = this.getById(id);
        if (taskEntity == null) return null;
        return BeanUtil.copyProperties(taskEntity, SearchTaskResult.class)
            .setBuiltin(Boolean.FALSE);
    }

    /**
     * 分页查询
     */
    public PageResult<SearchTaskResult> paging(PageParam pageParam, SearchTaskParam searchTaskParam) {
        String taskName = searchTaskParam.getTaskName();
        String fn = searchTaskParam.getFn();
        ScheduleType scheduleType = searchTaskParam.getScheduleType();
        Boolean disabled = searchTaskParam.getDisabled();
        return PageResult.of(this.page(pageParam.toPage(), Wrappers.<TaskEntity>lambdaQuery()
            .like(StrUtil.isNotBlank(taskName), TaskEntity::getTaskName, taskName)
            .like(StrUtil.isNotBlank(fn), TaskEntity::getFn, fn)
            .eq(scheduleType != null, TaskEntity::getScheduleType, scheduleType)
            .eq(disabled != null, TaskEntity::getDisabled, disabled)
        ).convert(it -> BeanUtil.copyProperties(it, SearchTaskResult.class)
            .setBuiltin(Boolean.FALSE)
        ));
    }

    @Override
    public void registerTask(TaskInfo taskInfo) {
        ScheduleType scheduleType = taskInfo.getScheduleType();
        long criticalTiming = TaskUtil.computedNextTiming(scheduleType, taskInfo.getScheduleConf());
        boolean disabled = scheduleType != ScheduleType.Manually && (criticalTiming <= 0 || taskInfo.isDisabled());
        TaskEntity taskEntity = BeanUtil.copyProperties(taskInfo, TaskEntity.class)
            .setId(null)
            .setCriticalTiming(criticalTiming)
            .setDisabled(disabled);
        TaskLogLevel logLevel = taskInfo.getLogLevel();
        if (logLevel == null) {
            taskEntity.setLogLevel(TaskLogLevel.INFO);
        }

        this.save(taskEntity);
    }

    @Override
    public List<TaskInfo> loadTask(long soon) {
        return this.list(Wrappers.<TaskEntity>lambdaQuery()
                .gt(TaskEntity::getCriticalTiming, 0)
                .le(TaskEntity::getCriticalTiming, soon)
                .eq(TaskEntity::getDisabled, false)
                .ne(TaskEntity::getScheduleType, ScheduleType.Manually))
            .stream()
            .map(it -> BeanUtil.copyProperties(it, TaskInfo.class).setBuiltin(false))
            .toList();
    }

    @Override
    public void updateCriticalTiming(long id, long criticalTiming) {
        boolean exists = this.exists(Wrappers.<TaskEntity>lambdaQuery().eq(TaskEntity::getId, id));
        if (!exists) return;
        this.updateById(new TaskEntity()
            .setId(id)
            .setCriticalTiming(criticalTiming)
            .setDisabled(criticalTiming <= 0));

    }

    @Override
    public boolean trigger(Long id) {
        TaskEntity taskEntity = this.getById(id);
        if (taskEntity != null) {
            Assert.isFalse(taskEntity.getDisabled(), () -> Exceptions.exception("请先启用任务"));
            TaskUtil.execute(BeanUtil.copyProperties(taskEntity, TaskInfo.class), true);
            return true;
        } else {
            return false;
        }
    }

    public void disable(Long id, Boolean disable) {
        this.updateById(new TaskEntity()
            .setId(id)
            .setDisabled(disable)
        );
    }
}
