package com.jay.monitor.data.server.job;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Queue;
import java.util.concurrent.ArrayBlockingQueue;

/**
 * 调度执行器
 *
 * @author ： liukx
 * @time ： 2019/11/5 - 15:25
 */
@Component
public class ScheduledJob implements Runnable, InitializingBean {

    private Logger logger = LoggerFactory.getLogger(ScheduledJob.class);

    private Queue<FailsQueueData> failQueue = new ArrayBlockingQueue<FailsQueueData>(20);

    @Autowired(required = false)
    private List<ScheduleJobServiceAdaptor> dayJobServiceAdaptors;

    private int maxCount = 3;

    public void init() {
        // 启动一个线程，用来处理跑失败的job
        new Thread(this, "server-fails-job-Run").start();
        /**
         * 天的任务启动的时候就需要启动一次，因为每天就执行一次，避免因为宕机错过这一次时间而难以补救。
         * 所以幂等这部分需要很好的做处理
         */
        // dayJob();
    }


    @Override
    public void afterPropertiesSet() throws Exception {
        init();
    }

    /**
     * 每天执行一次
     */
//    @Scheduled(cron = "0 0 0 1/1 * ? ")
//    public void dayJob() {
//        processJob(ScheduleJobService.DAY_TYPE);
//    }
    @Scheduled(cron = "0 0 8 * * ?")
    public void fixedDayJob() {
        processJob(ScheduleJobService.DAY_TYPE);
    }

//    @Scheduled(cron = "0/5 * * * * ?")
//    public void secondJob() {
//        processJob(ScheduleJobService.DAY_TYPE);
//    }

    /**
     * 每小时执行一次
     */
    @Scheduled(cron = "0 0 0/1 * * ?")
    public void hourJob() {
        processJob(ScheduleJobService.HOUR_TYPE);
    }

    private void processJobByType(ScheduleJobService job, int type) {
        if (type == ScheduleJobService.DAY_TYPE) {
            boolean invoke = job.isDayInvoke();
            if (invoke) {
                try {
                    job.processDayJob();
                } catch (Exception e) {
                    logger.error(" day : " + job.toString() + " invoke fails", e);
                    addFailsQueue(job, type);
                    e.printStackTrace();
                }
            }
        } else if (type == ScheduleJobService.HOUR_TYPE) {
            boolean invoke = job.isHourInvoke();
            if (invoke) {
                try {
                    job.processHourJob();
                } catch (Exception e) {
                    logger.error(" hour : " + job.toString() + " invoke fails", e);
                    addFailsQueue(job, type);
                    e.printStackTrace();
                }
            }
        }
    }

    private void processJob(int hourType) {
        if (dayJobServiceAdaptors != null) {
            dayJobServiceAdaptors.forEach((job) -> {
                processJobByType(job, hourType);
            });
        }
    }

    /**
     * 添加失败的任务到队列中重新执行
     *
     * @param job
     */
    private void addFailsQueue(ScheduleJobService job, int type) {
        logger.warn(" 加入一个失败任务到队列中 : " + job.toString());
        failQueue.add(new FailsQueueData(job, type));
    }


    @Override
    public void run() {
        logger.info(" 异常Job重跑线程启动 ...");
        while (true) {
            FailsQueueData queueData = null;
            try {
                queueData = failQueue.poll();
                if (queueData != null) {
                    ScheduleJobService jobService = queueData.getJobService();
                    processJobByType(jobService, queueData.getType());
                }
            } catch (Throwable e) {
                int count = queueData.getCount() + 1;
                if (count > maxCount) {
                    logger.error("job 重试执行超过最大次数 : " + queueData.getJobService().toString());
                } else {
                    logger.warn(" job : " + queueData.getJobService() + " 重试次数 + 1 目前 : " + count);
                    queueData.setCount(count);
                    failQueue.add(queueData);
                }
            }
            try {
                // 防止CPU飙高。
                Thread.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 异常job的定义
     */
    class FailsQueueData {
        /**
         * 重试次数
         */
        private Integer count = 0;
        /**
         * 执行类型 [小时/天] @link ScheduleJobService
         */
        private Integer type;
        /**
         * 具体执行器
         */
        private ScheduleJobService jobService;

        public FailsQueueData(ScheduleJobService jobService, int type) {
            this.jobService = jobService;
            this.type = type;
        }

        public Integer getType() {
            return type;
        }

        public void setType(Integer type) {
            this.type = type;
        }

        public Integer getCount() {
            return count;
        }

        public void setCount(Integer count) {
            this.count = count;
        }

        public ScheduleJobService getJobService() {
            return jobService;
        }

        public void setJobService(ScheduleJobService jobService) {
            this.jobService = jobService;
        }
    }
//    @Scheduled(cron = "0/5 * * * * ?")
//    public void test() {
//        System.out.println("=======测试成功==========");
//    }
}
