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

import static nl.tudelft.labracore.api.dto.RolePersonDetailsDTO.TypeEnum.*;

import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import nl.tudelft.labracore.api.RoleControllerApi;
import nl.tudelft.labracore.api.dto.*;
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.SessionCacheManager;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class RoleDTOService {
	@Autowired
	private EditionRolesCacheManager erCache;

	@Autowired
	private RoleControllerApi rApi;

	@Autowired
	private EditionCacheManager eCache;

	@Autowired
	private SessionCacheManager sessionCacheManager;

	public List<String> names(List<PersonSummaryDTO> people) {
		return people.stream().map(PersonSummaryDTO::getDisplayName).collect(Collectors.toList());
	}

	public List<String> roleNames(List<RolePersonDetailsDTO> roles,
			Set<RolePersonDetailsDTO.TypeEnum> types) {
		return roles.stream()
				.filter(role -> types.contains(role.getType()))
				.map(role -> Objects.requireNonNull(role.getPerson()).getDisplayName())
				.collect(Collectors.toList());
	}

	public List<String> roleNames(EditionDetailsDTO eDto, Set<RolePersonDetailsDTO.TypeEnum> types) {
		return roleNames(erCache.getRequired(eDto.getId()).getRoles(), types);
	}

	public List<PersonSummaryDTO> roles(List<RolePersonDetailsDTO> roles,
			Set<RolePersonDetailsDTO.TypeEnum> types) {
		return roles.stream()
				.filter(role -> types.contains(role.getType()))
				.map(RolePersonDetailsDTO::getPerson)
				.collect(Collectors.toList());
	}

	public List<PersonSummaryDTO> rolesL1(List<RolePersonLayer1DTO> roles,
			Set<RolePersonLayer1DTO.TypeEnum> types) {
		return roles.stream()
				.filter(role -> types.contains(role.getType()))
				.map(RolePersonLayer1DTO::getPerson)
				.collect(Collectors.toList());
	}

	public List<PersonSummaryDTO> roles(EditionDetailsDTO eDto,
			Set<RolePersonDetailsDTO.TypeEnum> types) {
		return roles(erCache.getRequired(eDto.getId()).getRoles(), types);
	}

	public List<PersonSummaryDTO> roles(EditionSummaryDTO eDto, Set<RolePersonDetailsDTO.TypeEnum> types) {
		return roles(eCache.getRequired(eDto.getId()), types);
	}

	/**
	 * Gets the display name of the role type.
	 *
	 * @param  type The type of a role as a string.
	 * @return      The user-friendly representation of the role type.
	 */
	public String typeDisplayName(String type) {
		switch (type) {
			case "STUDENT":
				return "Student";
			case "TA":
				return "TA";
			case "HEAD_TA":
				return "Head TA";
			case "TEACHER":
				return "Teacher";
			case "TEACHER_RO":
				return "Read-Only Teacher";
			case "ADMIN":
				return "Admin";
			case "BLOCKED":
				return "Blocked";
		}
		return "No role";
	}

	public List<String> assistantNames(EditionDetailsDTO eDto) {
		return roleNames(eDto, Set.of(TA));
	}

	public List<String> headTANames(EditionDetailsDTO eDto) {
		return roleNames(eDto, Set.of(HEAD_TA));
	}

	public List<String> teacherNames(EditionDetailsDTO eDto) {
		return roleNames(eDto, Set.of(TEACHER, TEACHER_RO));
	}

	public List<PersonSummaryDTO> students(EditionDetailsDTO eDto) {
		return roles(eDto, Set.of(STUDENT));
	}

	public List<PersonSummaryDTO> students(List<RolePersonDetailsDTO> roles) {
		return roles(roles, Set.of(STUDENT));
	}

	public List<PersonSummaryDTO> studentsL1(List<RolePersonLayer1DTO> roles) {
		return rolesL1(roles, Set.of(RolePersonLayer1DTO.TypeEnum.STUDENT));
	}

	public List<PersonSummaryDTO> assistants(EditionDetailsDTO eDto) {
		return roles(eDto, Set.of(TA));
	}

	public List<PersonSummaryDTO> staff(EditionSummaryDTO eDto) {
		return roles(eDto, Set.of(TA, HEAD_TA, TEACHER, TEACHER_RO));
	}

	public List<PersonSummaryDTO> headTAs(EditionDetailsDTO eDto) {
		return roles(eDto, Set.of(HEAD_TA));
	}

	public List<PersonSummaryDTO> teachers(EditionDetailsDTO eDto) {
		return roles(eDto, Set.of(TEACHER, TEACHER_RO));
	}

	public List<RoleDetailsDTO> rolesForPersonInEdition(EditionSummaryDTO eDto, Person person) {
		return rApi.getRolesById(Set.of(eDto.getId()), Set.of(person.getId())).collectList().block();
	}

	public boolean isStaff(RoleDetailsDTO role) {
		var type = role.getType();
		return type.equals(RoleDetailsDTO.TypeEnum.TA) || type.equals(RoleDetailsDTO.TypeEnum.HEAD_TA)
				|| type.equals(RoleDetailsDTO.TypeEnum.TEACHER)
				|| type.equals(RoleDetailsDTO.TypeEnum.TEACHER_RO);
	}
}
