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

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

import nl.tudelft.labracore.api.*;
import nl.tudelft.labracore.api.dto.*;

import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Configuration;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import test.labracore.*;

@Configuration
public class DevLcDbLoader implements InitializingBean {
	@MockBean
	private DbLoaderControllerApi dbApi;

	@Autowired
	private AuthorizationControllerApi aApi;

	@Autowired
	private EditionControllerApi eApi;

	@Autowired
	private ModuleControllerApi mApi;

	@Autowired
	private RoomControllerApi rApi;

	@Autowired
	private RoleControllerApi rlApi;

	@Autowired
	private SessionControllerApi sApi;

	@Autowired
	private StudentGroupControllerApi sgApi;

	@Autowired
	private PersonControllerApi pApi;

	private static Long sgId = 1L;

	private final ModelMapper mapper = new ModelMapper();

	private final AssignmentSummaryDTO oop1920Assignment = new AssignmentSummaryDTO()
			.id(1L)
			.name("Assignment 1");

	private final AssignmentSummaryDTO oopNowAssignment = new AssignmentSummaryDTO()
			.id(2L)
			.name("Assignment 1");

	private final AssignmentSummaryDTO adsNowAssignment = new AssignmentSummaryDTO()
			.id(3L)
			.name("Assignment 1");

	private final AssignmentSummaryDTO adsNowAssignmentI = new AssignmentSummaryDTO()
			.id(4L)
			.name("Implementation 1");

	private final ModuleDetailsDTO oop1920ModuleA = new ModuleDetailsDTO()
			.id(1L)
			.name("Assignments")
			.addAssignmentsItem(oop1920Assignment);

	private final ModuleDetailsDTO oopNowModuleA = new ModuleDetailsDTO()
			.id(2L)
			.name("Assignments")
			.addAssignmentsItem(oopNowAssignment);

	private final ModuleDetailsDTO adsNowModuleA = new ModuleDetailsDTO()
			.id(3L)
			.name("Assignments")
			.addAssignmentsItem(adsNowAssignment);

	private final ModuleDetailsDTO adsNowModuleI = new ModuleDetailsDTO()
			.id(4L)
			.name("Implementation")
			.addAssignmentsItem(adsNowAssignmentI);

	private final EditionDetailsDTO oop1920 = new EditionDetailsDTO()
			.id(1L)
			.name("OOP 19/20")
			.startDate(LocalDateTime.now().withYear(2019))
			.endDate(LocalDateTime.now().withYear(2020))
			.isArchived(false)
			.enrollability(EditionDetailsDTO.EnrollabilityEnum.OPEN)
			.addModulesItem(mapper.map(oop1920ModuleA, ModuleSummaryDTO.class));

	private final EditionDetailsDTO oopNow = new EditionDetailsDTO()
			.id(2L)
			.name("OOP NOW")
			.startDate(LocalDateTime.now())
			.endDate(LocalDateTime.now().plusYears(1))
			.isArchived(false)
			.enrollability(EditionDetailsDTO.EnrollabilityEnum.OPEN)
			.addModulesItem(mapper.map(oopNowModuleA, ModuleSummaryDTO.class));

	private final EditionDetailsDTO adsNow = new EditionDetailsDTO()
			.id(3L)
			.name("ADS NOW")
			.startDate(LocalDateTime.now())
			.endDate(LocalDateTime.now().plusYears(1))
			.isArchived(false)
			.enrollability(EditionDetailsDTO.EnrollabilityEnum.OPEN)
			.addModulesItem(mapper.map(adsNowModuleA, ModuleSummaryDTO.class))
			.addModulesItem(mapper.map(adsNowModuleI, ModuleSummaryDTO.class));

	private final RoomDetailsDTO room1 = new RoomDetailsDTO()
			.id(1L)
			.name("Room 1")
			.capacity(32)
			.abbreviation("R1")
			.description("");

	private final PersonSummaryDTO csestudent1 = new PersonSummaryDTO()
			.id(1L)
			.username("csestudent1");

	private final PersonSummaryDTO csestudent5 = new PersonSummaryDTO()
			.id(3L)
			.username("csestudent5");

	private final PersonSummaryDTO csestudent6 = new PersonSummaryDTO()
			.id(4L)
			.username("csestudent6");

	private final PersonSummaryDTO csestudent7 = new PersonSummaryDTO()
			.id(5L)
			.username("csestudent7");

	private final PersonSummaryDTO csestudent8 = new PersonSummaryDTO()
			.id(6L)
			.username("csestudent8");

	private final PersonSummaryDTO cseteacher1 = new PersonSummaryDTO()
			.id(2L)
			.username("cseteacher1");

	private final RoleDetailsDTO teacher1InOop = new RoleDetailsDTO()
			.id(new Id().editionId(oopNow.getId()).personId(cseteacher1.getId()))
			.type(RoleDetailsDTO.TypeEnum.TEACHER)
			.person(cseteacher1);

	private final RoleDetailsDTO student1InOop = new RoleDetailsDTO()
			.id(new Id().editionId(oopNow.getId()).personId(csestudent1.getId()))
			.type(RoleDetailsDTO.TypeEnum.HEAD_TA)
			.person(csestudent1);

	private final RoleDetailsDTO student5InOop = new RoleDetailsDTO()
			.id(new Id().editionId(oopNow.getId()).personId(csestudent5.getId()))
			.type(RoleDetailsDTO.TypeEnum.STUDENT)
			.person(csestudent5);

	private final RoleDetailsDTO student6InOop = new RoleDetailsDTO()
			.id(new Id().editionId(oopNow.getId()).personId(csestudent6.getId()))
			.type(RoleDetailsDTO.TypeEnum.STUDENT)
			.person(csestudent6);

	private final RoleDetailsDTO student7InOop = new RoleDetailsDTO()
			.id(new Id().editionId(oopNow.getId()).personId(csestudent7.getId()))
			.type(RoleDetailsDTO.TypeEnum.STUDENT)
			.person(csestudent7);

	private final RoleDetailsDTO student8InOop = new RoleDetailsDTO()
			.id(new Id().editionId(oopNow.getId()).personId(csestudent8.getId()))
			.type(RoleDetailsDTO.TypeEnum.STUDENT)
			.person(csestudent8);

	private final RoleDetailsDTO student5InAds = new RoleDetailsDTO()
			.id(new Id().editionId(adsNow.getId()).personId(csestudent5.getId()))
			.type(RoleDetailsDTO.TypeEnum.STUDENT)
			.person(csestudent5);

	private final RoleDetailsDTO student6InAds = new RoleDetailsDTO()
			.id(new Id().editionId(adsNow.getId()).personId(csestudent6.getId()))
			.type(RoleDetailsDTO.TypeEnum.STUDENT)
			.person(csestudent6);

	private final RoleDetailsDTO student7InAds = new RoleDetailsDTO()
			.id(new Id().editionId(adsNow.getId()).personId(csestudent7.getId()))
			.type(RoleDetailsDTO.TypeEnum.STUDENT)
			.person(csestudent7);

	private final RoleDetailsDTO student8InAds = new RoleDetailsDTO()
			.id(new Id().editionId(adsNow.getId()).personId(csestudent8.getId()))
			.type(RoleDetailsDTO.TypeEnum.STUDENT)
			.person(csestudent8);

	@Override
	public void afterPropertiesSet() {
		EditionApiMocker eApiMocker = new EditionApiMocker(eApi);
		ModuleApiMocker mApiMocker = new ModuleApiMocker(mApi);
		RoleApiMocker rlApiMocker = new RoleApiMocker(aApi, pApi, eApi, rlApi);
		RoomApiMocker rApiMocker = new RoomApiMocker(rApi);
		StudentGroupApiMocker sgApiMocker = new StudentGroupApiMocker(sgApi);

		eApiMocker.save(oop1920);
		eApiMocker.save(oopNow);
		eApiMocker.save(adsNow);

		rApiMocker.save(room1);

		mApiMocker.save(oop1920ModuleA);
		mApiMocker.save(oopNowModuleA);
		mApiMocker.save(adsNowModuleA);
		mApiMocker.save(adsNowModuleI);

		rlApiMocker.save(teacher1InOop);
		rlApiMocker.save(student1InOop);
		rlApiMocker.save(student5InOop);
		rlApiMocker.save(student6InOop);
		rlApiMocker.save(student7InOop);
		rlApiMocker.save(student8InOop);
		rlApiMocker.save(student5InAds);
		rlApiMocker.save(student6InAds);
		rlApiMocker.save(student7InAds);
		rlApiMocker.save(student8InAds);

		sgApiMocker.save(createIndividualStudentGroup(student5InOop, oopNowModuleA));
		sgApiMocker.save(createIndividualStudentGroup(student6InOop, oopNowModuleA));
		sgApiMocker.save(createIndividualStudentGroup(student7InOop, oopNowModuleA));
		sgApiMocker.save(createIndividualStudentGroup(student8InOop, oopNowModuleA));
		sgApiMocker.save(createIndividualStudentGroup(student5InAds, adsNowModuleA));
		sgApiMocker.save(createIndividualStudentGroup(student6InAds, adsNowModuleA));
		sgApiMocker.save(createIndividualStudentGroup(student7InAds, adsNowModuleA));
		sgApiMocker.save(createIndividualStudentGroup(student8InAds, adsNowModuleA));
		sgApiMocker.save(createIndividualStudentGroup(student8InAds, adsNowModuleI));
		sgApiMocker.save(createIndividualStudentGroup(student8InAds, adsNowModuleI));
		sgApiMocker.save(createIndividualStudentGroup(student8InAds, adsNowModuleI));
		sgApiMocker.save(createIndividualStudentGroup(student8InAds, adsNowModuleI));

		eApiMocker.mock();
		rApiMocker.mock();
		mApiMocker.mock();
		rlApiMocker.mock();
		sgApiMocker.mock();

		when(dbApi.getAllPeopleInDB()).thenReturn(Flux.fromIterable(
				List.of(cseteacher1, csestudent1, csestudent5, csestudent6, csestudent7, csestudent8)));

		AtomicLong counter = new AtomicLong(1);
		when(sApi.addSingleSession(any())).thenAnswer(invocation -> Mono.just(counter.getAndIncrement()));
		when(sApi.addSharedSession(any())).thenAnswer(invocation -> Mono.just(counter.getAndIncrement()));
	}

	private StudentGroupDetailsDTO createIndividualStudentGroup(RoleDetailsDTO role,
			ModuleDetailsDTO module) {
		return new StudentGroupDetailsDTO()
				.name("Group #" + sgId)
				.id(sgId++)
				.addMembersItem(mapper.map(role, RolePersonLayer1DTO.class))
				.module(mapper.map(module, ModuleSummaryDTO.class));
	}
}
