package de.ozgcloud.xta.client;

import static de.ozgcloud.xta.client.extension.XtaServerSetupExtensionTestUtil.*;
import static org.assertj.core.api.Assertions.*;

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.api.extension.RegisterExtension;

import de.ozgcloud.xta.client.extension.XtaMessageExampleLoader;
import de.ozgcloud.xta.client.extension.XtaServerSetupExtensionTestUtil;
import de.ozgcloud.xta.client.extension.XtaTestServerSetupExtension;
import de.ozgcloud.xta.client.model.XtaMessage;
import de.ozgcloud.xta.client.model.XtaMessageStatus;
import genv3.de.xoev.transport.xta.x211.InvalidMessageIDException;
import lombok.SneakyThrows;

class XtaClientITCase {

	@RegisterExtension
	static final XtaTestServerSetupExtension XTA_TEST_SERVER_SETUP_EXTENSION = new XtaTestServerSetupExtension();

	private XtaClient client;

	@BeforeEach
	@SneakyThrows
	void setup() {
		client = XTA_TEST_SERVER_SETUP_EXTENSION.getClient();
	}

	@DisplayName("get messages metadata")
	@Nested
	class TestGetMessagesMetadata {

		@DisplayName("with no messages")
		@Nested
		class TestWithNoMessages {

			@DisplayName("should return zero pending messages")
			@Test
			@SneakyThrows
			void shouldReturnZeroPendingMessages() {
				var result = client.getMessagesMetadata(READER_CLIENT_IDENTIFIER1.value());

				assertThat(result.pendingMessageCount()).isZero();
			}
		}

		@DisplayName("with one message")
		@Nested
		class TestWithOneMessage {

			@BeforeEach
			void setup() {
				XTA_TEST_SERVER_SETUP_EXTENSION.sendTestMessage();
			}

			@DisplayName("should return one pending message for client")
			@Test
			@SneakyThrows
			void shouldReturnOnePendingMessageClient() {
				var result = client.getMessagesMetadata(READER_CLIENT_IDENTIFIER1.value());

				assertThat(result.pendingMessageCount()).isOne();
			}

			@DisplayName("should return no pending message for another client")
			@Test
			@SneakyThrows
			void shouldReturnNoPendingMessageForAnotherClient() {
				var result = client.getMessagesMetadata(READER_CLIENT_IDENTIFIER2.value());

				assertThat(result.pendingMessageCount()).isZero();
			}

		}

	}

	@DisplayName("get message")
	@Nested
	class TestGetMessage {

		private String messageId;
		private XtaMessage message;

		@BeforeEach
		@SneakyThrows
		void setup() {
			var messageConfig = XtaMessageExampleLoader.MessageExampleConfig.builder()
					.messageLabel("dfoerdermittel")
					.reader(READER_CLIENT_IDENTIFIER1)
					.author(AUTHOR_CLIENT_IDENTIFIER)
					.build();
			message = XtaMessageExampleLoader.load(messageConfig);
			messageId = XtaServerSetupExtensionTestUtil.sendTestMessage(client, message);
		}

		@DisplayName("should return message with green status")
		@Test
		@SneakyThrows
		void shouldReturnMessageWithGreenStatus() {
			var result = client.getMessage(READER_CLIENT_IDENTIFIER1.value(), messageId);

			assertThat(result.message().metaData().messageId()).isEqualTo(messageId);
			assertThat(result.transportReport().metaData().messageId()).isEqualTo(messageId);
			assertThat(result.transportReport().status()).isEqualTo(XtaMessageStatus.GREEN);
		}

		@DisplayName("should return message with correct message file content")
		@Test
		@SneakyThrows
		void shouldReturnMessageWithCorrectMessageFileContent() {
			var messageContent = extractMessageFileContent(message);

			var result = client.getMessage(READER_CLIENT_IDENTIFIER1.value(), messageId);
			var resultContent = extractMessageFileContent(result.message());

			assertThat(messageContent).isEqualTo(resultContent);
		}

		@DisplayName("should not show message id for a closed message in status list")
		@Test
		@SneakyThrows
		void shouldNotShowMessageIdForClosedMessageInStatusList() {
			assertThatNoException().isThrownBy(() -> client.getMessage(READER_CLIENT_IDENTIFIER1.value(), messageId));
			var metadataResult = client.getMessagesMetadata(READER_CLIENT_IDENTIFIER1.value());
			if (!metadataResult.messages().isEmpty()) {
				assertThat(metadataResult.messages()).allMatch(metadata -> !messageId.equals(metadata.messageId()));
			}
		}

		@DisplayName("should throw invalid message id exception for modified message id")
		@Test
		void shouldThrowInvalidMessageIdExceptionForModifiedMessageId() {
			assertThatThrownBy(() -> client.getMessage(READER_CLIENT_IDENTIFIER1.value(), messageId + "1"))
					.isInstanceOf(InvalidMessageIDException.class);
		}

		@DisplayName("should throw invalid message id exception for other client")
		@Test
		void shouldThrowInvalidMessageIdExceptionForOtherClient() {
			assertThatThrownBy(() -> client.getMessage(READER_CLIENT_IDENTIFIER2.value(), messageId))
					.isInstanceOf(InvalidMessageIDException.class);
		}
	}

	@DisplayName("send message")
	@Nested
	class TestSendMessage {

		private XtaMessage message;

		@BeforeEach
		void beforeEach() {
			var messageConfig = XtaMessageExampleLoader.MessageExampleConfig.builder()
					.messageLabel("dfoerdermittel")
					.reader(READER_CLIENT_IDENTIFIER1)
					.author(READER_CLIENT_IDENTIFIER1)
					.build();
			message = XtaMessageExampleLoader.load(messageConfig);
		}

		@DisplayName("should return transport report with open status")
		@Test
		@SneakyThrows
		void shouldReturnTransportReportWithOpenStatus() {
			var transportReport = client.sendMessage(message);

			assertThat(transportReport.status()).isEqualTo(XtaMessageStatus.OPEN);
		}

	}
}