package de.ozgcloud.admin.keycloak;

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

import java.util.List;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.keycloak.admin.client.resource.GroupsResource;
import org.keycloak.representations.idm.GroupRepresentation;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;

import com.thedeanda.lorem.LoremIpsum;

import jakarta.ws.rs.core.Response;

class KeycloakApiServiceTest {

	@Spy
	@InjectMocks
	private KeycloakApiService service;
	@Mock
	private GroupsResource groupsResource;
	@Mock
	private Response response;

	@Nested
	class TestGetAllGroups {

		@Test
		void shouldCallGroupsResource() {
			service.getAllGroups();

			verify(groupsResource).groups("", 0, Integer.MAX_VALUE, false);
		}

		@Test
		void shouldReturnGroupRepresentations() {
			var groupRepresentation = GroupRepresentationTestFactory.create();
			when(groupsResource.groups("", 0, Integer.MAX_VALUE, false)).thenReturn(List.of(groupRepresentation));

			var gotGroupRepresentations = service.getAllGroups();

			assertThat(gotGroupRepresentations).containsExactly(groupRepresentation);
		}
	}

	@Nested
	class TestAddGroup {

		private final GroupRepresentation groupRepresentation = GroupRepresentationTestFactory.create();
		private final String resourceId = GroupRepresentationTestFactory.create().getId();

		@BeforeEach
		void init() {
			when(groupsResource.add(groupRepresentation)).thenReturn(response);
			doReturn(resourceId).when(service).getCreatedResourceIdFromResponse(response);
		}

		@Test
		void shouldCallGroupsResource() {
			callService();

			verify(groupsResource).add(groupRepresentation);
		}

		@Test
		void shouldGetAddedResourceIdFromResponse() {
			callService();

			verify(service).getCreatedResourceIdFromResponse(response);
		}

		@Test
		void shouldReturnAddedResourceId() {
			var id = callService();

			assertThat(id).isEqualTo(resourceId);
		}

		private String callService() {
			return service.addGroup(groupRepresentation);
		}
	}

	@Nested
	class TestGetCreatedResourceIdFromResponse {

		private final String resourceId = GroupRepresentationTestFactory.create().getId();
		private final String location = LoremIpsum.getInstance().getUrl();

		@Test
		void shouldExtractResourceIdFromLocationHeader() {
			givenResponseCreated();

			callService();

			verify(service).extractResourceIdFromLocationHeader(location);
		}

		@Test
		void shouldReturnResourceId() {
			givenResponseCreated();

			var id = callService();

			assertThat(id).isEqualTo(resourceId);
		}

		@Test
		void shouldThrowExceptionIsStatusOtherThenCreated() {
			givenResponseUnauthorized();

			assertThatExceptionOfType(ResourceCreationException.class).isThrownBy(this::callService)
					.withMessageStartingWith("Failed to add group - got response with status 403 (Unauthorized)");
		}

		private void givenResponseCreated() {
			when(response.getStatus()).thenReturn(201);
			when(response.getHeaderString("Location")).thenReturn(location);
			doReturn(resourceId).when(service).extractResourceIdFromLocationHeader(location);
		}

		private void givenResponseUnauthorized() {
			when(response.getStatus()).thenReturn(403);
			when(response.getStatusInfo()).thenReturn(Response.Status.UNAUTHORIZED);
		}

		private String callService() {
			return service.getCreatedResourceIdFromResponse(response);
		}
	}

	@Nested
	class TestExtractResourceIdFromLocationHeader {

		private final String resourceId = GroupRepresentationTestFactory.create().getId();
		private final String location = "https://keycloak-url.test/admin/realms/test/groups/" + resourceId;

		@Test
		void shouldReturnId() {
			var id = service.extractResourceIdFromLocationHeader(location);

			assertThat(id).isEqualTo(resourceId);
		}
	}
}