package de.otto.edison.jobs.repository.cleanup;

import de.otto.edison.jobs.domain.JobInfo;
import de.otto.edison.jobs.repository.JobRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import static de.otto.edison.jobs.domain.JobInfo.JobStatus.SKIPPED;
import static java.util.Comparator.comparing;
import static java.util.Comparator.reverseOrder;
import static java.util.stream.Collectors.groupingBy;

/**
 * A JobCleanupStrategy that is removing all but the newest N skipped jobs.
 * <p>
 *
 * @author Peter Fouquet
 * @since 1.0.0
 */
public class DeleteSkippedJobs implements JobCleanupStrategy {

    private static final Logger LOG = LoggerFactory.getLogger(DeleteSkippedJobs.class);
    private static final long KEEP_LAST_JOBS_CLEANUP_INTERVAL = 10L * 60L * 1000L;

    private final int numberOfJobsToKeep;
    private JobRepository jobRepository;

    /**
     * @param jobRepository the JobRepository instance used to fetch job infos
     * @param numberOfJobsToKeep the number of jobs that are kept
     */
    public DeleteSkippedJobs(final JobRepository jobRepository, final int numberOfJobsToKeep) {
    	this.jobRepository = jobRepository;
        this.numberOfJobsToKeep = numberOfJobsToKeep;
        LOG.info("DeleteSkippedJobs strategy configured with numberOfJobsToKeep='{}'", numberOfJobsToKeep);
    }

    /**
     * Execute the cleanup of the given repository.
     */
    @Scheduled(fixedRate = KEEP_LAST_JOBS_CLEANUP_INTERVAL)
    public void doCleanUp() {
        final List<JobInfo> jobs = jobRepository.findAllJobInfoWithoutMessages();

        findJobsToDelete(jobs)
                .forEach(jobInfo -> jobRepository.removeIfStopped(jobInfo.getJobId()));
    }

    private List<JobInfo> findJobsToDelete(final List<JobInfo> jobs) {
        List<JobInfo> jobsToDelete = new ArrayList<>();
        jobs.stream()
                .sorted(comparing(JobInfo::getStarted, reverseOrder()))
                .collect(groupingBy(JobInfo::getJobType))
                .forEach((jobType, jobExecutions) -> {
                    jobExecutions.stream()
                            .filter(j -> j.isStopped() && Objects.equals(j.getStatus(), SKIPPED))
                            .skip(numberOfJobsToKeep)
                            .forEach(jobsToDelete::add);
                });
        return jobsToDelete;
    }
}
