/*
 * Queue - A Queueing system that can be used to handle labs in higher education
 * Copyright (C) 2016-2024  Delft University of Technology
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
package nl.tudelft.queue.service.dto;

import java.time.format.DateTimeFormatter;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import org.commonmark.node.Node;
import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;
import org.springframework.stereotype.Service;

import lombok.AllArgsConstructor;
import nl.tudelft.labracore.api.dto.EditionCollectionDetailsDTO;
import nl.tudelft.labracore.api.dto.SessionDetailsDTO;
import nl.tudelft.librador.dto.DTOConverter;
import nl.tudelft.queue.cache.EditionCollectionCacheManager;
import nl.tudelft.queue.cache.ModuleCacheManager;
import nl.tudelft.queue.cache.SessionCacheManager;
import nl.tudelft.queue.dto.view.QueueSessionSummaryDTO;
import nl.tudelft.queue.dto.view.QueueSessionViewDTO;
import nl.tudelft.queue.dto.view.labs.CapacitySessionViewDTO;
import nl.tudelft.queue.model.QueueSession;
import nl.tudelft.queue.model.embeddables.Slot;
import nl.tudelft.queue.model.labs.*;
import nl.tudelft.queue.service.LabStatusService;

@Service
@AllArgsConstructor
public class QueueSessionViewDTOService {

	private final EditionCollectionCacheManager editionCollectionCache;
	private final ModuleCacheManager moduleCache;
	private final SessionCacheManager sessionCache;

	private final LabStatusService labStatusService;

	private final Parser parser;
	private final HtmlRenderer renderer;

	private final DTOConverter converter;

	@SuppressWarnings("unchecked")
	public <S extends QueueSession<?>> QueueSessionViewDTO<?> convert(S session) {
		QueueSessionViewDTO<S> view = converter.convert(session,
				(Class<QueueSessionViewDTO<S>>) session.viewClass());

		view.setSession(sessionCache.getRequired(session.getSession()));
		view.setModules(new HashSet<>(moduleCache.getAndIgnoreMissing(session.getModules().stream())));
		view.setStatus(labStatusService.getStatus(session));

		Node document = parser.parse(session.getExtraInfo() == null ? "" : session.getExtraInfo());
		view.setExtraInfo(renderer.render(document));

		switch (view) {
			case CapacitySessionViewDTO capacitySessionView -> {
				capacitySessionView.setCapacity(view.getSession().getRooms()
						.stream().mapToInt(room -> Optional.ofNullable(room.getCapacity()).orElse(0))
						.sum());
			}
			default -> {
			}
		}

		return view;
	}

	public QueueSessionSummaryDTO convertToSummary(QueueSession<?> session) {
		QueueSessionSummaryDTO view = converter.convert(session, QueueSessionSummaryDTO.class);
		SessionDetailsDTO coreSession = sessionCache.getRequired(session.getSession());

		view.setName(coreSession.getName());
		view.setReadableName(coreSession.getName() + " : "
				+ DateTimeFormatter.ofPattern("HH:mm - dd MMM yyyy").format(coreSession.getStart()));
		view.setSlot(new Slot(coreSession.getStart(), coreSession.getEndTime()));
		view.setStatus(labStatusService.getStatus(session));

		view.setIsShared(coreSession.getEditionCollection() != null);
		if (view.getIsShared()) {
			EditionCollectionDetailsDTO editionCollection = editionCollectionCache
					.getRequired(coreSession.getEditionCollection().getId());
			view.setAssociatedEditions(editionCollection.getEditions());
		} else {
			view.setAssociatedEditions(List.of(Objects.requireNonNull(coreSession.getEdition())));
		}

		view.setIsActiveOrGracePeriod(view.getSlot().open());
		if (session instanceof Lab lab) {
			view.setIsActiveOrGracePeriod(view.getSlot().openWithGracePeriod(lab.getEolGracePeriod()));
		}

		return view;
	}

}
