/*
 * Queue - A Queueing system that can be used to handle labs in higher education
 * Copyright (C) 2016-2021  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 nl.tudelft.labracore.api.AssignmentControllerApi;
import nl.tudelft.labracore.api.StudentGroupControllerApi;
import nl.tudelft.labracore.api.dto.*;
import nl.tudelft.queue.cache.AssignmentCacheManager;
import nl.tudelft.queue.cache.EditionCacheManager;
import nl.tudelft.queue.cache.ModuleCacheManager;
import nl.tudelft.queue.dto.create.QueueAssignmentCreateDTO;

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.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class AssignmentController {

	@Autowired
	private AssignmentCacheManager aCache;

	@Autowired
	private ModuleCacheManager mCache;

	@Autowired
	private EditionCacheManager eCache;

	@Autowired
	private AssignmentControllerApi aApi;

	@Autowired
	private StudentGroupControllerApi sgApi;

	/**
	 * Gets the page for creating an assignment. This is a basic page with only the most basic of properties
	 * for an assignment. Further adjustments or creation options should be made available in Portal or later
	 * in Queue.
	 *
	 * @param  moduleId The id of the module to add the assignment to.
	 * @param  model    The model to fill out for Thymeleaf template resolution.
	 * @return          The Thymeleaf template to resolve.
	 */
	@GetMapping("/module/{moduleId}/assignment/create")
	@PreAuthorize("@permissionService.canManageModule(#moduleId)")
	public String getAssignmentCreatePage(@PathVariable Long moduleId,
			Model model) {
		return addCreateAssignmentAttributes(moduleId, new QueueAssignmentCreateDTO(moduleId), model);
	}

	/**
	 * Handles a Post request to add a new assignment. The assignment is passed through a
	 * {@link QueueAssignmentCreateDTO}. After adding the assignment successfully, the user is redirected back
	 * to the edition/modules page. If something goes wrong during conversion, the user is instead directed
	 * back to the assignment create page.
	 *
	 * @param  mId   The id of the module to add the assignment to.
	 * @param  dto   The dto containing information for creating the assignment.
	 * @param  model The model to fill out for Thymeleaf template resolution.
	 * @return       A redirect back to the edition/modules page.
	 */
	@PostMapping("/module/{mId}/assignment/create")
	@PreAuthorize("@permissionService.canManageModule(#mId)")
	public String createAssignment(@PathVariable Long mId,
			QueueAssignmentCreateDTO dto,
			Model model) {

		ModuleDetailsDTO module = mCache.getRequired(mId);

		dto.setModuleId(mId);

		AssignmentCreateDTO create = dto.apply();
		if (dto.hasErrors()) {
			return addCreateAssignmentAttributes(mId, dto, model);
		}

		aApi.addAssignment(create).block();

		return "redirect:/edition/" + module.getEdition().getId() + "/modules";
	}

	/**
	 * Gets the assignment removal page. This page is simply to confirm whether the user really wants to
	 * delete the assignment with the given id.
	 *
	 * @param  assignmentId The id of the assignment that the user might want to delete.
	 * @param  model        The model to fill out for Thymeleaf template resolution.
	 * @return              The Thymeleaf template to resolve.
	 */
	@GetMapping("/assignment/{assignmentId}/remove")
	@PreAuthorize("@permissionService.canManageAssignment(#assignmentId)")
	public String getAssignmentRemovePage(@PathVariable Long assignmentId,
			Model model) {
		var assignment = aCache.getRequired(assignmentId);
		var module = mCache.getRequired(assignment.getModule().getId());
		var edition = eCache.getRequired(module.getEdition().getId());

		model.addAttribute("edition", edition);
		model.addAttribute("assignments",
				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
						.stream()
						.flatMap(m -> m.getAssignments().stream()).toList());
		model.addAttribute("assignment", assignment);

		return "assignment/remove";
	}

	/**
	 * Deletes the assignment with the given id and redirects back to the modules page for the relevant
	 * edition.
	 *
	 * @param  assignmentId The id of the assignment to delete.
	 * @return              A redirect back to the edition/modules page.
	 */
	@PostMapping("/assignment/{assignmentId}/remove")
	@PreAuthorize("@permissionService.canManageAssignment(#assignmentId)")
	public String removeAssignment(@PathVariable Long assignmentId) {
		var assignment = aCache.getRequired(assignmentId);
		var module = mCache.getRequired(assignment.getModule().getId());

		aApi.deleteAssignment(assignmentId).block();

		return "redirect:/edition/" + module.getEdition().getId() + "/modules";
	}

	/**
	 * Get the page with a list of groups to join for an assignment.
	 *
	 * @param  assignmentId The id of the assignment
	 * @return              The group page
	 */
	@GetMapping("/assignment/{assignmentId}/groups")
	public String getAssignmentGroups(@PathVariable Long assignmentId, Model model) {
		AssignmentDetailsDTO assignment = aCache.getRequired(assignmentId);
		ModuleDetailsDTO module = mCache.getRequired(assignment.getModule().getId());

		model.addAttribute("module", module);
		model.addAttribute("edition", eCache.getRequired(module.getEdition().getId()));
		model.addAttribute("groups", sgApi.getAllGroupsInModule(module.getId()).collectList().block());

		return "module/groups";
	}

	/**
	 * Adds attributes for the create assignment page to the given model.
	 *
	 * @param  moduleId The id of the module the assignment will be a part of.
	 * @param  dto      The DTO to add to the model for the page form to fill out.
	 * @param  model    The model to fill with attributes for the create-assignment page.
	 * @return          The template to load for the create assignments page.
	 */
	private String addCreateAssignmentAttributes(Long moduleId, QueueAssignmentCreateDTO dto,
			Model model) {
		ModuleDetailsDTO module = mCache.getRequired(moduleId);
		EditionDetailsDTO edition = eCache.getRequired(module.getEdition().getId());

		model.addAttribute("edition", edition);
		model.addAttribute("assignments",
				mCache.getAndIgnoreMissing(edition.getModules().stream().map(ModuleSummaryDTO::getId))
						.stream()
						.flatMap(m -> m.getAssignments().stream()).toList());
		model.addAttribute("_module", module);

		model.addAttribute("dto", dto);

		return "assignment/create";
	}

}
