/*
 * Copyright (c) 2024.
 * Lizenziert unter der EUPL, Version 1.2 oder - sobald
 * diese von der Europäischen Kommission genehmigt wurden -
 * Folgeversionen der EUPL ("Lizenz");
 * Sie dürfen dieses Werk ausschließlich gemäß
 * dieser Lizenz nutzen.
 * Eine Kopie der Lizenz finden Sie hier:
 *
 * https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
 *
 * Sofern nicht durch anwendbare Rechtsvorschriften
 * gefordert oder in schriftlicher Form vereinbart, wird
 * die unter der Lizenz verbreitete Software "so wie sie
 * ist", OHNE JEGLICHE GEWÄHRLEISTUNG ODER BEDINGUNGEN -
 * ausdrücklich oder stillschweigend - verbreitet.
 * Die sprachspezifischen Genehmigungen und Beschränkungen
 * unter der Lizenz sind dem Lizenztext zu entnehmen.
 */

package de.ozgcloud.token;

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

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;

import de.ozgcloud.token.saml.SamlTokenService;
import io.grpc.stub.StreamObserver;

class TokenCheckGrpcServiceTest {

	@Spy
	@InjectMocks
	private TokenCheckGrpcService service;

	@Mock
	private SamlTokenService samlTokenService;

	@Mock
	private CheckTokenResultMapper tokenCheckMapper;

	@Mock
	private StreamObserver<GrpcCheckTokenResponse> tokenStreamObserver;
	@Mock
	private GrpcCheckTokenResponse grpcCheckTokenResponse;

	@Nested
	class TestCheckToken {

		@Mock
		private TokenValidationResult tokenCheckResult;

		@BeforeEach
		void init() {
			when(samlTokenService.validate(any())).thenReturn(tokenCheckResult);
			doReturn(grpcCheckTokenResponse).when(service).buildCheckResponse(any());
		}

		@Test
		void shouldCallSamlTokenService() {
			checkToken();

			verify(samlTokenService).validate(GrpcCheckTokenRequestTestFactory.TOKEN);
		}

		@Test
		void shouldCallBuildCheckResponse() {
			checkToken();

			verify(service).buildCheckResponse(tokenCheckResult);
		}

		@Test
		void shouldCallOnNext() {
			checkToken();

			verify(tokenStreamObserver).onNext(grpcCheckTokenResponse);
		}

		@Test
		void shouldCallOnCompleted() {
			checkToken();

			verify(tokenStreamObserver).onCompleted();
		}

		private void checkToken() {
			service.checkToken(GrpcCheckTokenRequestTestFactory.create(), tokenStreamObserver);
		}
	}

	@Nested
	class TestBuildCheckResponse {

		@Nested
		class TestValidToken {

			private final TokenValidationResult tokenCheckResult = CheckTokenResultTestFactory.createValid();

			@BeforeEach
			void mock() {
				doReturn(grpcCheckTokenResponse).when(service).buildValidCheckTokenResponse(any());
			}

			@Test
			void shouldCallBuildValidCheckTokenResponse() {
				service.buildCheckResponse(tokenCheckResult);

				verify(service).buildValidCheckTokenResponse(tokenCheckResult);
				verify(service, never()).buildInvalidCheckTokenResponse(any());
			}

			@Test
			void shouldReturnResponse() {
				var result = service.buildCheckResponse(tokenCheckResult);

				assertThat(result).isEqualTo(grpcCheckTokenResponse);
			}
		}

		@Nested
		class TestInvalidToken {

			private final TokenValidationResult tokenCheckResult = CheckTokenResultTestFactory.createInvalid();

			@BeforeEach
			void mock() {
				doReturn(grpcCheckTokenResponse).when(service).buildInvalidCheckTokenResponse(any());
			}

			@Test
			void shouldCallBuildInvalidCheckTokenResponse() {
				service.buildCheckResponse(tokenCheckResult);

				verify(service).buildInvalidCheckTokenResponse(tokenCheckResult);
				verify(service, never()).buildValidCheckTokenResponse(any());
			}

			@Test
			void shouldReturnResponse() {
				var result = service.buildCheckResponse(tokenCheckResult);

				assertThat(result).isEqualTo(grpcCheckTokenResponse);
			}
		}
	}

	@Nested
	class TestBuildValidCheckTokenResponse {

		private final TokenValidationResult tokenCheckResult = CheckTokenResultTestFactory.createValid();

		@BeforeEach
		void mock() {
			when(tokenCheckMapper.toTokenAttributes(any())).thenReturn(GrpcCheckTokenResponseTestFactory.CHECK_TOKEN_RESULT);
		}

		@Test
		void shouldCallMapper() {
			buildCheckTokenResponse();

			verify(tokenCheckMapper).toTokenAttributes(tokenCheckResult);
		}

		@Test
		void shouldReturnResponse() {
			var response = buildCheckTokenResponse();

			assertThat(response).isEqualTo(GrpcCheckTokenResponseTestFactory.createValid());
		}

		private GrpcCheckTokenResponse buildCheckTokenResponse() {
			return service.buildValidCheckTokenResponse(tokenCheckResult);
		}
	}

	@Nested
	class TestBuildInvalidCheckTokenResponse {

		private final TokenValidationResult tokenCheckResult = CheckTokenResultTestFactory.createInvalid();

		@BeforeEach
		void mock() {
			when(tokenCheckMapper.toCheckError(any())).thenReturn(GrpcCheckErrorTestFactory.create());
		}

		@Test
		void shouldCallMapper() {
			buildCheckTokenResponse();

			verify(tokenCheckMapper).toCheckError(tokenCheckResult);
		}

		@Test
		void shouldReturnResponse() {
			var response = buildCheckTokenResponse();

			assertThat(response).isEqualTo(GrpcCheckTokenResponseTestFactory.createInvalid());
		}

		private GrpcCheckTokenResponse buildCheckTokenResponse() {
			return service.buildInvalidCheckTokenResponse(tokenCheckResult);
		}
	}
}