package com.bocloud.ims.service.engine;

import com.bocloud.cmp.vm.model.VmSnapshotParam;
import com.bocloud.common.model.BocloudResult;
import com.bocloud.common.model.RequestContext;
import com.bocloud.ims.entity.resource.CloudServer;
import com.bocloud.ims.entity.resource.SnapshotTask;
import com.bocloud.ims.interfaces.resource.CloudServerService;
import com.bocloud.ims.repository.resource.CloudServerRepository;
import com.bocloud.ims.repository.resource.SnapshotTaskRepository;
import com.bocloud.ims.service.utils.BeanUtils;
import com.bocloud.service.core.CurrentService;
import com.bocloud.service.lock.BocloudACLock;
import com.bocloud.service.lock.LockFactory;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.SchedulingException;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.config.TriggerTask;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.util.Assert;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.*;

/**
 * 定时快照任务
 *
 * @author wangyu
 * @version 4.1.0-SNAPSHOT
 * @since 2019/7/27
 */
//@Configuration
//@EnableScheduling
public class SnapshotTaskEngine implements InitializingBean, SchedulingConfigurer {

    private static Logger logger = LoggerFactory.getLogger(SnapshotTaskEngine.class);
    private static final String FIELD_SCHEDULED_FUTURES = "scheduledTasks";
    @Autowired
    private CurrentService currentService;
    @Autowired
    private CloudServerService cloudServerService;
    @Autowired
    private CloudServerRepository cloudServerRepository;
    @Autowired
    private SnapshotTaskRepository snapshotTaskRepository;
    @Autowired
    private LockFactory lockFactory;
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    /**
     * 快照任务队列
     */
    private static BlockingQueue<SnapshotTask> queue = new LinkedBlockingQueue<>();
    /**
     * 任务注册器
     */
    private static ScheduledTaskRegistrar taskRegistrar;
    private static Set<ScheduledFuture<?>> scheduledFutures = null;
    private static Map<String, ScheduledFuture<?>> taskFutures = new ConcurrentHashMap<>();

    /**
     * 定时任务执行线程池
     *
     * @return
     */
    @Bean(destroyMethod = "shutdown")
    public Executor taskExecutor() {
        return new ScheduledThreadPoolExecutor(100);
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        // 创建一个线程池调度器，默认是单线程执行
        taskRegistrar.setScheduler(taskExecutor());
        SnapshotTaskEngine.taskRegistrar = taskRegistrar;
        if (!currentService.isLeader()) {
            return;
        }
        taskExecutor.submit(() -> {
            // 处理任务
            this.processTask();
        });
    }

    /**
     * 处理任务
     */
    private void processTask() {
        boolean flag = true;
        while (flag) {
            SnapshotTask task;
            try {
                task = queue.take();
                TriggerTask triggerTask = this.buildTriggerTask(task);
                if (null != triggerTask) {
                    logger.info("add Snapshot trigger task,name【{}】,remark【{}】,cron【{}】", task.getName(),
                            task.getRemark(), task.getCron());
                    addTriggerTask(task.getTaskId(), triggerTask);
                }
            } catch (InterruptedException e) {
                logger.error("get Snapshot task from queue InterruptedException!", e);
                flag = false;
                Thread.currentThread().interrupt();
            }
        }
    }

    /**
     * 构造快照任务执行触发类
     *
     * @param task
     * @return
     */
    private TriggerTask buildTriggerTask(SnapshotTask task) {
        String cron = task.getCron();
        if (StringUtils.isEmpty(cron)) {
            return null;
        }
        String name = task.getName();
        String remark = task.getRemark();
        if (null != task.getServerId()) {
            BocloudResult result = cloudServerService.detail(task.getServerId());
            if (result.isFailed()) {
                return null;
            }
            CloudServer server = (CloudServer) result.getData();
            if (StringUtils.isEmpty(name)) {
                name = server.getName();
                remark = name;
            }
        }
        TriggerTask triggerTask = new TriggerTask(new SnapshotTriggerTask(task, name, remark, cloudServerService,
                snapshotTaskRepository, cloudServerRepository), new SnapshotTrigger(task.getCron()));
        return triggerTask;
    }

    @SuppressWarnings("unchecked")
    private static Set<ScheduledFuture<?>> getScheduledFutures() {
        if (null == scheduledFutures) {
            try {
                scheduledFutures = (Set<ScheduledFuture<?>>) BeanUtils.getProperty(taskRegistrar,
                        FIELD_SCHEDULED_FUTURES);
            } catch (NoSuchFieldException e) {
                throw new SchedulingException("not found scheduledFutures field.");
            }
        }
        return scheduledFutures;
    }

    /**
     * 添加任务
     *
     * @param taskId
     * @param triggerTask
     */
    public static void addTriggerTask(String taskId, TriggerTask triggerTask) {
        if (taskFutures.containsKey(taskId)) {
            logger.warn("the taskId {} was added", taskId);
            return;
        }
        TaskScheduler scheduler = taskRegistrar.getScheduler();
        ScheduledFuture<?> future = scheduler.schedule(triggerTask.getRunnable(), triggerTask.getTrigger());
        getScheduledFutures().add(future);
        taskFutures.put(taskId, future);
    }

    /**
     * 取消任务
     *
     * @param taskId
     */
    public static void cancelTriggerTask(String taskId) {
        ScheduledFuture<?> future = taskFutures.get(taskId);
        if (future != null) {
            future.cancel(true);
        }
        taskFutures.remove(taskId);
        getScheduledFutures().remove(future);
    }

    /**
     * 重置任务
     *
     * @param taskId
     * @param triggerTask
     */
    public static void resetTriggerTask(String taskId, TriggerTask triggerTask) {
        cancelTriggerTask(taskId);
        addTriggerTask(taskId, triggerTask);
    }

    /**
     * 任务编号
     *
     * @return
     */
    public static Set<String> taskIds() {
        return taskFutures.keySet();
    }

    /**
     * 是否存在任务
     *
     * @param taskId
     * @return
     */
    public static boolean hasTask(String taskId) {
        return taskFutures.containsKey(taskId);
    }

    /**
     * 任务调度是否已经初始化完成
     *
     * @return
     */
    public static boolean inited() {
        return taskRegistrar != null && taskRegistrar.getScheduler() != null;
    }

    /**
     * 向队列中加入定时快照任务
     *
     * @param task
     */
    public static void put(SnapshotTask task) {
        queue.add(task);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // 将数据库快照定时创建任务缓存到队列中
//        this.init();
    }

    /**
     * @throws Exception
     */
    private void init() throws Exception {
        List<SnapshotTask> tasks = snapshotTaskRepository.listByVendorId(null);
        for (SnapshotTask task : tasks) {
            queue.add(task);
        }
    }

    /**
     * 快照定时任务实现内部类
     */
    class SnapshotTriggerTask implements Runnable {

        private SnapshotTask task;// 快照执行任务
        private String name;// 快照名称
        private String remark;// 快照描述
        private CloudServerService cloudServerService;
        private SnapshotTaskRepository snapshotTaskRepository;
        private CloudServerRepository cloudServerRepository;

        public SnapshotTriggerTask(SnapshotTask task, String name, String remark, CloudServerService cloudServerService,
                                   SnapshotTaskRepository snapshotTaskRepository, CloudServerRepository cloudServerRepository) {
            this.task = task;
            this.name = name;
            this.remark = remark;
            this.cloudServerService = cloudServerService;
            this.snapshotTaskRepository = snapshotTaskRepository;
            this.cloudServerRepository = cloudServerRepository;
        }

        @Override
        public void run() {
            String path = "snapshot_task_" + task.getId();
            try (BocloudACLock lock = lockFactory.getACLock(path)) {
                Assert.isTrue(lock.acquire(10, TimeUnit.SECONDS), "请求超时");
                Thread.sleep(1000);
                SnapshotTask snapshotTask = snapshotTaskRepository.query(task.getId());
                if (null == snapshotTask) {
                    return;
                }
                // 创建快照
                CloudServer server = cloudServerRepository.query(task.getServerId());
                if (null != server) {
                	if (StringUtils.isEmpty(name)) {
                        name = server.getName();
                    }
                    RequestContext context = new RequestContext(server.getCatalog(), server.getCreatorId(),
                            server.getTenantId(), server.getProjectId(), null);
                    cloudServerService.snapshot(new VmSnapshotParam(task.getServerId(), name + "_" + System.currentTimeMillis(), remark, null, null, null), context);
                }
            } catch (Exception e) {
                logger.error("Get error message：", e);
            }
        }
    }

    /**
     * 调度类
     */
    class SnapshotTrigger implements Trigger {

        private String cronExpress;

        public SnapshotTrigger(String cronExpress) {
            this.cronExpress = cronExpress;
        }

        @Override
        public Date nextExecutionTime(TriggerContext triggerContext) {
            CronTrigger cronTrigger = new CronTrigger(cronExpress);
            return cronTrigger.nextExecutionTime(triggerContext);
        }
    }

}
