/**
 * 文件：TaskServiceImpl.java 版本：1.0.0 日期： Copyright &reg; All right reserved.
 */

package com.mortals.xhx.base.system.task.service.impl;

import com.mortals.framework.ap.GlobalSysInfo;
import com.mortals.framework.common.code.ExcuteStatus;
import com.mortals.framework.common.code.TaskExcuteStrategy;
import com.mortals.framework.common.code.TaskInterimExcuteStatus;
import com.mortals.framework.exception.AppException;
import com.mortals.framework.service.ITaskExcuteService;
import com.mortals.framework.service.impl.AbstractCRUDServiceImpl;
import com.mortals.framework.springcloud.service.IApplicationStartedService;
import com.mortals.framework.util.DateUtils;
import com.mortals.framework.util.StringUtils;
import com.mortals.framework.util.SystemUtil;
import com.mortals.xhx.base.system.task.dao.TaskDao;
import com.mortals.xhx.base.system.task.model.TaskEntity;
import com.mortals.xhx.base.system.task.model.TaskQuery;
import com.mortals.xhx.base.system.task.service.TaskService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * <p>
 * Title: 任务信息
 * </p>
 * <p>
 * Description: TaskServiceImpl service接口
 * </p>
 * <p>
 * Copyright: Copyright &reg;
 * </p>
 * <p>
 * Company:
 * </p>
 *
 * @author
 * @version 1.0.0
 */
@Service("taskService")
public class TaskServiceImpl extends AbstractCRUDServiceImpl<TaskDao, TaskEntity, Long>
        implements TaskService, IApplicationStartedService {//

    private boolean isStop = false;
    /** 休眠时间 */
    private long sleepTime = 1000;

    private boolean isUnlockSuccess = true;

    private TaskEntity prevTask = null;

    private Thread thread = null;
    /** 日志打印时间，key：任务ID，value：最后一次打印日志时间 */
    private Map<Long, Long> printLogTime = new HashMap<Long, Long>();
    @Autowired(required=false)
    private TaskService taskService;

    public boolean doForceUpdateTaskLock(TaskEntity task) throws AppException {
        if (task == null) {
            return false;
        }
        Map<String, Object> data = new HashMap<String, Object>();
        data.put("status", ExcuteStatus.RUNNING.getValue());
        data.put("lastExcuteHost", task.getLastExcuteHost());
        Map<String, Object> condition = new HashMap<String, Object>();
        condition.put("status", ExcuteStatus.WAIT_RUN.getValue());
        condition.put("id", task.getId());
        int ret = dao.update(data, condition);
        return ret > 0 ? true : false;
    }

    public boolean doForceUpdateTaskUnLock(TaskEntity task) throws AppException {
        int ret = 0;
        if (task.getInterimExcuteStatus() == TaskInterimExcuteStatus.IMMEDIATE_EXECUTION_BEFORE_DELETE.getValue()) {
            ret = dao.delete(task.getId());
        } else {
            Map<String, Object> data = new HashMap<String, Object>();
            data.put("status", ExcuteStatus.WAIT_RUN.getValue());
            data.put("interimExcuteStatus", TaskInterimExcuteStatus.UNUSE.getValue());
            if (task.getLastExcuteTime() != null) {
                data.put("lastExcuteTime", task.getLastExcuteTime());
            }
            Map<String, Object> condition = new HashMap<String, Object>();
            condition.put("status", ExcuteStatus.RUNNING.getValue());
            condition.put("id", task.getId());
            ret = this.dao.update(data, condition);
        }
        return ret > 0 ? true : false;
    }

    public boolean doForceUpdateTaskUnLock(String excuteHost) throws AppException {
        if (StringUtils.isEmpty(excuteHost)) {
            return false;
        }
        Map<String, Object> data = new HashMap<>();
        data.put("status", ExcuteStatus.WAIT_RUN.getValue());
        Map<String, Object> condition = new HashMap<>();
        condition.put("lastExcuteHost", excuteHost);
        int ret = this.dao.update(data, condition);
        return ret > 0 ? true : false;
    }

    private int converTime(String time) {
        if (StringUtils.isEmpty(time)) {
            return 0;
        }
        String[] times = time.trim().split(":");
        StringBuffer ret = new StringBuffer();
        for (String tt : times) {
            tt = StringUtils.trim(tt);
            while (tt.length() < 2) {
                tt = "0" + tt;
            }
            ret.append(tt);
        }
        return Integer.parseInt(ret.toString());
    }

    private boolean checkPrintLog(TaskEntity task) {
        if (task == null) {
            return true;
        }
        int strategy = task.getExcuteStrategy() == null ? -1 : task.getExcuteStrategy();
        if (strategy == TaskExcuteStrategy.INTERVAL.getValue())// 按间隔时间
        {
            long currTime = System.currentTimeMillis();
            Long lastPrintTime = printLogTime.get(task.getId());
            if (lastPrintTime == null || lastPrintTime.longValue() == 0
                    || (currTime - lastPrintTime.longValue()) >= 600000) {
                printLogTime.put(task.getId(), currTime);
                return true;
            }
            return false;
        }
        return true;
    }

    @Override
    public int getOrder() {
        return 100;
    }

    @Override
    public void start() throws AppException {
        final String localIp = SystemUtil.getLocalHostIp();
        log.info("任务执行线程启动...-->" + localIp);
        thread = new Thread(() -> {
            // 将本机上次执行而未完成的任务状态变更
            try {
                taskService.doForceUpdateTaskUnLock(localIp);
            } catch (Throwable e) {
                log.error("释放上次执行而未完成的任务锁失败-->" + localIp, e);
            }
            int threadSize = 10;
            ExecutorService executorService = Executors.newFixedThreadPool(threadSize);
            while (!isStop) {
                if (!isUnlockSuccess && prevTask != null) {
                    try {
                        taskService.doForceUpdateTaskUnLock(prevTask);
                        isUnlockSuccess = true;
                        prevTask = null;
                    } catch (Throwable e) {
                        isUnlockSuccess = false;
                        if (checkPrintLog(prevTask)) {
                            log.error("释放任务锁失败-->" + prevTask, e);
                        }
                        try {
                            Thread.sleep(sleepTime);
                        } catch (Throwable ex) {
                        }
                        continue;
                    }
                }
                Calendar cal = Calendar.getInstance();
                int day = cal.get(Calendar.DAY_OF_MONTH);
                int week = cal.get(Calendar.DAY_OF_WEEK) - 1;
                if (week == 0) {
                    week = 7;
                }
                int maxDay = cal.getActualMaximum(Calendar.DAY_OF_MONTH);
                int currTime = converTime(cal.get(Calendar.HOUR_OF_DAY) + ":" + cal.get(Calendar.MINUTE));
                try {
                    TaskQuery params = new TaskQuery();
                    params.setStatus(ExcuteStatus.WAIT_RUN.getValue());
                    List<TaskEntity> taskList = find(params, null);
                    if (taskList != null && taskList.size() > 0) {
                        final Date currDateTime = cal.getTime();
                        String currDateTimeStr = DateUtils.getDateTime(currDateTime, "yyyyMMddHHmm");
                        for (TaskEntity task : taskList) {
                            try {
                                String allowExcuteHost = task.getExcuteHost();
                                if (StringUtils.isNotEmpty(allowExcuteHost)
                                        && ("," + allowExcuteHost + ",").indexOf("," + localIp + ",") == -1) {
                                    continue;
                                }
                                int strategy = task.getExcuteStrategy() == null ? -1 : task.getExcuteStrategy();
                                int excuteDate = task.getExcuteDate() == null ? -1 : task.getExcuteDate();
                                int interimExcuteStatus = task.getInterimExcuteStatus() == null ? -1
                                        : task.getInterimExcuteStatus();
                                int excuteTime = converTime(task.getExcuteTime());
                                Date lastExcuteTime = task.getLastExcuteTime();
                                String lastExcuteTimeStr = "";
                                if (lastExcuteTime != null) {
                                    lastExcuteTimeStr = DateUtils.getDateTime(lastExcuteTime, "yyyyMMddHHmm");
                                }
                                if (interimExcuteStatus != TaskInterimExcuteStatus.UNUSE.getValue() // 启用立即执行
                                        || ((strategy == TaskExcuteStrategy.DAY.getValue() // 按天
                                                || (strategy == TaskExcuteStrategy.WEEK.getValue()
                                                        && week == excuteDate)
                                // 按周
                                                || (strategy == TaskExcuteStrategy.MONTH.getValue()
                                                        && (day == excuteDate
                                                                || (excuteDate > maxDay && day == maxDay))))// 按月
                                                && (excuteTime == currTime
                                                        && !lastExcuteTimeStr.equals(currDateTimeStr)) // 执行间隔最少为一天
                                ) || (strategy == TaskExcuteStrategy.INTERVAL.getValue() // 按间隔时间
                                        && (lastExcuteTime == null || lastExcuteTime.getTime()
                                                + excuteDate * 1000 <= currDateTime.getTime()))) {
                                    final boolean printLog = checkPrintLog(task);
                                    Object excuteBean = GlobalSysInfo.getBeanByName(task.getExcuteService());
                                    if (excuteBean == null || !(excuteBean instanceof ITaskExcuteService)) {
                                        if (printLog) {
                                            log.warn("未获取到服务执行组件，任务执行失败!-->" + task);
                                        }
                                        continue;
                                    }
                                    final ITaskExcuteService excuteService = (ITaskExcuteService) excuteBean;
                                    task.setLastExcuteHost(localIp);
                                    if (taskService.doForceUpdateTaskLock(task)) {
                                        task.setStatus(ExcuteStatus.RUNNING.getValue());
                                        final TaskEntity excuteTask = task;
                                        executorService.execute(new Runnable() {
                                            @Override
                                            public void run() {
                                                try {
                                                    if (printLog) {
                                                        log.info("开始执行任务...-->" + excuteTask);
                                                    }
                                                    long startTime = System.currentTimeMillis();
                                                    excuteService.excuteTask(excuteTask);
                                                    // 执行成功，更新执行时间
                                                    excuteTask.setLastExcuteTime(currDateTime);
                                                    if (printLog) {
                                                        log.info("任务执行完成，用时："
                                                                + ((System.currentTimeMillis() - startTime) / 1000) + "秒-->"
                                                                + excuteTask);
                                                    }
                                                } catch (Throwable e) {
                                                    if (printLog) {
                                                        log.error("执行任务失败-->" + excuteTask, e);
                                                    }
                                                    if (strategy == TaskExcuteStrategy.INTERVAL.getValue()) {
                                                     // 按间隔时间,更新执行时间，避免一直重复执行
                                                        excuteTask.setLastExcuteTime(currDateTime);
                                                    }
                                                } finally {
                                                    try {
                                                        taskService.doForceUpdateTaskUnLock(excuteTask);
                                                        isUnlockSuccess = true;
                                                        prevTask = null;
                                                    } catch (Throwable e) {
                                                        isUnlockSuccess = false;
                                                        prevTask = excuteTask;
                                                        if (printLog) {
                                                            log.error("释放任务锁失败-->" + excuteTask, e);
                                                        }
                                                    }
                                                }
                                            }
                                        });
                                    }
                                }
                            } catch (Throwable e) {
                                log.error("执行任务失败-->" + task, e);
                            }
                        }
                    }
                } catch (Throwable e) {
                    log.error("获取任务信息失败", e);
                }
                try {
                    Thread.sleep(sleepTime);
                } catch (Throwable e) {
                }
            }
            executorService.shutdown();
            log.info("任务执行线程退出!");
        });
        thread.start();
    }

    @Override
    public void stop() throws AppException {
        this.isStop = true;
        if (thread != null) {
            thread.interrupt();
        }
    }

}