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

import static nl.tudelft.librador.SpringContext.getBean;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
import nl.tudelft.labracore.api.dto.*;
import nl.tudelft.librador.dto.view.View;
import nl.tudelft.queue.cache.*;
import nl.tudelft.queue.csv.CsvAble;
import nl.tudelft.queue.model.QueueSession;
import nl.tudelft.queue.model.Request;
import nl.tudelft.queue.model.RequestEvent;

import com.beust.jcommander.Strings;

@Data
@SuperBuilder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public abstract class RequestViewDTO<R extends Request<?>> extends View<R>
		implements CsvAble {

	private Long id;

	private LocalDateTime createdAt;

	private RequestEventInfoViewDTO eventInfo;

	private RoomDetailsDTO room;

	private PersonSummaryDTO requester;
	private StudentGroupDetailsDTO studentGroup;

	private QueueSession<?> qSession;
	private SessionDetailsDTO session;

	private EditionDetailsDTO edition;

	private EditionCollectionDetailsDTO editionCollection;

	private List<RequestEventViewDTO<?>> events;

	@Override
	public void postApply() {
		qSession = data.getSession();

		room = getBean(RoomCacheManager.class).get(data.getRoom())
				.orElse(new RoomDetailsDTO().id(-1L).abbreviation("")
						.name("To be determined")
						.building(new BuildingSummaryDTO().name("TBD")));
		requester = getBean(PersonCacheManager.class).getRequired(data.getRequester());

		session = getBean(SessionCacheManager.class).getRequired(qSession.getSession());
		studentGroup = getBean(StudentGroupCacheManager.class).getRequired(data.getStudentGroup());

		if (session.getEdition() != null) {
			edition = getBean(EditionCacheManager.class).getRequired(session.getEdition().getId());
		} else if (!session.getEditions().isEmpty()) {
			editionCollection = getBean(EditionCollectionCacheManager.class)
					.getRequired(session.getEditionCollection().getId());
		}

		eventInfo.postApply();

		events = data.getEventInfo().getEvents().stream()
				.map(RequestEvent::viewDTO)
				.collect(Collectors.toList());
	}

	/**
	 * Gets the name of the student group that this request is from.
	 *
	 * @return The name of the group that the request was created for with description of its members.
	 */
	public String studentGroupName() {
		return studentGroup.getName() + " (" +
				Strings.join(", ", studentGroup.getMembers().stream()
						.filter(r -> r.getType() == RolePersonLayer1DTO.TypeEnum.STUDENT)
						.map(r -> r.getPerson().getDisplayName()).collect(Collectors.toList()))
				+
				")";
	}

	/**
	 * Converts the student group name or requester name into a string.
	 *
	 * @return The name of the requesting entity (either the group or the student).
	 */
	public String requesterEntityName() {
		if (studentGroup.getMembers().size() == 1) {
			return requester.getDisplayName();
		} else {
			return studentGroupName();
		}
	}

	/**
	 * @return The name of the organization that manages the session this Request is a part of. (This could
	 *         either be an EditionCollection name or an Edition name)
	 */
	public String organizationName() {
		return edition.getCourse().getCode() + " (" + edition.getName() + ")";
	}

	/**
	 * Converts the request into a sentence readable for humans.
	 *
	 * @return The human-readable sentence representing this request.
	 */
	public abstract String toSentence();

	public static String[] csvHeader() {
		return new String[] {
				"Id",
				"Created at",
				"Status",
				"Room",
				"Building",
				"Requester",
				"Student Group Members",
				"Session name",
				"Session start"
		};
	}

	@Override
	public String[] toColumns() {
		return new String[] {
				Objects.toString(id),
				createdAt.format(DateTimeFormatter.ISO_DATE_TIME),
				eventInfo.getStatus().getDisplayName(),
				(room != null) ? room.getName() : "",
				(room != null) ? room.getBuilding().getName() : "",
				requester.getDisplayName(),
				(studentGroup != null) ? String.join(", ", studentGroup.getMemberUsernames()) : "",
				session.getName(),
				session.getStart().format(DateTimeFormatter.ISO_DATE_TIME)
		};
	}

	/**
	 * @return The underlying request for any additional queries.
	 */
	public Request<?> getData() {
		return data;
	}
}
