/*
 * 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.util.List;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.web.PageableDefault;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

import jakarta.transaction.Transactional;
import nl.tudelft.labracore.api.PersonControllerApi;
import nl.tudelft.labracore.api.StudentGroupControllerApi;
import nl.tudelft.labracore.api.dto.EditionDetailsDTO;
import nl.tudelft.labracore.api.dto.RoleEditionDetailsDTO;
import nl.tudelft.labracore.api.dto.SessionSummaryDTO;
import nl.tudelft.labracore.api.dto.StudentGroupSummaryDTO;
import nl.tudelft.labracore.lib.security.user.AuthenticatedPerson;
import nl.tudelft.labracore.lib.security.user.Person;
import nl.tudelft.queue.cache.EditionCacheManager;
import nl.tudelft.queue.cache.EditionRolesCacheManager;
import nl.tudelft.queue.cache.PersonCacheManager;
import nl.tudelft.queue.cache.SessionCacheManager;
import nl.tudelft.queue.repository.LabRequestRepository;
import nl.tudelft.queue.repository.QueueSessionRepository;
import nl.tudelft.queue.service.LabService;
import nl.tudelft.queue.service.RequestTableService;
import nl.tudelft.queue.service.SessionService;

@Controller
public class HistoryController {

	@Autowired
	private EditionCacheManager eCache;

	@Autowired
	private EditionRolesCacheManager erCache;

	@Autowired
	private PersonCacheManager pCache;

	@Autowired
	private SessionCacheManager sCache;

	@Autowired
	private StudentGroupControllerApi sgApi;

	@Autowired
	private PersonControllerApi pApi;

	@Autowired
	private QueueSessionRepository qsr;

	@Autowired
	private LabRequestRepository rr;

	@Autowired
	private RequestTableService rts;

	@Autowired
	private SessionService sessionService;

	@Autowired
	private LabService ls;

	/**
	 * Gets the view representing the request history of the currently authenticated student.
	 *
	 * @param  user     The currently authenticated user.
	 * @param  model    The model to fill out for Thymeleaf template resolution.
	 * @param  pageable The pageable dictating what the page of student request should contain.
	 * @return          The Thymeleaf template to resolve.
	 */
	@GetMapping("/history")
	public String getHistoryView(@AuthenticatedPerson Person user, Model model,
			@PageableDefault(sort = "createdAt", direction = Sort.Direction.DESC, size = 25) Pageable pageable) {
		List<Long> groups = sgApi.getGroupsForPerson(user.getId())
				.map(StudentGroupSummaryDTO::getId)
				.collectList().block();

		setHistoryModel("/history", model, user.getId(), groups, pageable);

		return "history/index";
	}

	/**
	 * Gets the view representing the request history of a student.
	 *
	 * @param  model     The model to fill out for Thymeleaf template resolution.
	 * @param  editionId The id of the edition to view history of.
	 * @param  studentId The id of the student to view history of.
	 * @param  pageable  The pageable dictating what the page of student request should contain.
	 * @return           The Thymeleaf template to resolve.
	 */
	@GetMapping("/history/edition/{editionId}/student/{studentId}")
	@PreAuthorize("@permissionService.isAdminOrTeacher()")
	public String getStudentHistoryView(Model model,
			@PathVariable Long editionId,
			@PathVariable Long studentId,
			@PageableDefault(sort = "createdAt", direction = Sort.Direction.DESC, size = 25) Pageable pageable) {
		var edition = eCache.getRequired(editionId);

		List<Long> groups = sgApi.getGroupsForPersonAndCourse(studentId, edition.getCourse().getId())
				.map(StudentGroupSummaryDTO::getId)
				.collectList().block();

		model.addAttribute("edition", edition);
		model.addAttribute("student", pCache.getRequired(studentId));

		setHistoryModel("/history/edition/" + editionId + "/student/" + studentId,
				model, studentId, groups, pageable);

		return "history/student";
	}

	/**
	 * Sets the model for history pages. History pages are filled with information similar to the request
	 * table view. This method fills in the model attributes for this request table view and stores the filter
	 * for this page.
	 *
	 * @param key      The key under which to store the filter of the page.
	 * @param model    The model to fill out for Thymeleaf resolution.
	 * @param groupIds The ids of the groups to find requests for.
	 * @param pageable The pageable containing the information on the page to show.
	 */
	@Transactional
	public void setHistoryModel(String key, Model model, Long studentId, List<Long> groupIds,
			Pageable pageable) {
		var editions = eCache.getAndIgnoreMissing(pApi.getRolesForPerson(studentId)
				.filter(r -> r.getType() == RoleEditionDetailsDTO.TypeEnum.STUDENT)
				.map(r -> r.getEdition().getId())
				.toStream());

		erCache.getAndIgnoreMissing(editions.stream().map(EditionDetailsDTO::getId));

		var sessionIds = editions.stream()
				.flatMap(e -> e.getSessions().stream().map(SessionSummaryDTO::getId))
				.collect(Collectors.toList());

		sessionService.getCoreSessions(sessionIds);

		var labs = qsr.findAllBySessions(sessionIds);

		var filter = rts.checkAndStoreFilterDTO(null, key);
		rts.addFilterAttributes(model, labs);

		model.addAttribute("filter", filter);
		model.addAttribute("requests",
				rts.convertRequestsToView(rr.findAllPreviousRequests(groupIds, filter, pageable),
						rr.countPreviousRequests(groupIds, filter)));
	}

}
