package de.ozgcloud.alfa;

import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;

import java.util.Optional;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.EnumSource.Mode;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.LinkRelation;

import com.thedeanda.lorem.LoremIpsum;

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.UserProfileTestFactory;
import de.ozgcloud.alfa.common.user.UserRole;
import de.ozgcloud.alfa.system.SystemStatusService;
import de.ozgcloud.alfa.vorgang.Vorgang.VorgangStatus;

class RootViewLinkHandlerTest {

	@Spy
	@InjectMocks
	private RootViewLinkHandler viewLinkHandler;
	@Mock
	private CurrentUserService currentUserService;
	@Mock
	private SystemStatusService systemStatusService;

	@DisplayName("Add view links")
	@Nested
	class TestAddViewLinks {

		@Mock
		private Root rootResource;

		private Optional<UserId> emptyUserId = Optional.empty();

		@Test
		void shouldAddVorgaengeAllLink() {
			var modelBuilder = ModelBuilder.fromEntity(rootResource);
			viewLinkHandler.addViewLinks(modelBuilder, emptyUserId);

			assertThat(modelBuilder.buildModel().getLink(RootViewLinkHandler.ALL_VORGAENGE_REL))
					.get().extracting(Link::getHref).isEqualTo("/api/vorgangs?page=0&limit=100");
		}

		@Test
		void shouldCallAddSearchAllVorgaengeLink() {
			var modelBuilder = ModelBuilder.fromEntity(rootResource);
			viewLinkHandler.addViewLinks(modelBuilder, emptyUserId);

			verify(viewLinkHandler).addSearchAllVorgaengeLink(modelBuilder);
		}

		@Test
		void shouldAddLinksForVerwaltungPoststelle() {
			when(currentUserService.hasRole(UserRole.VERWALTUNG_POSTSTELLE)).thenReturn(true);

			var modelBuilder = ModelBuilder.fromEntity(rootResource);
			viewLinkHandler.addViewLinks(modelBuilder, emptyUserId);

			verify(viewLinkHandler).addViewLinksForVerwaltungPoststelle(modelBuilder, emptyUserId);
		}

		@Test
		void shouldAddLinksForVerwaltungUser() {
			when(currentUserService.hasRole(UserRole.VERWALTUNG_POSTSTELLE)).thenReturn(false);
			when(currentUserService.hasRole(UserRole.VERWALTUNG_USER)).thenReturn(true);

			var modelBuilder = ModelBuilder.fromEntity(rootResource);
			viewLinkHandler.addViewLinks(modelBuilder, emptyUserId);

			verify(viewLinkHandler).addViewLinksForVerwaltungUser(modelBuilder, emptyUserId);
		}

		@DisplayName("for " + UserRole.VERWALTUNG_POSTSTELLE)
		@Nested
		class TestVerwaltungPoststelle {

			private final ModelBuilder<Root> modelBuilder = ModelBuilder.fromEntity(rootResource);

			@Test
			void shouldCallBuildMyVorgaengeLink() {
				viewLinkHandler.addViewLinksForVerwaltungPoststelle(modelBuilder, Optional.of(UserProfileTestFactory.ID));

				verify(viewLinkHandler).buildMyVorgaengeLink(UserProfileTestFactory.ID);
			}

			@DisplayName("search my vorgaenge")
			@Nested
			class TestSearchMyVorgaenge {

				@DisplayName("on configured search server")
				@Nested
				class TestOnConfiguredSearchServer {

					@Test
					void shouldCallBuildSearchMyVorgaengeLink() {
						when(systemStatusService.isSearchServerAvailable()).thenReturn(true);

						viewLinkHandler.addViewLinksForVerwaltungPoststelle(modelBuilder, Optional.of(UserProfileTestFactory.ID));

						verify(viewLinkHandler).buildSearchMyVorgaengeLink(UserProfileTestFactory.ID);
					}
				}

				@DisplayName("on not configured search server")
				@Nested
				class TestOnNotConfiguredSearchServer {

					@Test
					void shouldNotCallBuildSearchMyVorgaengeLink() {
						when(systemStatusService.isSearchServerAvailable()).thenReturn(false);

						viewLinkHandler.addViewLinksForVerwaltungPoststelle(modelBuilder, Optional.of(UserProfileTestFactory.ID));

						verify(viewLinkHandler, never()).buildSearchMyVorgaengeLink(any());
					}
				}
			}
		}

		@DisplayName("for " + UserRole.VERWALTUNG_USER)
		@Nested
		class TestVerwaltungUser {

			private final ModelBuilder<Root> modelBuilder = ModelBuilder.fromEntity(rootResource);

			@Test
			void shouldCallAddUnassignedVorgaengeLink() {
				viewLinkHandler.addViewLinksForVerwaltungUser(modelBuilder, Optional.of(UserProfileTestFactory.ID));

				verify(viewLinkHandler).buildGetAllUnassignedVorgaengeLink();
			}

			@Test
			void shouldCallAddMyVorgaengeLink() {
				viewLinkHandler.addViewLinksForVerwaltungUser(modelBuilder, Optional.of(UserProfileTestFactory.ID));

				verify(viewLinkHandler).buildMyVorgaengeLink(UserProfileTestFactory.ID);
			}

			@DisplayName("search my vorgaenge")
			@Nested
			class TestSearchMy {

				@DisplayName("on configured search server")
				@Nested
				class TestOnConfiguredSearchServer {

					@Test
					void shouldCallBuildSearchMyVorgaengeLink() {
						when(systemStatusService.isSearchServerAvailable()).thenReturn(true);

						viewLinkHandler.addViewLinksForVerwaltungUser(modelBuilder, Optional.of(UserProfileTestFactory.ID));

						verify(viewLinkHandler).buildSearchMyVorgaengeLink(UserProfileTestFactory.ID);
					}
				}

				@DisplayName("on not configured search server")
				@Nested
				class TestOnNotConfiguredSearchServer {

					@Test
					void shouldNotCallBuildSearchMyVorgaengeLink() {
						when(systemStatusService.isSearchServerAvailable()).thenReturn(false);

						viewLinkHandler.addViewLinksForVerwaltungUser(modelBuilder, Optional.of(UserProfileTestFactory.ID));

						verify(viewLinkHandler, never()).buildSearchMyVorgaengeLink(any());
					}
				}
			}

			@Test
			void shouldCallAddVorgangStatusLinks() {
				viewLinkHandler.addViewLinksForVerwaltungUser(modelBuilder, Optional.of(UserProfileTestFactory.ID));

				verify(viewLinkHandler).addAllVorgangStatusLinks(modelBuilder);
			}

			@Test
			void shouldCallAddMyVorgaengeStatusLinks() {
				viewLinkHandler.addViewLinksForVerwaltungUser(modelBuilder, Optional.of(UserProfileTestFactory.ID));

				verify(viewLinkHandler).buildMyVorgaengeStatusLinks(UserProfileTestFactory.ID);
			}

			@Test
			void shouldCallAddUnassignedVorgaengeStatusLinks() {
				viewLinkHandler.addViewLinksForVerwaltungUser(modelBuilder, Optional.of(UserProfileTestFactory.ID));

				verify(viewLinkHandler).buildUnassignedVorgaengeStatusLinks();
			}

			@DisplayName("search unassigned vorgaenge")
			@Nested
			class TestSearchUnassignedVorgaengeLink {

				@DisplayName("with configured search server")
				@Nested
				class TestWithConfiguredSearchServer {

					@Test
					void shouldCallBuildSearchUnassignedVorgaengeLink() {
						when(systemStatusService.isSearchServerAvailable()).thenReturn(true);

						viewLinkHandler.addViewLinksForVerwaltungUser(modelBuilder, Optional.of(UserProfileTestFactory.ID));

						verify(viewLinkHandler).buildSearchUnassignedVorgaengeLink();
					}
				}

				@DisplayName("with not configured search server")
				@Nested
				class TestWithNotConfiguredSearchServer {

					@Test
					void shouldNotCallBuildSearchUnassignedVorgaengeLink() {
						when(systemStatusService.isSearchServerAvailable()).thenReturn(false);

						viewLinkHandler.addViewLinksForVerwaltungUser(modelBuilder, Optional.of(UserProfileTestFactory.ID));

						verify(viewLinkHandler, never()).buildSearchUnassignedVorgaengeLink();
					}
				}
			}

			@DisplayName("vorgaenge with open wiedervorlagen")
			@Nested
			class TestVorgaengeWithOpenWiedervorlagen {

				@Test
				void shouldAddAllLink() {
					var modelBuilder = ModelBuilder.fromEntity(rootResource);
					viewLinkHandler.addViewLinksForVerwaltungUser(modelBuilder, Optional.of(UserProfileTestFactory.ID));

					assertThat(modelBuilder.buildModel().getLink(RootViewLinkHandler.ALL_WIEDERVORLAGEN_REL))
							.get().extracting(Link::getHref).isEqualTo("/api/vorgangs?page=0&limit=100&nextFrist=exists");
				}

				@DisplayName("considering user")
				@Nested
				class TestConsideringUser {

					@Test
					void shouldAddAssignedLinkIfExists() {
						var modelBuilder = ModelBuilder.fromEntity(rootResource);
						viewLinkHandler.addViewLinksForVerwaltungUser(modelBuilder, Optional.of(UserProfileTestFactory.ID));

						assertThat(modelBuilder.buildModel().getLink(RootViewLinkHandler.MY_WIEDERVORLAGEN_REL))
								.get().extracting(Link::getHref)
								.isEqualTo("/api/vorgangs?page=0&limit=100&assignedTo=" + UserProfileTestFactory.ID + "&nextFrist=exists");
					}

					@Test
					void shoulNotAddAssignedLinkIfNotExists() {
						var modelBuilder = ModelBuilder.fromEntity(rootResource);
						viewLinkHandler.addViewLinksForVerwaltungUser(modelBuilder, Optional.empty());

						assertThat(modelBuilder.buildModel().getLink(RootViewLinkHandler.MY_WIEDERVORLAGEN_REL)).isNotPresent();
					}
				}

				@Test
				void shouldAddUnassignedLink() {
					var modelBuilder = ModelBuilder.fromEntity(rootResource);
					viewLinkHandler.addViewLinksForVerwaltungUser(modelBuilder, Optional.of(UserProfileTestFactory.ID));

					assertThat(modelBuilder.buildModel().getLink(RootViewLinkHandler.UNASSIGNED_WIEDERVORLAGEN_REL))
							.get().extracting(Link::getHref).isEqualTo("/api/vorgangs?page=0&limit=100&assignedTo=&nextFrist=exists");
				}
			}

			@Test
			void shouldAddLinksForUngeleseneNachrichten() {
				viewLinkHandler.addViewLinksForVerwaltungUser(modelBuilder, Optional.of(UserProfileTestFactory.ID));

				verify(viewLinkHandler).addGetByUngeleseneNachrichtenLinks(modelBuilder, Optional.of(UserProfileTestFactory.ID));
			}
		}

		@DisplayName("search all vorgaenge")
		@Nested
		class TestSearchAllVorgaenge {

			@Test
			void shouldBeAddedIfSearchServerIsAvailable() {
				when(systemStatusService.isSearchServerAvailable()).thenReturn(true);

				var modelBuilder = ModelBuilder.fromEntity(rootResource);
				viewLinkHandler.addSearchAllVorgaengeLink(modelBuilder);

				assertThat(modelBuilder.buildModel().getLink(RootViewLinkHandler.SEARCH_ALL_REL))
						.get().extracting(Link::getHref).isEqualTo("/api/vorgangs?page=0&limit=100&searchBy={searchBy}");
			}

			@Test
			void shouldNotBeAddedIfSearchServerIsUnavailable() {
				when(systemStatusService.isSearchServerAvailable()).thenReturn(false);

				var modelBuilder = spy(ModelBuilder.fromEntity(rootResource));
				viewLinkHandler.addSearchAllVorgaengeLink(modelBuilder);

				verify(modelBuilder, never()).addLink(any(Link.class));
			}
		}

		@DisplayName("my vorgaenge")
		@Nested
		class TestAddMyLink {

			@Test
			void shouldBeAddedIfUserIsPresent() {
				var link = viewLinkHandler.buildMyVorgaengeLink(UserProfileTestFactory.ID);

				assertThat(link.getHref()).isEqualTo("/api/vorgangs?page=0&limit=100&assignedTo=" + UserProfileTestFactory.ID);
			}
		}

		@DisplayName("search my vorgaenge")
		@Nested
		class TestSearchMyVorgaengeLink {

			@Test
			void shouldCallBuild() {
				viewLinkHandler.buildSearchMyVorgaengeLink(UserProfileTestFactory.ID);

				verify(viewLinkHandler).buildGetAllVorgaengeByAssignedToAndSearchByLink(UserProfileTestFactory.ID);
			}

			@Test
			void shouldBuildLinkWithMyVorgaengeRel() {
				var link = viewLinkHandler.buildSearchMyVorgaengeLink(UserProfileTestFactory.ID);

				assertThat(link.getRel()).extracting(LinkRelation::value).isEqualTo(RootViewLinkHandler.SEARCH_MY_VORGEANGE_REL);
			}
		}

		@DisplayName("search unassigned vorgaenge")
		@Nested
		class TestSearchUnassignedVorgaenge {

			@Test
			void shouldBuildLink() {
				var link = viewLinkHandler.buildSearchUnassignedVorgaengeLink();

				assertThat(link.getHref()).isEqualTo("/api/vorgangs?page=0&limit=100&assignedTo=&searchBy={searchBy}");
				assertThat(link.getRel().value()).isEqualTo(RootViewLinkHandler.SEARCH_UNASSIGNED_VORGEANGE_REL);
			}
		}

		@DisplayName("search all vorgaenge by assignedTo")
		@Nested
		class TestBuildGetAllVorgaengeByAssigendToAndSearchLink {

			@Test
			void shouldBuildLink() {
				var link = viewLinkHandler.buildGetAllVorgaengeByAssignedToAndSearchByLink(UserProfileTestFactory.ID).withSelfRel();

				assertThat(link).extracting(Link::getHref)
						.isEqualTo("/api/vorgangs?page=0&limit=100&assignedTo=" + UserProfileTestFactory.ID.toString() + "&searchBy={searchBy}");
			}
		}

		@DisplayName("all vorgang status")
		@Nested
		class TestAddAllVorgangStatusLinks {

			private final ModelBuilder<Root> modelBuilder = ModelBuilder.fromEntity(rootResource);

			@Test
			void shouldCallAddVorgangStatusLinks() {
				viewLinkHandler.addAllVorgangStatusLinks(modelBuilder);

				verify(viewLinkHandler).addVorgangStatusLinks(modelBuilder, RootViewLinkHandler.VORGAENGE_ALL_REL_TEMPLATE);
			}
		}

		@DisplayName("my vorgang status")
		@Nested
		class TestAddMyVorgangStatusLinks {

			@Test
			void shouldCallAddVorgangStatusLink() {
				viewLinkHandler.buildMyVorgaengeStatusLinks(UserProfileTestFactory.ID);

				verify(viewLinkHandler).buildVorgangStatusLinks(RootViewLinkHandler.VORGAENGE_MY_REL_TEMPLATE, UserProfileTestFactory.ID);
			}

		}

		@DisplayName("vorgang status")
		@Nested
		class TestBuildVorgangStatusLinks {

			private final UserId userId = UserProfileTestFactory.ID;

			@Test
			void shouldCallBuildVorgangListByPageLink() {
				viewLinkHandler.buildVorgangStatusLinks(RootViewLinkHandler.VORGAENGE_ALL_REL_TEMPLATE, UserProfileTestFactory.ID);

				verify(viewLinkHandler, times(7)).buildGetAllVorgaengeByAssignedToAndStatus(
						any(VorgangStatus.class),
						eq(RootViewLinkHandler.VORGAENGE_ALL_REL_TEMPLATE),
						eq(userId));
			}

			@Test
			void shouldCallFormatLinkRelTemplate() {
				viewLinkHandler.buildVorgangStatusLinks(RootViewLinkHandler.VORGAENGE_ALL_REL_TEMPLATE, UserProfileTestFactory.ID);

				verify(viewLinkHandler, times(7)).formatLinkRelTemplate(eq(RootViewLinkHandler.VORGAENGE_ALL_REL_TEMPLATE), any(VorgangStatus.class));
			}

			@Test
			void shouldBuildLinks() {
				var links = viewLinkHandler.buildVorgangStatusLinks(RootViewLinkHandler.VORGAENGE_ALL_REL_TEMPLATE, UserProfileTestFactory.ID);

				assertThat(links).hasSize(7);
			}
		}

		@DisplayName("format linkrel template")
		@Nested
		class TestFormatLinkRelTemplate {

			@ParameterizedTest
			@EnumSource(mode = Mode.INCLUDE, names = { "NEU", "ANGENOMMEN", "IN_BEARBEITUNG", "BESCHIEDEN", "ABGESCHLOSSEN", "VERWORFEN",
					"ZU_LOESCHEN" })
			void shouldCallFormatLinkRelTemplateWithAllVorgaengeTemplate(VorgangStatus status) {
				var linkRel = viewLinkHandler.formatLinkRelTemplate(RootViewLinkHandler.VORGAENGE_ALL_REL_TEMPLATE, status);

				assertThat(linkRel).isEqualTo("vorgaenge_" + status.toString().toLowerCase() + "_all");
			}

			@ParameterizedTest
			@EnumSource(mode = Mode.INCLUDE, names = { "NEU", "ANGENOMMEN", "IN_BEARBEITUNG", "BESCHIEDEN", "ABGESCHLOSSEN", "VERWORFEN",
					"ZU_LOESCHEN" })
			void shouldCallFormatLinkRelTemplateWithMyVorgaengeTemplate(VorgangStatus status) {
				var linkRel = viewLinkHandler.formatLinkRelTemplate(RootViewLinkHandler.VORGAENGE_MY_REL_TEMPLATE, status);

				assertThat(linkRel).isEqualTo("vorgaenge_" + status.toString().toLowerCase() + "_my");
			}

			@ParameterizedTest
			@EnumSource(mode = Mode.INCLUDE, names = { "NEU", "ANGENOMMEN", "IN_BEARBEITUNG", "BESCHIEDEN", "ABGESCHLOSSEN", "VERWORFEN",
					"ZU_LOESCHEN" })
			void shouldCallFormatLinkRelTemplateWithUnassignedVorgaengeTemplate(VorgangStatus status) {
				var linkRel = viewLinkHandler.formatLinkRelTemplate(RootViewLinkHandler.VORGAENGE_UNASSIGNED_REL_TEMPLATE, status);

				assertThat(linkRel).isEqualTo("vorgaenge_" + status.toString().toLowerCase() + "_unassigned");
			}
		}

		@DisplayName("build get all vorgaenge by status")
		@Nested
		class TestBuildGetAllByStatus {

			@ParameterizedTest
			@EnumSource(mode = Mode.INCLUDE, names = { "NEU", "ANGENOMMEN", "IN_BEARBEITUNG", "BESCHIEDEN", "ABGESCHLOSSEN", "VERWORFEN",
					"ZU_LOESCHEN" })
			void shouldAddByStatus(VorgangStatus status) {
				var link = viewLinkHandler.buildGetAllVorgaengeByStatus(status, RootViewLinkHandler.VORGAENGE_MY_REL_TEMPLATE);

				assertThat(link.getHref()).isEqualTo("/api/vorgangs?page=0&limit=100&status=" + status.toString());
			}

		}

		@DisplayName("build get all vorgaenge by assignedTo and status")
		@Nested
		class TestBuildGetAllVorgaengeByAssignedToAndStatus {

			@ParameterizedTest
			@EnumSource(mode = Mode.INCLUDE, names = { "NEU", "ANGENOMMEN", "IN_BEARBEITUNG", "BESCHIEDEN", "ABGESCHLOSSEN", "VERWORFEN",
					"ZU_LOESCHEN" })
			void shouldAddByAssignedToAndStatus(VorgangStatus status) {
				var link = viewLinkHandler.buildGetAllVorgaengeByAssignedToAndStatus(status, RootViewLinkHandler.VORGAENGE_MY_REL_TEMPLATE,
						UserProfileTestFactory.ID);

				assertThat(link.getHref()).isEqualTo(
						"/api/vorgangs?page=0&limit=100&assignedTo=" + UserProfileTestFactory.ID.toString() + "&status=" + status.toString());
			}
		}

		@DisplayName("build get all unassigned vorgaenge by status")
		@Nested
		class TestBuildGetAllUnassignedVorgaengeByStatus {

			@ParameterizedTest
			@EnumSource(mode = Mode.INCLUDE, names = { "NEU", "ANGENOMMEN", "IN_BEARBEITUNG", "BESCHIEDEN", "ABGESCHLOSSEN", "VERWORFEN",
					"ZU_LOESCHEN" })
			void shouldAddByUnassignedToAndStatus(VorgangStatus status) {
				var link = viewLinkHandler.buildGetAllVorgaengeByAssignedToAndStatus(status, RootViewLinkHandler.VORGAENGE_MY_REL_TEMPLATE,
						UserId.empty());

				assertThat(link.getHref()).isEqualTo("/api/vorgangs?page=0&limit=100&assignedTo=&status=" + status.toString());
			}
		}
	}

	@Nested
	class TestBuildGelAllByUngeleseneNachrichtenLink {

		@Test
		void shouldHaveLinkHref() {
			var link = viewLinkHandler.buildGelAllByUngeleseneNachrichtenLink();

			assertThat(link)
					.extracting(Link::getHref)
					.isEqualTo("/api/vorgangs?page=0&limit=100&nachrichten=ungelesene");
		}

		@Test
		void shouldHaveLinkRel() {
			var link = viewLinkHandler.buildGelAllByUngeleseneNachrichtenLink();

			assertThat(link)
					.extracting(Link::getRel)
					.extracting(LinkRelation::value)
					.isEqualTo(RootViewLinkHandler.ALL_UNGELESENE_NACHRICHTEN_REL);
		}
	}

	@Nested
	class TestBuildGetAllByAssignedToAndUngeleseneNachrichten {

		@Nested
		class AssignTo {

			@Test
			void shouldHaveLink() {
				var link = viewLinkHandler.buildGetAllByAssignedToAndUngeleseneNachrichten(UserProfileTestFactory.ID,
						RootViewLinkHandler.MY_UNGELESENE_NACHRICHTEN_REL);

				assertThat(link)
						.extracting(Link::getHref)
						.isEqualTo("/api/vorgangs?page=0&limit=100&assignedTo=" + UserProfileTestFactory.ID + "&nachrichten=ungelesene");
			}

			@Test
			void shouldHaveLinkRel() {
				var link = viewLinkHandler.buildGetAllByAssignedToAndUngeleseneNachrichten(UserProfileTestFactory.ID,
						RootViewLinkHandler.MY_UNGELESENE_NACHRICHTEN_REL);

				assertThat(link)
						.extracting(Link::getRel)
						.extracting(LinkRelation::value)
						.isEqualTo(RootViewLinkHandler.MY_UNGELESENE_NACHRICHTEN_REL);
			}
		}

		@Nested
		class Unassigned {

			@Test
			void shouldHaveLink() {
				var link = viewLinkHandler.buildGetAllByAssignedToAndUngeleseneNachrichten(UserId.empty(),
						RootViewLinkHandler.UNASSIGNED_UNGELESENE_NACHRICHTEN_REL);

				assertThat(link)
						.extracting(Link::getHref)
						.isEqualTo("/api/vorgangs?page=0&limit=100&assignedTo=&nachrichten=ungelesene");
			}

			@Test
			void shouldHaveLinkRel() {
				var link = viewLinkHandler.buildGetAllByAssignedToAndUngeleseneNachrichten(UserId.empty(),
						RootViewLinkHandler.UNASSIGNED_UNGELESENE_NACHRICHTEN_REL);

				assertThat(link)
						.extracting(Link::getRel)
						.extracting(LinkRelation::value)
						.isEqualTo(RootViewLinkHandler.UNASSIGNED_UNGELESENE_NACHRICHTEN_REL);
			}

		}

	}

	@Nested
	class TestAddGetByUngeleseneNachrichtenLinks {

		@Mock
		private Root rootResource;
		private final ModelBuilder<Root> modelBuilder = ModelBuilder.fromEntity(rootResource);
		private final Link linkAllUngeleseneNachrichten = Link.of(LoremIpsum.getInstance().getUrl());
		private final Link linkMyUngeleseneNachrichten = Link.of(LoremIpsum.getInstance().getUrl());
		private final Link linkUnassignedUngeleseneNachrichten = Link.of(LoremIpsum.getInstance().getUrl());

		@BeforeEach
		void setUp() {
			doReturn(linkAllUngeleseneNachrichten).when(viewLinkHandler).buildGelAllByUngeleseneNachrichtenLink();
			doReturn(linkUnassignedUngeleseneNachrichten).when(viewLinkHandler)
					.buildGetAllByAssignedToAndUngeleseneNachrichten(UserId.empty(), RootViewLinkHandler.UNASSIGNED_UNGELESENE_NACHRICHTEN_REL);
		}

		@Test
		void shouldBuildLinkForUngeleseneNachrichten() {
			viewLinkHandler.addGetByUngeleseneNachrichtenLinks(modelBuilder, Optional.of(UserProfileTestFactory.ID));

			verify(viewLinkHandler).buildGelAllByUngeleseneNachrichtenLink();
		}

		@Test
		void shouldAddLinkForUngeleseneNachrichten() {
			var modelBuilder = ModelBuilder.fromEntity(rootResource);

			viewLinkHandler.addGetByUngeleseneNachrichtenLinks(modelBuilder, Optional.of(UserProfileTestFactory.ID));

			assertThat(modelBuilder.buildModel().getLinks()).contains(linkAllUngeleseneNachrichten);
		}

		@Test
		void shouldBuildLinkForUnassignedUngeleseneNachrichten() {
			viewLinkHandler.addGetByUngeleseneNachrichtenLinks(modelBuilder, Optional.empty());

			verify(viewLinkHandler).buildGetAllByAssignedToAndUngeleseneNachrichten(UserId.empty(),
					RootViewLinkHandler.UNASSIGNED_UNGELESENE_NACHRICHTEN_REL);
		}

		@Test
		void shouldAddLinkForUnassignedUngeleseneNachrichten() {
			var modelBuilder = ModelBuilder.fromEntity(rootResource);

			viewLinkHandler.addGetByUngeleseneNachrichtenLinks(modelBuilder, Optional.empty());

			assertThat(modelBuilder.buildModel().getLinks()).contains(linkUnassignedUngeleseneNachrichten);
		}

		@Nested
		class UsedIdExists {

			@BeforeEach
			void setUp() {
				doReturn(linkMyUngeleseneNachrichten).when(viewLinkHandler).buildGetAllByAssignedToAndUngeleseneNachrichten(UserProfileTestFactory.ID,
						RootViewLinkHandler.MY_UNGELESENE_NACHRICHTEN_REL);
			}

			@Test
			void shouldBuildLinkForMyUngeleseneNachrichten() {
				viewLinkHandler.addGetByUngeleseneNachrichtenLinks(modelBuilder, Optional.of(UserProfileTestFactory.ID));

				verify(viewLinkHandler).buildGetAllByAssignedToAndUngeleseneNachrichten(UserProfileTestFactory.ID,
						RootViewLinkHandler.MY_UNGELESENE_NACHRICHTEN_REL);
			}

			@Test
			void shouldAddLinkForMyUngeleseneNachrichten() {
				var modelBuilder = ModelBuilder.fromEntity(rootResource);

				viewLinkHandler.addGetByUngeleseneNachrichtenLinks(modelBuilder, Optional.of(UserProfileTestFactory.ID));

				assertThat(modelBuilder.buildModel().getLinks()).contains(linkMyUngeleseneNachrichten);
			}
		}

		@Nested
		class UserIdEmpty {

			@Test
			void shouldNotBuildLinkForMyUngeleseneNachrichten() {
				viewLinkHandler.addGetByUngeleseneNachrichtenLinks(modelBuilder, Optional.empty());

				verify(viewLinkHandler, never()).buildGetAllByAssignedToAndUngeleseneNachrichten(UserProfileTestFactory.ID,
						RootViewLinkHandler.MY_UNGELESENE_NACHRICHTEN_REL);
			}
		}

	}

	//	@DisplayName("Test user assistance documentation link")
	//	@Nested
	//	class TestDocumentationLink {
	//
	//		private static final String DOCUMENTATION_URL = "http://alfa-docs";
	//
	//		@Test
	//		void shouldNotHaveLinkForUndefinedUrl() {
	//			var link = viewLinkHandler.toModel(root);
	//
	//			assertThat(link.getLink(RootViewLinkHandler.REL_DOCUMENTATIONS)).isEmpty();
	//		}
	//
	//		@Test
	//		void shouldHaveLinkForDefinedUrl() {
	//			ReflectionTestUtils.setField(viewLinkHandler, "documentationUrl", DOCUMENTATION_URL);
	//
	//			var link = viewLinkHandler.toModel(root);
	//
	//			assertThat(link.getLink(RootViewLinkHandler.REL_DOCUMENTATIONS))
	//					.isPresent()
	//					.map(Link::getHref)
	//					.hasValue(DOCUMENTATION_URL);
	//		}
	//
	//		@Test
	//		void shouldNotHaveLinkForEmptyStringUrl() {
	//			ReflectionTestUtils.setField(viewLinkHandler, "documentationUrl", "");
	//
	//			var link = viewLinkHandler.toModel(root);
	//
	//			assertThat(link.getLink(RootViewLinkHandler.REL_DOCUMENTATIONS)).isEmpty();
	//		}
	//
	//	}
}