/*
 * 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.controller;

import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import jakarta.validation.Valid;
import nl.tudelft.labracore.api.EditionCollectionControllerApi;
import nl.tudelft.labracore.api.RoleControllerApi;
import nl.tudelft.labracore.api.dto.*;
import nl.tudelft.librador.dto.DTOConverter;
import nl.tudelft.queue.cache.EditionCacheManager;
import nl.tudelft.queue.cache.EditionCollectionCacheManager;
import nl.tudelft.queue.dto.create.QueueEditionCollectionCreateDTO;
import nl.tudelft.queue.dto.view.QueueSessionSummaryDTO;
import nl.tudelft.queue.repository.LabRepository;
import nl.tudelft.queue.service.RoleDTOService;
import nl.tudelft.queue.service.dto.QueueSessionViewDTOService;

@Controller
@RequestMapping("/shared-edition")
public class SharedEditionController {

	@Autowired
	private EditionCollectionControllerApi editionCollectionControllerApi;

	@Autowired
	private EditionCollectionCacheManager editionCollectionCacheManager;

	@Autowired
	private EditionCacheManager editionCacheManager;

	@Autowired
	private RoleDTOService roleDTOService;

	@Autowired
	private QueueSessionViewDTOService queueSessionViewDTOService;

	@Autowired
	private LabRepository lr;

	@Autowired
	private RoleControllerApi rApi;

	@Autowired
	private DTOConverter converter;

	/**
	 * Get the page for a shared edition which includes all the details of the specific shared edition.
	 *
	 * @param  id    The id of the shared edition to be displayed.
	 * @param  model The model to fill out for Thymeleaf template resolution.
	 * @return       The Thymeleaf template to resolve.
	 */
	@GetMapping("/{id}")
	@PreAuthorize("@permissionService.isAuthenticated()")
	public String getSharedEdition(@PathVariable Long id, Model model) {
		var collection = editionCollectionCacheManager.getRequired(id);

		var editions = editionCacheManager.getAndIgnoreMissing(
				collection.getEditions().stream().map(EditionSummaryDTO::getId).toList());

		var teachers = editions.stream().map(edition -> roleDTOService.teachers(edition))
				.flatMap(List::stream).toList();

		var headTas = editions.stream().map(edition -> roleDTOService.headTAs(edition)).flatMap(List::stream)
				.toList();

		var assistants = editions.stream().map(edition -> roleDTOService.assistants(edition))
				.flatMap(List::stream).toList();

		var students = editions.stream().map(edition -> roleDTOService.students(edition))
				.flatMap(List::stream).toList();

		var allSessions = editions.stream().map(EditionDetailsDTO::getSessions).flatMap(List::stream)
				.toList();

		var now = LocalDateTime.now();
		var currentSessionsIds = allSessions.stream()
				.filter(s -> s.getStart().isBefore(now) && s.getEndTime().isAfter(now))
				.map(SessionSummaryDTO::getId).toList();
		var currentSessions = lr.findAllBySessions(currentSessionsIds).stream()
				.map(queueSessionViewDTOService::convertToSummary)
				.sorted(Comparator.comparing(l -> l.getSlot().getOpensAt()))
				.toList();
		var upComingSessionsIds = allSessions.stream().filter(s -> s.getStart().isAfter(now))
				.map(SessionSummaryDTO::getId).toList();
		var upComingSessions = lr.findAllBySessions(upComingSessionsIds).stream()
				.map(queueSessionViewDTOService::convertToSummary)
				.sorted(Comparator.comparing(l -> l.getSlot().getOpensAt()))
				.toList();
		var pastSessionsIds = allSessions.stream().filter(s -> s.getEndTime().isBefore(now))
				.map(SessionSummaryDTO::getId).toList();
		var pastSessions = lr.findAllBySessions(pastSessionsIds).stream()
				.map(queueSessionViewDTOService::convertToSummary)
				.sorted(Comparator.comparing((QueueSessionSummaryDTO l) -> l.getSlot().getOpensAt())
						.reversed())
				.toList();

		var editionTeachers = editions.stream().collect(Collectors.toMap(EditionDetailsDTO::getId,
				s -> roleDTOService.teacherNames(s)));

		var roles = rApi
				.getRolesByEditions(
						editions.stream().map(EditionDetailsDTO::getId).collect(Collectors.toSet()))
				.filter(r -> r.getType() != RolePersonDetailsDTO.TypeEnum.STUDENT)
				.collect(Collectors.groupingBy(RolePersonDetailsDTO::getPerson)).block();

		model.addAttribute("collection", collection);
		model.addAttribute("editions", editions);
		model.addAttribute("editionTeachers", editionTeachers);
		model.addAttribute("roles", roles);
		model.addAttribute("currentSessions", currentSessions);
		model.addAttribute("upComingSessions", upComingSessions);
		model.addAttribute("pastSessions", pastSessions);

		return "shared-edition/view";
	}

	/**
	 * Endpoint to create a new shared edition. Upon creation, the user will be redirected to view the shared
	 * edition.
	 *
	 * @param  sharedEdition The dto containing the shared edition to be created.
	 * @return               Redirect to the shared editions page.
	 */
	@PostMapping("/create")
	@PreAuthorize("@permissionService.isAdminOrTeacher()")
	public String createSharedEdition(
			@Valid QueueEditionCollectionCreateDTO sharedEdition) {
		var id = editionCollectionControllerApi.addEditionCollection(sharedEdition.apply(converter)).block();
		return "redirect:/shared-edition/" + id;
	}

}
