package gov.va.vss.service.scheduledJobs;

import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import gov.va.shared.util.PersistenceUtil;
import gov.va.vss.model.printing.DeferredPrintResult;
import gov.va.vss.model.printing.PrintRequest;

@Service
public class PrintRequestDistributionJob extends AbstractScheduledJob {
	private static final Logger log = LoggerFactory.getLogger(PrintRequestDistributionJob.class);

	private final ConcurrentHashMap<Long, DeferredPrintResult> suspendedPrintJobsByKioskId = new ConcurrentHashMap<>();

	@Value("${scheduledJobs.purgePrintRequests.purgeRequestedOlderThanMinutes}")
	private int purgeRequestedOlderThanMinutes;
	@Value("${scheduledJobs.purgePrintRequests.purgeCompletedOlderThanMinutes}")
	private int purgeCompletedOlderThanMinutes;

	@Scheduled(fixedDelayString = "${scheduledJobs.kioskCheckin.fixedDelay}")
	public void checkinKiosks() {
		kioskService.kioskCheckin(suspendedPrintJobsByKioskId.keySet());
	}

	@Scheduled(fixedRateString = "${scheduledJobs.distributePrintRequests.fixedRate}")
	public void distributePrintRequests() {
		log.info("Distributing print requests...");
		if (suspendedPrintJobsByKioskId.isEmpty()) {
			log.debug("No parked print requests, exiting...");
			return;
		}

		List<PrintRequest> requests = printRequestDAO.findByCriteria(null, null, false, null);

		Map<Long, List<PrintRequest>> requestsByKiosk = new HashMap<>();
		for (PrintRequest r : requests) {
			long kioskId = r.getKiosk().getId();
			if (!suspendedPrintJobsByKioskId.containsKey(kioskId)) {
				log.debug("Print request found for kiosk ID {} but no parked request found, skipping...", kioskId);
				continue;
			}

			requestsByKiosk.computeIfAbsent(kioskId, k -> new ArrayList<>()).add(r);
		}

		for (Entry<Long, List<PrintRequest>> entry : requestsByKiosk.entrySet()) {
			try {
				long kioskId = entry.getKey();
				List<PrintRequest> kioskRequests = entry.getValue();

				DeferredPrintResult result = suspendedPrintJobsByKioskId.get(kioskId);
				if (result == null || result.isSetOrExpired()) {
					log.debug(
							"Print request found for kiosk ID {} but parked request not found or it was already set - skipping...",
							kioskId);
					continue;
				}

				List<String> texts = kioskRequests.stream().map(p -> p.getPrintText().replace("\r\n", "\n")).collect(Collectors.toList());
				result.setResult(texts);

				int numRecordsUpdated = printRequestService
						.bulkUpdate(PersistenceUtil.translateObjectsToIds(kioskRequests), ZonedDateTime.now());
				log.info("Updated {} print requests to completed", numRecordsUpdated);
			} catch (Exception e) {
				log.error("Error while distributing print requests", e);
			}
		}
	}
	
	@Scheduled(cron = "${scheduledJobs.purgePrintRequests.cron}")
	public void purgeOldPrintRequests() {
		printRequestService.bulkDeleteByCriteria(ZonedDateTime.now().minusMinutes(purgeRequestedOlderThanMinutes),
				ZonedDateTime.now().minusMinutes(purgeCompletedOlderThanMinutes));
	}

	public ConcurrentHashMap<Long, DeferredPrintResult> getSuspendedPrintJobsMap() {
		return suspendedPrintJobsByKioskId;
	}
}
