package cn.com.capcloud.core.quartz.task;

import cn.com.capcloud.ccui.core.common.json.JackJSONObject;
import cn.com.capcloud.core.noentitydb.BaseDao;
import cn.com.capcloud.core.websocket.RedisPublication;
import cn.com.capcloud.core.cache.BaseConfigStaticCache;
import cn.com.capcloud.core.quartz.config.TaskJob;
import org.quartz.CronExpression;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.Message;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.TriggerContext;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.ReentrantLock;

@Service
public class TaskService {
    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(TaskService.class);
    @Resource
    private JdbcTemplate jdbcTemplate;
    @Resource
    private RedisPublication redisPublication;
    /**
     * 可重入锁
     */
    private ReentrantLock lock = new ReentrantLock();
    /**
     * 定时任务线程池
     */
    @Resource
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;
    /**
     * 存放已经启动的任务map
     */
    private Map<String, ScheduledFuture> scheduledFutureMap = new ConcurrentHashMap<>();


    public void runTask(Message message){
        String messageBody = new String(message.getBody());
        JackJSONObject messageObject = JackJSONObject.fromObject(messageBody);
        runTask(messageObject);
    }

    public void runTask(JackJSONObject messageObject){
        String userId = String.valueOf(messageObject.get("userId"));
        String taskUUID = String.valueOf(messageObject.get("taskUUID"));
        String type = String.valueOf(messageObject.get("type"));
        String wsCode="wsTask";
        try {
            if(type.equals("start")||type.equals("startNow")||type.equals("stop")||type.equals("restart")){
                Thread.sleep(1000);//模拟延时1秒
                redisPublication.sendMessage("{\"wsCode\":\""+wsCode+"\",\"message\":\"正在执行...\",\"status\":\"ok\",\"isHideMask\":\"N\"}",String.valueOf(userId));
                Thread.sleep(1000);//模拟延时1秒
                if(type.equals("start")){//根据任务 uuid 启动任务
                    start(taskUUID,userId);
                    redisPublication.sendMessage("{\"wsCode\":\""+wsCode+"\",\"message\":\"启动任务成功\",\"status\":\"ok\",\"isHideMask\":\"Y\"}",String.valueOf(userId));
                }
                if(type.equals("startNow")){//根据任务 uuid 启动任务 (一次)
                    startNow(taskUUID,userId);
                    redisPublication.sendMessage("{\"wsCode\":\""+wsCode+"\",\"message\":\"启动任务成功 (一次)\",\"status\":\"ok\",\"isHideMask\":\"Y\"}",String.valueOf(userId));
                }
                if(type.equals("stop")){//根据 uuid 停止任务
                    stop(taskUUID);
                    redisPublication.sendMessage("{\"wsCode\":\""+wsCode+"\",\"message\":\"停止任务成功\",\"status\":\"ok\",\"isHideMask\":\"Y\"}",String.valueOf(userId));
                }
                if(type.equals("restart")){//根据任务 uuid 重启任务
                    restart(taskUUID,userId);
                    redisPublication.sendMessage("{\"wsCode\":\""+wsCode+"\",\"message\":\"重启任务成功\",\"status\":\"ok\",\"isHideMask\":\"Y\"}",String.valueOf(userId));
                }
            }else{
                redisPublication.sendMessage("{\"wsCode\":\""+wsCode+"\",\"message\":\"没有找到对应的操作："+type+"\",\"status\":\"no_code\",\"isHideMask\":\"Y\"}",String.valueOf(userId));
            }
            updateTaskStatus();
            Thread.sleep(1000);//模拟延时1秒
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    public void updateTaskStatus() {
        //初始化所有状态
        String whereSql=" where status=0 ";
        List<Map<String, Object>> scheduledTaskBeanList = BaseDao.queryNotPage("tbl_data_task",  whereSql,jdbcTemplate);
        String taskBeanStatus="";
        for (int i = 0; i < scheduledTaskBeanList.size(); i++) {
            String uuid=String.valueOf(scheduledTaskBeanList.get(i).get("uuid"));
            if(createInstance(uuid)==null){
                taskBeanStatus="weishili";
            }else if(isStart(uuid)){
                taskBeanStatus="gongzuozhong";
            }else{
                taskBeanStatus="weigongzuo";
            }
            jdbcTemplate.execute("update tbl_data_task set task_bean_status='"+taskBeanStatus+"' where uuid='"+uuid+"'");
        }
    }

    /**
     * 根据任务 uuid 启动任务
     */
    public Boolean start(String uuid,String userId) {
        //添加锁放一个线程启动，防止多人启动多次
        lock.lock();
        logger.info(">>>>>> 添加任务启动锁完毕");
        try {//校验是否已经启动
            if (this.isStart(uuid)) {
                logger.info(">>>>>> 当前任务已经启动，无需重复启动！");
                return false;
            }
            this.doStartTask(uuid,userId);//启动任务
        } finally {
            lock.unlock();// 释放锁
            logger.info(">>>>>> 释放任务启动锁完毕");
        }
        return true;
    }
    /**
     * 根据任务 uuid 启动任务 (一次)
     */
    public Boolean startNow(String uuid,String userId) {
        Map<String,Object> scheduledTask=BaseConfigStaticCache.getRedisCache().getTaskInfoByUUID(uuid);
        String beanClass = String.valueOf(scheduledTask.get("task_bean_class"));
        logger.info(">>>>>> 启动任务 "+beanClass+" 开始 >>>>>>");
        //添加锁放一个线程启动，防止多人启动多次
        lock.lock();
        logger.info(">>>>>> 添加任务启动锁完毕");
        try {//启动任务
            this.doStartTaskNow(uuid,userId);
        } finally {
            lock.unlock();// 释放锁
            logger.info(">>>>>> 释放任务启动锁完毕");
        }
        logger.info(">>>>>> 启动任务 "+beanClass+" 结束 >>>>>>");
        return true;
    }

    /**
     * 根据 uuid 停止任务
     */
    public Boolean stop(String uuid) {
        String taskKey=getTaskKey(uuid);
        logger.info(">>>>>> 进入停止任务  >>>>>>");
        //当前任务实例是否存在
        boolean taskStartFlag = scheduledFutureMap.containsKey(taskKey);
        logger.info(">>>>>> 当前任务实例是否存在 "+taskStartFlag);
        if (taskStartFlag) {
            //获取任务实例
            ScheduledFuture scheduledFuture = scheduledFutureMap.get(taskKey);
            //关闭实例
            scheduledFuture.cancel(true);
        }
        logger.info(">>>>>> 结束停止任务 >>>>>>");
        return taskStartFlag;
    }

    /**
     * 根据任务 uuid 重启任务
     */
    public Boolean restart(String uuid,String userId) {
        logger.info(">>>>>> 进入重启任务  >>>>>>");
        this.stop(uuid);//先停止
        return this.start(uuid,userId);//再启动
    }

    /**
     * 程序启动时初始化  ==> 启动所有正常状态的任务
     */
    public void initAllTask(List<Map<String,Object>> scheduledTaskBeanList) {
        logger.info("程序启动 ==> 初始化所有任务开始 ！size="+scheduledTaskBeanList.size()+"");
        if (CollectionUtils.isEmpty(scheduledTaskBeanList)) {
            updateTaskStatus();
            return;
        }
        for (Map<String,Object> scheduledTaskMap : scheduledTaskBeanList) {
            String uuid=String.valueOf(scheduledTaskMap.get("uuid"));
            //校验是否已经启动
            if (this.isStart(uuid)) {
                continue;
            }
            this.doStartTask(uuid,"");//启动任务
        }
        logger.info("程序启动 ==> 初始化所有任务结束 ！size="+scheduledTaskBeanList.size()+"");
    }

    /**
     * 执行启动任务
     */
    private void doStartTask(String uuid,String userId) {
        Map<String,Object> scheduledTask=BaseConfigStaticCache.getRedisCache().getTaskInfoByUUID(uuid);
        String taskKey=getTaskKey(uuid);
        //表达式
        String taskCron = String.valueOf(scheduledTask.get("task_cron_self"));
        if("null".equals(taskCron)||taskCron.length()==0){//如果task_cron_self表达式为空则使用task_cron表达式
            taskCron = String.valueOf(scheduledTask.get("task_cron"));
        }
        //获取需要定时调度的接口
        TaskJob scheduledTaskJob = (TaskJob) createInstance(uuid);
        if(scheduledTaskJob!=null){
            scheduledTaskJob.setJdbcTemplate(jdbcTemplate);
            scheduledTaskJob.setUserId(userId);
            logger.info(">>>>>> 任务 [ "+ scheduledTask.get("task_name")+" ] ,cron="+taskCron+" ");
            String finalTaskCron = taskCron;
            ScheduledFuture scheduledFuture = threadPoolTaskScheduler.schedule(scheduledTaskJob,
                    new Trigger() {
                        @Override
                        public Date nextExecutionTime(TriggerContext triggerContext) {
                            CronTrigger cronTrigger = new CronTrigger(finalTaskCron);
                            return cronTrigger.nextExecutionTime(triggerContext);
                        }
                    });
            //将启动的任务放入 map
            scheduledFutureMap.put(taskKey, scheduledFuture);
        }else{
            logger.info("计划任务异常 请检查路径是否正确  "+taskKey);
        }
    }

    /**
     * 执行启动任务(立即执行一次)
     */
    private void doStartTaskNow(String uuid,String userId) {
        Map<String,Object> scheduledTask=BaseConfigStaticCache.getRedisCache().getTaskInfoByUUID(uuid);
        //获取需要定时调度的接口
        TaskJob scheduledTaskJob = (TaskJob) createInstance(uuid);
        if(scheduledTaskJob!=null){
            scheduledTaskJob.setJdbcTemplate(jdbcTemplate);
            scheduledTaskJob.setUserId(userId);
            logger.info(">>>>>> 立即执行一次任务 [ "+ scheduledTask.get("task_name")+" ]");
            scheduledTaskJob.run();
        }else{
            logger.info("计划任务异常 请检查路径是否正确  ");
        }
    }

    /**
     * 任务是否已经启动
     */
    public Boolean isStart(String uuid) {
        String taskKey=getTaskKey(uuid);
        //校验是否已经启动
        if (scheduledFutureMap.containsKey(taskKey)) {
            if (!scheduledFutureMap.get(taskKey).isCancelled()) {
                return true;
            }
        }
        return false;
    }
    public static Object createInstance(String uuid){
        Map<String, Object> scheduledTask=BaseConfigStaticCache.getRedisCache().getTaskInfoByUUID(uuid);
        String beanClass=String.valueOf(scheduledTask.get("task_bean_class"));
        try {
            Class clz = Class.forName(beanClass);
            Object obj = clz.newInstance();
            return obj;
        } catch (Exception e) {
            return null;
        }
    }

    public static String getTaskKey(String uuid){
        Map<String, Object> scheduledTask=BaseConfigStaticCache.getRedisCache().getTaskInfoByUUID(uuid);
        String code=String.valueOf(scheduledTask.get("task_code"));
        String beanClass=String.valueOf(scheduledTask.get("task_bean_class"));
        String taskKey=code+"_"+beanClass;
        return taskKey;
    }

    public static String checkTaskCron(String taskCron){
        if(!CronExpression.isValidExpression(taskCron)){
            return "";
        }
        return taskCron;
    }

    public static String formatDateByPattern(Date date,String dateFormat){
        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
        String formatTimeStr = null;
        if (date != null) {
            formatTimeStr = sdf.format(date);
        }
        return formatTimeStr;
    }
    /***
     * convert Date to cron ,eg.  "0 06 10 15 1 ? 2014"
     * @param date  : 时间转cron表达式
     * @return
     */
    public static String getCron(Date  date, long millisecond){
        long time;
        if(millisecond==0){
            time=date.getTime()+20000;
        }else{
            time=date.getTime()+millisecond;
        }
        String dateFormat="ss mm HH dd MM ? yyyy";
        return formatDateByPattern(new Date(time), dateFormat);
    }

}
