package com.workfusion.odf.mail.impl.javax;

import java.io.IOException;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;
import javax.mail.search.SubjectTerm;

import microsoft.exchange.webservices.data.core.PropertySet;
import microsoft.exchange.webservices.data.core.service.schema.ItemSchema;
import microsoft.exchange.webservices.data.search.filter.SearchFilter;
import org.apache.commons.io.IOUtils;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import com.workfusion.odf.service.mail.entity.EmailMessage;
import com.workfusion.odf.service.mail.impl.javax.ImapPop3EmailClient;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.params.provider.Arguments.arguments;

public class EmailAttachmentTest {

    private static final String OUTLOOK = "outlook.office365.com";
    private static final String EXCHANGE = "https://" + OUTLOOK + "/EWS/Exchange.asmx";
    private static final String IMAP_PORT = "993";

    private static final String USERNAME = "rpa-coe-gf@workfusion.com";
    private static final String PASSWORD = "uX878eHV3AQtJBE";

    private static EwsEmailsClient ewsClient;
    private static ImapPop3EmailClient imapClient;

    private static byte[] expectedAttachment;
    private static byte[] expectedInlineAttachment;

    @BeforeAll
    static void beforeAll() throws IOException {
        ewsClient = new EwsEmailsClient(EXCHANGE, USERNAME, PASSWORD);
        imapClient = new ImapPop3EmailClient(
                ImapPop3EmailClient.SupportedProtocol.IMAP,
                OUTLOOK,
                IMAP_PORT,
                USERNAME,
                PASSWORD,
                true);

        expectedAttachment = IOUtils.resourceToByteArray("mail/attachment.png", EwsEmailsClientTest.class.getClassLoader());
        expectedInlineAttachment = IOUtils.resourceToByteArray("mail/inline_attachment.png", EwsEmailsClientTest.class.getClassLoader());
    }

    static Stream<Arguments> messageClientsProvider() {
        final Function<String, EmailMessage> ewsClientFunction = (String subject) -> fetchMessage(ewsClient, subject);
        final Function<String, EmailMessage> imapClientFunction = (String subject) -> fetchMessage(imapClient, subject);

        return Stream.of(
                arguments("EWSEmailsClient", ewsClientFunction),
                arguments("ImapPop3EmailClient", imapClientFunction));
    }

    @DisplayName("test inline attachment")
    @ParameterizedTest(name = "fetched by {0}")
    @MethodSource("messageClientsProvider")
    void testInlineAttachment(String clientName, Function<String, EmailMessage> client) {
        final EmailMessage message = client.apply("EwsEmailsClientTest inline attachment");

        shouldContainInlineAttachment(message);
        shouldNotContainAttachments(message);

    }

    @DisplayName("test mixed attachments")
    @ParameterizedTest(name = "fetched by {0}")
    @MethodSource("messageClientsProvider")
    void testMixedAttachments(String clientName, Function<String, EmailMessage> client) {
        final EmailMessage message = client.apply("EwsEmailsClientTest mixed attachments");

        shouldContainInlineAttachment(message);
        shouldContainAttachment(message);
    }

    void shouldContainInlineAttachment(EmailMessage message) {
        assertThat(message.getInlineFiles()).as("should have single inline attachment")
                .hasSize(1);
        assertThat(message.getInlineFiles().get(0).getContent()).as("should have expected inline attachment")
                .isEqualTo(expectedInlineAttachment);
    }

    void shouldContainAttachment(EmailMessage message) {
        assertThat(message.getAttachments()).as("should have single attachment")
                .hasSize(1);
        assertThat(message.getAttachments().get(0).getContent()).as("should have expected attachment")
                .isEqualTo(expectedAttachment);
    }

    void shouldNotContainAttachments(EmailMessage message) {
        assertThat(message.getAttachments()).as("should have no more attachments").isEmpty();
    }

    static EmailMessage fetchMessage(EwsEmailsClient client, String subject) {
        List<EmailMessage> messages = client.fetchMessages("unit_tests_exchange",
                PropertySet.FirstClassProperties,
                new SearchFilter.IsEqualTo(ItemSchema.Subject, subject),
                false);

        assertThat(messages).as("should fetch single message with subject '%s'", subject).hasSize(1);

        return messages.get(0);
    }

    static EmailMessage fetchMessage(ImapPop3EmailClient client, String subject) {
        List<EmailMessage> messages = client.searchMessages("Inbox/unit_tests_exchange",
                new SubjectTerm(subject));

        assertThat(messages).as("should fetch single message with subject '%s'", subject).hasSize(1);

        return messages.get(0);
    }
}
