package ru.yandex.webmaster3.worker.http;

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import ru.yandex.webmaster3.core.WebmasterException;
import ru.yandex.webmaster3.core.http.Action;
import ru.yandex.webmaster3.core.http.ActionRequest;
import ru.yandex.webmaster3.core.http.ActionResponse;
import ru.yandex.webmaster3.core.worker.task.PeriodicTaskType;
import ru.yandex.webmaster3.core.worker.task.TaskResult;
import ru.yandex.webmaster3.core.worker.task.WorkerTaskType;
import ru.yandex.webmaster3.worker.SimpleZkScheduler;
import ru.yandex.webmaster3.worker.TaskRegistry;
import ru.yandex.webmaster3.worker.queue.TaskQueueMetrics;
import ru.yandex.webmaster3.worker.queue.TaskScheduler;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author aherman
 */
public class GetTaskRunStatisticsAction extends Action<GetTaskRunStatisticsAction.Request, GetTaskRunStatisticsAction.Response> {
    private TaskQueueMetrics taskQueueMetrics;
    private TaskScheduler taskScheduler;
    private SimpleZkScheduler simpleZkScheduler;

    @Autowired
    private TaskRegistry taskRegistry;

    @Override
    public Response process(Request request) throws WebmasterException {
        List<TaskQueueMetrics.TaskStatistics> taskStatistics = taskQueueMetrics.getTaskStatistics();
        taskStatistics.sort(Comparator.comparing(o -> o.getTaskType().name()));

        Map<WorkerTaskType, Float> taskPriorities = taskStatistics.stream()
                .filter(s -> !taskScheduler.getPausedTasks().contains(s.getTaskType()))
                .filter(s -> s.getEnqueueed() > 0)
                .collect(Collectors.toMap(s -> s.getTaskType(), s -> taskRegistry.getTaskRegistryMap().get(s.getTaskType()).getPriority(s)));

        List<TaskQueueMetrics.WorkerStatistics> workerStatistics = taskQueueMetrics.getWorkerStatistics();
        workerStatistics.sort(Comparator.comparing(o -> o.getName()));

        List<PeriodicTaskState> periodicTaskStates = new ArrayList<>();
        taskRegistry.getPeriodicTaskRegistryMap().forEach((k,pt) -> {
                periodicTaskStates.add(new PeriodicTaskState(k, pt.isRunning(), pt.getSchedule().toString(), pt.getLastStarted(), pt.getLastFinished(), pt.getLastRunTimeMs(), pt.getLastResult()));
        });
        periodicTaskStates.sort(Comparator.comparing(pts -> pts.getTaskType().name()));

        return new Response(workerStatistics, taskStatistics, taskPriorities,
                taskScheduler.getPausedTasks(), periodicTaskStates, simpleZkScheduler.getPausedTasks());
    }

    @Required
    public void setTaskQueueMetrics(TaskQueueMetrics taskQueueMetrics) {
        this.taskQueueMetrics = taskQueueMetrics;
    }

    @Required
    public void setTaskScheduler(TaskScheduler taskScheduler) {
        this.taskScheduler = taskScheduler;
    }

    @Required
    public void setSimpleZkScheduler(SimpleZkScheduler simpleZkScheduler) {
        this.simpleZkScheduler = simpleZkScheduler;
    }

    private static class PeriodicTaskState {
        private final PeriodicTaskType taskType;
        private final boolean running;
        private final String schedule;
        private final String lastStarted;
        private final String lastFinished;
        private final long lastRunTimeMs;
        private final TaskResult lastResult;

        public PeriodicTaskState(PeriodicTaskType taskType, boolean running, String schedule, DateTime lastStarted, DateTime lastFinished, long lastRunTimeMs, TaskResult lastResult) {
            this.taskType = taskType;
            this.running = running;
            this.schedule = schedule;

            DateTimeFormatter dtf = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
            this.lastStarted = (lastStarted != null) ? dtf.print(lastStarted) : null;
            this.lastFinished = (lastFinished != null) ? dtf.print(lastFinished) : null;
            this.lastRunTimeMs = lastRunTimeMs;
            this.lastResult = lastResult;
        }

        public PeriodicTaskType getTaskType() {
            return taskType;
        }

        public boolean isRunning() {
            return running;
        }

        public String getSchedule() {
            return schedule;
        }

        public String getLastStarted() {
            return lastStarted;
        }

        public String getLastFinished() {
            return lastFinished;
        }

        public long getLastRunTimeMs() {
            return lastRunTimeMs;
        }

        public TaskResult getLastResult() {
            return lastResult;
        }
    }

    public static class Request implements ActionRequest {}

    public static class Response implements ActionResponse.NormalResponse {
        private final List<TaskQueueMetrics.WorkerStatistics> workerStatistics;
        private final List<TaskQueueMetrics.TaskStatistics> taskStatistics;
        private final Map<WorkerTaskType, Float> taskPriorities;
        private final Set<WorkerTaskType> pausedTasks;
        private final List<PeriodicTaskState> periodicTaskStates;
        private final Set<PeriodicTaskType> pausedPeriodicTasks;

        public Response(List<TaskQueueMetrics.WorkerStatistics> workerStatistics,
                        List<TaskQueueMetrics.TaskStatistics> taskStatistics,
                        Map<WorkerTaskType, Float> taskPriorities,
                        Set<WorkerTaskType> pausedTasks,
                        List<PeriodicTaskState> periodicTaskStates,
                        Set<PeriodicTaskType> pausedPeriodicTasks)
        {
            this.workerStatistics = workerStatistics;
            this.taskStatistics = taskStatistics;
            this.taskPriorities = taskPriorities;
            this.pausedTasks = pausedTasks;
            this.periodicTaskStates = periodicTaskStates;
            this.pausedPeriodicTasks = pausedPeriodicTasks;
        }

        public List<TaskQueueMetrics.WorkerStatistics> getWorkerStatistics() {
            return workerStatistics;
        }

        public List<TaskQueueMetrics.TaskStatistics> getTaskStatistics() {
            return taskStatistics;
        }

        public Map<WorkerTaskType, Float> getTaskPriorities() {
            return taskPriorities;
        }

        public Set<WorkerTaskType> getPausedTasks() {
            return pausedTasks;
        }

        public List<PeriodicTaskState> getPeriodicTaskStates() {
            return periodicTaskStates;
        }

        public Set<PeriodicTaskType> getPausedPeriodicTasks() {
            return pausedPeriodicTasks;
        }
    }
}
