package de.ozgcloud.alfa;

import static org.springframework.hateoas.server.mvc.WebMvcLinkBuilder.*;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.server.LinkBuilder;
import org.springframework.stereotype.Component;

import de.ozgcloud.alfa.common.ModelBuilder;
import de.ozgcloud.alfa.common.user.CurrentUserService;
import de.ozgcloud.alfa.common.user.UserId;
import de.ozgcloud.alfa.common.user.UserRole;
import de.ozgcloud.alfa.system.SystemStatusService;
import de.ozgcloud.alfa.vorgang.Vorgang.VorgangStatus;
import de.ozgcloud.alfa.vorgang.VorgangController;
import lombok.NonNull;

@Component
class RootViewLinkHandler {

	static final String VORGAENGE_ALL_REL_TEMPLATE = "vorgaenge_%s_all";
	static final String VORGAENGE_MY_REL_TEMPLATE = "vorgaenge_%s_my";
	static final String VORGAENGE_UNASSIGNED_REL_TEMPLATE = "vorgaenge_%s_unassigned";

	static final String ALL_VORGAENGE_REL = "vorgaenge_all";
	static final String MY_VORGEANGE_REL = "vorgaenge_my";
	static final String UNASSIGNED_VORGEANGE_REL = "vorgaenge_unassigned";

	static final String ALL_WIEDERVORLAGEN_REL = "vorgaenge_wiedervorlagen_all";
	static final String MY_WIEDERVORLAGEN_REL = "vorgaenge_wiedervorlagen_my";
	static final String UNASSIGNED_WIEDERVORLAGEN_REL = "vorgaenge_wiedervorlagen_unassigned";

	static final String SEARCH_ALL_REL = "search_all";
	static final String SEARCH_MY_VORGEANGE_REL = "search_my";
	static final String SEARCH_UNASSIGNED_VORGEANGE_REL = "search_unassigned";

	static final String ALL_UNGELESENE_NACHRICHTEN_REL = "vorgaenge_ungelesene_nachrichten_all";
	static final String MY_UNGELESENE_NACHRICHTEN_REL = "vorgaenge_ungelesene_nachrichten_my";
	static final String UNASSIGNED_UNGELESENE_NACHRICHTEN_REL = "vorgaenge_ungelesene_nachrichten_unassigned";

	static final int PAGE_SIZE = 100;

	@Autowired
	private CurrentUserService currentUserService;
	@Autowired
	private SystemStatusService systemStatusService;

	private final List<VorgangStatus> statusList = List.of(VorgangStatus.NEU, VorgangStatus.ANGENOMMEN, VorgangStatus.IN_BEARBEITUNG,
			VorgangStatus.BESCHIEDEN, VorgangStatus.ABGESCHLOSSEN, VorgangStatus.VERWORFEN, VorgangStatus.ZU_LOESCHEN);

	public void addViewLinks(ModelBuilder<Root> modelBuilder, Optional<UserId> userId) {
		modelBuilder.addLink(buildGetAllVorgaengeLink());
		addSearchAllVorgaengeLink(modelBuilder);

		if (currentUserService.hasRole(UserRole.VERWALTUNG_POSTSTELLE)) {
			addViewLinksForVerwaltungPoststelle(modelBuilder, userId);
		}
		if (currentUserService.hasRole(UserRole.VERWALTUNG_USER)) {
			addViewLinksForVerwaltungUser(modelBuilder, userId);
		}
	}

	private Link buildGetAllVorgaengeLink() {
		return linkTo(methodOn(VorgangController.class).getAll(0, PAGE_SIZE)).withRel(ALL_VORGAENGE_REL);
	}

	void addSearchAllVorgaengeLink(ModelBuilder<Root> modelBuilder) {
		if (systemStatusService.isSearchServerAvailable()) {
			modelBuilder.addLink(buildGetAllVorgaengeBySearchByLink());
		}
	}

	Link buildGetAllVorgaengeBySearchByLink() {
		return linkTo(methodOn(VorgangController.class).getAllBySearchBy(0, PAGE_SIZE, null)).withRel(SEARCH_ALL_REL);
	}

	void addViewLinksForVerwaltungPoststelle(ModelBuilder<Root> modelBuilder, Optional<UserId> userId) {
		userId.map(this::buildMyVorgaengeLink).ifPresent(modelBuilder::addLink);

		if (systemStatusService.isSearchServerAvailable()) {
			userId.map(this::buildSearchMyVorgaengeLink).ifPresent(modelBuilder::addLink);
		}
	}

	void addViewLinksForVerwaltungUser(ModelBuilder<Root> modelBuilder, Optional<UserId> userId) {
		addAllVorgangStatusLinks(modelBuilder);

		modelBuilder.addLink(buildGetAllUnassignedVorgaengeLink());
		modelBuilder.addLinks(buildUnassignedVorgaengeStatusLinks());

		userId.map(this::buildMyVorgaengeLink).ifPresent(modelBuilder::addLink);
		userId.map(this::buildMyVorgaengeStatusLinks).ifPresent(modelBuilder::addLinks);

		if (systemStatusService.isSearchServerAvailable()) {
			userId.map(this::buildSearchMyVorgaengeLink).ifPresent(modelBuilder::addLink);
			modelBuilder.addLink(buildSearchUnassignedVorgaengeLink());
		}

		modelBuilder.addLink(buildGetAllByHasNextWiedervorlageFristLink().withRel(ALL_WIEDERVORLAGEN_REL));
		modelBuilder
				.addLink(buildGetAllByAssignedToAndHasNextWiedervorlageFristLink(UserId.empty(), UNASSIGNED_WIEDERVORLAGEN_REL));
		userId.map(id -> buildGetAllByAssignedToAndHasNextWiedervorlageFristLink(id, MY_WIEDERVORLAGEN_REL)).ifPresent(modelBuilder::addLink);

		addGetByUngeleseneNachrichtenLinks(modelBuilder, userId);
	}

	Link buildGetAllUnassignedVorgaengeLink() {
		return buildGetAllVorgaengeByAssignedToLink(UserId.empty()).withRel(UNASSIGNED_VORGEANGE_REL);
	}

	Link buildMyVorgaengeLink(@NonNull UserId userId) {
		return buildGetAllVorgaengeByAssignedToLink(userId).withRel(MY_VORGEANGE_REL);
	}

	LinkBuilder buildGetAllVorgaengeByAssignedToLink(UserId userId) {
		return linkTo(methodOn(VorgangController.class).getAllByAssignedTo(0, PAGE_SIZE, userId));
	}

	Link buildSearchMyVorgaengeLink(@NonNull UserId userId) {
		return buildGetAllVorgaengeByAssignedToAndSearchByLink(userId).withRel(SEARCH_MY_VORGEANGE_REL);
	}

	Link buildSearchUnassignedVorgaengeLink() {
		return buildGetAllVorgaengeByAssignedToAndSearchByLink(UserId.empty()).withRel(SEARCH_UNASSIGNED_VORGEANGE_REL);
	}

	LinkBuilder buildGetAllVorgaengeByAssignedToAndSearchByLink(UserId userId) {
		return linkTo(methodOn(VorgangController.class).getAllByAssignedToAndSearchBy(0, PAGE_SIZE, userId, null));
	}

	void addAllVorgangStatusLinks(ModelBuilder<Root> modelBuilder) {
		addVorgangStatusLinks(modelBuilder, VORGAENGE_ALL_REL_TEMPLATE);
	}

	void addVorgangStatusLinks(ModelBuilder<Root> modelBuilder, String linkRelTemplate) {
		statusList.forEach(status -> modelBuilder.addLink(buildGetAllVorgaengeByStatus(status, linkRelTemplate)));
	}

	Link buildGetAllVorgaengeByStatus(VorgangStatus status, String linkRelTemplate) {
		var linkRel = formatLinkRelTemplate(linkRelTemplate, status);
		return linkTo(methodOn(VorgangController.class).getAllByStatus(0, PAGE_SIZE, status)).withRel(linkRel);
	}

	List<Link> buildMyVorgaengeStatusLinks(@NonNull UserId userId) {
		return buildVorgangStatusLinks(VORGAENGE_MY_REL_TEMPLATE, userId);
	}

	List<Link> buildUnassignedVorgaengeStatusLinks() {
		return buildVorgangStatusLinks(VORGAENGE_UNASSIGNED_REL_TEMPLATE, UserId.empty());
	}

	List<Link> buildVorgangStatusLinks(String linkRelTemplate, UserId userId) {
		return statusList.stream().map(status -> buildGetAllVorgaengeByAssignedToAndStatus(status, linkRelTemplate, userId)).toList();
	}

	Link buildGetAllVorgaengeByAssignedToAndStatus(VorgangStatus status, String linkRelTemplate, UserId userId) {
		var linkRel = formatLinkRelTemplate(linkRelTemplate, status);
		return linkTo(methodOn(VorgangController.class).getAllByAssignedToAndStatus(0, PAGE_SIZE, userId, status)).withRel(linkRel);
	}

	String formatLinkRelTemplate(String linkRelTempalte, VorgangStatus status) {
		return String.format(linkRelTempalte, status.toString().toLowerCase());
	}

	Link buildGetAllByHasNextWiedervorlageFristLink() {
		return linkTo(methodOn(VorgangController.class).getAllByHasNextWiedervorlageFrist(0, PAGE_SIZE,
				VorgangController.PARAM_NEXT_WIEDERVORLAGE_FRIST_EXISTS)).withRel(ALL_WIEDERVORLAGEN_REL);
	}

	Link buildGetAllByAssignedToAndHasNextWiedervorlageFristLink(@NonNull UserId userId, String linkRel) {
		return linkTo(methodOn(VorgangController.class).getAllByAssignedToAndHasNextWiedervorlageFrist(0, PAGE_SIZE, userId,
				VorgangController.PARAM_NEXT_WIEDERVORLAGE_FRIST_EXISTS)).withRel(linkRel);
	}

	void addGetByUngeleseneNachrichtenLinks(ModelBuilder<Root> modelBuilder, Optional<UserId> userId) {
		modelBuilder.addLink(buildGelAllByUngeleseneNachrichtenLink());
		modelBuilder.addLink(buildGetAllByAssignedToAndUngeleseneNachrichten(UserId.empty(), UNASSIGNED_UNGELESENE_NACHRICHTEN_REL));
		userId.map(id -> buildGetAllByAssignedToAndUngeleseneNachrichten(id, MY_UNGELESENE_NACHRICHTEN_REL)).ifPresent(modelBuilder::addLink);
	}

	Link buildGelAllByUngeleseneNachrichtenLink() {
		return linkTo(methodOn(VorgangController.class)
				.getAllByUngeleseneNachrichten(0, PAGE_SIZE, VorgangController.PARAM_NACHRICHTEN_UNGELESENE))
				.withRel(ALL_UNGELESENE_NACHRICHTEN_REL);
	}

	Link buildGetAllByAssignedToAndUngeleseneNachrichten(@NonNull UserId userId, String linkRel) {
		return linkTo(methodOn(VorgangController.class)
				.getAllByAssignedToAndUngeleseneNachrichten(0, PAGE_SIZE, userId, VorgangController.PARAM_NACHRICHTEN_UNGELESENE))
				.withRel(linkRel);
	}
}