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

import static nl.tudelft.labracore.api.dto.RoleDetailsDTO.TypeEnum.*;
import static nl.tudelft.labracore.api.dto.RoleDetailsDTO.TypeEnum.TEACHER;
import static org.assertj.core.api.AssertionsForClassTypes.*;

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

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Lazy;

import jakarta.transaction.Transactional;
import nl.tudelft.labracore.api.AuthorizationControllerApi;
import nl.tudelft.labracore.api.CourseControllerApi;
import nl.tudelft.labracore.api.dto.*;
import nl.tudelft.queue.cache.*;
import nl.tudelft.queue.model.LabRequest;
import nl.tudelft.queue.model.enums.CommunicationMethod;
import nl.tudelft.queue.model.enums.Language;
import nl.tudelft.queue.model.enums.RequestType;
import nl.tudelft.queue.model.labs.RegularLab;
import nl.tudelft.queue.repository.LabRepository;
import nl.tudelft.queue.repository.LabRequestRepository;
import nl.tudelft.queue.repository.QueueSessionRepository;
import nl.tudelft.queue.repository.RequestRepository;
import test.TestDatabaseLoader;
import test.labracore.ModuleApiMocker;
import test.labracore.PersonApiMocker;
import test.labracore.SessionApiMocker;
import test.labracore.StudentGroupApiMocker;
import test.test.TestQueueApplication;

@Transactional
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@SpringBootTest(classes = TestQueueApplication.class)
public class PermissionServiceTest {
	@Autowired
	private PermissionService ps;
	@Autowired
	private QueueSessionRepository qsr;

	@Autowired
	private LabRepository lr;

	@Autowired
	private RequestRepository rr;

	@Autowired
	private RequestService rs;

	@Autowired
	private LabRequestRepository lrr;

	@Autowired
	@Lazy
	private LabService ls;

	@Autowired
	private AssignmentCacheManager aCache;

	@Autowired
	private ModuleCacheManager mCache;

	@Autowired
	private EditionCacheManager eCache;

	@Autowired
	private EditionCollectionCacheManager ecCache;

	@Autowired
	private RoleCacheManager rCache;

	@Autowired
	private SessionCacheManager sCache;

	@Autowired
	private StudentGroupCacheManager sgCache;

	@Autowired
	private AuthorizationControllerApi aApi;

	@Autowired
	private CourseControllerApi cApi;

	private static final Set<RoleDetailsDTO.TypeEnum> STAFF_ROLES = Set.of(TA, HEAD_TA, TEACHER, TEACHER_RO);
	private static final Set<RoleDetailsDTO.TypeEnum> NON_MANAGER_ROLES = Set.of(STUDENT, TA);
	private static final Set<RoleDetailsDTO.TypeEnum> MANAGER_ROLES = Set.of(HEAD_TA, TEACHER);

	@Autowired
	private ModelMapper mapper;
	@Autowired
	private TestDatabaseLoader db;
	@Autowired
	private StudentGroupApiMocker sgApiMocker;
	@Autowired
	private PersonApiMocker pApiMocker;
	@Autowired
	private ModuleApiMocker mApiMocker;

	private LabRequest request;
	private StudentGroupDetailsDTO group1;

	private PersonSummaryDTO student1;
	private PersonSummaryDTO student2;
	private RegularLab lab1;
	@Autowired
	private SessionApiMocker sApiMocker;

	@BeforeEach
	void setUp() {
		sgApiMocker.mock();
		mApiMocker.mock();
		sApiMocker.mock();
		pApiMocker.mock();

		EditionDetailsDTO edition1 = db.getOopNow();
		AssignmentDetailsDTO assignment1 = db.getOopNowAssignments()[0];

		student1 = pApiMocker
				.save(new PersonSummaryDTO().id(1L).defaultRole(PersonSummaryDTO.DefaultRoleEnum.STUDENT));
		student2 = pApiMocker
				.save(new PersonSummaryDTO().id(2L).defaultRole(PersonSummaryDTO.DefaultRoleEnum.STUDENT));

		ModuleDetailsDTO module1 = mApiMocker.save(new ModuleDetailsDTO()
				.id(1L)
				.name("module1")
				.edition(mapper.map(edition1, EditionSummaryDTO.class)));

		group1 = sgApiMocker.save(new StudentGroupDetailsDTO()
				.id(12L)
				.members(List.of(student1.getId(), student2.getId()).stream()
						.map(id -> new RolePersonLayer1DTO().type(RolePersonLayer1DTO.TypeEnum.STUDENT)
								.person(new PersonSummaryDTO().id(id)))
						.collect(Collectors.toList()))
				.module(mapper.map(module1, ModuleSummaryDTO.class))
				.name("Group 1")
				.capacity(2));

		SessionDetailsDTO session1 = sApiMocker.save(new SessionDetailsDTO()
				.id(1L)
				.name("session1")
				.edition(mapper.map(edition1, EditionSummaryDTO.class)));

		request = new LabRequest(RequestType.QUESTION, "question for request", "room", null, Language.ANY,
				null, assignment1.getId(), 12L, null);
		lab1 = RegularLab.builder()
				.communicationMethod(CommunicationMethod.STUDENT_VISIT_TA)
				.session(session1.getId())
				.modules(Set.of(module1.getId()))
				.build();
		lab1.setRequests(List.of(request));
	}

	@Test
	void getOpenRequestForGroupOfPersonNoOpenRequest() {
		assertThat(rs.getOpenRequestForGroupOfPerson(student1.getId(), lab1)).isEmpty();

	}

	@Test
	void getOpenRequestForGroupOfPersonGroupHasRequest() {
		request.setStudentGroup(group1.getId());
		assertThat(rs.getOpenRequestForGroupOfPerson(student1.getId(), lab1)).isPresent()
				.isEqualTo(Optional.of(request));

	}

	@Test
	void canEnqueueSelfNow() {
		request.setStudentGroup(group1.getId());
		assertThat(rs.getOpenRequestForGroupOfPerson(student1.getId(), lab1)).isPresent()
				.isEqualTo(Optional.of(request));

	}

}
