package com.uas.search.schedule;

import com.uas.search.annotation.NotEmpty;
import com.uas.search.util.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.uas.search.util.CollectionUtils;
import com.uas.search.util.StringUtils;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 管理定时任务
 * 
 * @author sunyj
 * @since 2016年12月19日 上午10:54:43
 */
@Service
public class TaskServiceImpl implements TaskService {

	/**
	 * 存储定时任务信息
	 */
	private List<TaskInformation> taskInformations = new ArrayList<>();

	/**
	 * 定时任务调度
	 */
	private ScheduledExecutorService scheduledExecutorService;

	/**
	 * 任务执行日志文件的路径
	 */
	private static final String LOG_DIR = System.getProperty("java.io.tmpdir");

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

	@Override
	public void newTask(TaskInformation taskInformation) {
		if (taskInformation == null || StringUtils.isEmpty(taskInformation.getTitle())
				|| taskInformation.getCommand() == null) {
			throw new IllegalArgumentException("定时任务的 title 和 command 不可为空");
		}
		if (contain(taskInformation)) {
			throw new IllegalStateException("任务已存在：" + taskInformation);
		}
		taskInformations.add(taskInformation);
	}

	@Override
	public void remove(@NotEmpty("code") String code) {
		for (TaskInformation d : taskInformations) {
			if (Objects.equals(code, d.getCode())) {
				taskInformations.remove(d);
				return;
			}
		}
		throw new IllegalArgumentException("定时任务不存在：" + code);
	}

	@Override
	public boolean exist(@NotEmpty("code") String code) {
		for (TaskInformation d : taskInformations) {
			if (Objects.equals(code, d.getCode())) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 判断定时任务是否已经存在
	 * 
	 * @param taskInformation
	 * @return
	 */
	private boolean contain(TaskInformation taskInformation) {
		if (CollectionUtils.isEmpty(taskInformations)) {
			return false;
		}
		for (TaskInformation d : taskInformations) {
			if (taskInformation.equals(d)) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 保存日志到本地文件
	 * 
	 * @param log
	 */
	private void saveLog(TaskLog log) {
		if (log == null) {
			throw new NullPointerException();
		}
		FileWriter fileWriter = null;
		try {
			String logFilePath = (LOG_DIR.endsWith(File.separator) ? LOG_DIR : LOG_DIR + "/") + "mall-search-mysql-task-log.log";
			fileWriter = new FileWriter(logFilePath, true);
			fileWriter.write(log.toJSONString() + "\n");
			fileWriter.flush();
		} catch (IOException e) {
			logger.error("", e);
		} finally {
			if (fileWriter != null) {
				try {
					fileWriter.close();
				} catch (IOException e) {
					logger.error("", e);
				}
			}
		}
	}

	@Override
	public List<TaskInformation> allTaskInformations() {
		return taskInformations;
	}

	@Override
	public String start() {
		String message = "";
		if (!isStopped()) {
			message = "已存在运行的定时任务";
			logger.error(message);
			return message;
		}
		if (!CollectionUtils.isEmpty(taskInformations)) {
			// 线程数为1，以免多个创建索引的任务互相影响，导致索引创建失败
			scheduledExecutorService = Executors.newScheduledThreadPool(1);
			for (TaskInformation taskInformation : taskInformations) {
				logger.info("New task: " + taskInformation);
				switch (taskInformation.getScheduleType()){
                    case FixedRate:
                        scheduledExecutorService.scheduleAtFixedRate(getCommand(taskInformation),
                                taskInformation.getInitialDelay(), taskInformation.getInterval(), TimeUnit.MILLISECONDS);
                        break;
                    case FixedDelay:
                        scheduledExecutorService.scheduleWithFixedDelay(getCommand(taskInformation),
                                taskInformation.getInitialDelay(), taskInformation.getInterval(), TimeUnit.MILLISECONDS);
                        break;
                }
			}
			message = "已开启定时任务：" + taskInformations;
			logger.info(message + "\n");
			return message;
		} else {
			message = "定时任务为空";
			logger.error(message + "\n");
			return message;
		}
	}

	/**
	 * 获取任务需执行的命令
	 * 
	 * @param taskInformation
	 * @return
	 */
	private Runnable getCommand(final TaskInformation taskInformation) {
		return new Runnable() {
			@Override
			public void run() {
				try {
					Executable command = taskInformation.getCommand();
					String result = command.execute();
					saveLog(new TaskLog(taskInformation, new Date(), result));
				} catch (Throwable e) {
					saveLog(new TaskLog(taskInformation, new Date(),
							"定时任务出错" + ExceptionUtils.getDetailedMessage(e)));
				}
			}
		};
	}

	@Override
	public String stop() {
		String message = "";
		if (isStopped()) {
			message = "定时任务已经停止或者未开启过";
			logger.error(message);
			return message;
		}
		logger.info("Remove old tasks...");
		scheduledExecutorService.shutdownNow();
		message = "已关闭定时任务";
		logger.info(message + "\n");
		return message;
	}

	@Override
	public boolean isStopped() {
		if (scheduledExecutorService == null) {
			return true;
		}
		return scheduledExecutorService.isShutdown() || scheduledExecutorService.isTerminated();
	}
}
