package be.ehealth.businessconnector.ehbox.v3.session;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import be.ehealth.businessconnector.ehbox.api.domain.Addressee;
import be.ehealth.businessconnector.ehbox.api.domain.DocumentMessage;
import be.ehealth.businessconnector.ehbox.api.domain.NewsMessage;
import be.ehealth.businessconnector.ehbox.v3.EhboxTestDataBuilder;
import be.ehealth.businessconnector.ehbox.v3.EhboxTestUtilities;
import be.ehealth.businessconnector.ehbox.v3.builders.BuilderFactory;
import be.ehealth.technicalconnector.exception.ConnectorException;
import be.ehealth.technicalconnector.exception.TechnicalConnectorException;
import be.ehealth.technicalconnector.session.Session;
import be.fgov.ehealth.commons.protocol.v1.ResponseType;
import be.fgov.ehealth.ehbox.consultation.protocol.v3.AcknowledgmentsStatus;
import be.fgov.ehealth.ehbox.consultation.protocol.v3.GetBoxInfoResponse;
import be.fgov.ehealth.ehbox.consultation.protocol.v3.GetFullMessageRequest;
import be.fgov.ehealth.ehbox.consultation.protocol.v3.GetFullMessageResponse;
import be.fgov.ehealth.ehbox.consultation.protocol.v3.GetHistoryRequest;
import be.fgov.ehealth.ehbox.consultation.protocol.v3.GetHistoryResponse;
import be.fgov.ehealth.ehbox.consultation.protocol.v3.GetMessageAcknowledgmentsStatusResponse;
import be.fgov.ehealth.ehbox.consultation.protocol.v3.GetMessageListResponseType;
import be.fgov.ehealth.ehbox.consultation.protocol.v3.GetMessagesListRequest;
import be.fgov.ehealth.ehbox.consultation.protocol.v3.Message;
import be.fgov.ehealth.ehbox.consultation.protocol.v3.Row;
import be.fgov.ehealth.ehbox.core.v3.BoxIdType;
import be.fgov.ehealth.ehbox.publication.protocol.v3.SendMessageResponse;

/**
 * eHealthBox Service Integration Tests This test shows the use of the eHealthBox Service through the use of the Business Connector.
 * 
 * The tests below use the Session Management Service to manage the session and SAML/STS token. The eHealthBox of the test user specified in
 * the business connector property file is used, these tests expect that this is set to a valid INSS of a citizen.
 * 
 * @author EHP
 * 
 */
@RunWith(value = Parameterized.class)
public class EhboxV3ServiceConsultationIntegrationTest extends AbstractEhboxV3ServiceIntegration {

    private BoxIdType mailBoxToUse;

    @BeforeClass
    public static void initialize() throws Exception {
        AbstractEhboxV3ServiceIntegration.initializeMailboxesInKnownCondition();
    }


    @AfterClass
    public static void unloadSession() throws Exception {
        Session.getInstance().unloadSession();
    }


    /**
     * @param mailBoxToUse
     */
    public EhboxV3ServiceConsultationIntegrationTest(BoxIdType mailBoxToUse) {
        super();
        this.mailBoxToUse = mailBoxToUse;
    }


    @Parameters
    public static Collection<Object[]> data() {
        Object[][] data = new Object[][]{
            {
                null
            }, {
                null
            }
        };
        data[1][0] = getAlternativeMailBox();
        return Arrays.asList(data);
    }


    /**
     * Property key test.session.user.
     */
    private static final Logger LOG = LoggerFactory.getLogger(EhboxV3ServiceConsultationIntegrationTest.class);


    /**
     * Response ID.
     */


    /**
     * Test the getBoxInfo operation from the eHealthBox Service through the use of the Business Connector. The getBoxInfo retrieves general
     * info about an eHealthBox
     * 
     * The following main steps are done: - Invoke the business connector - Verify the response
     * 
     * @throws Exception
     */
    @Test
    public void testGetBoxInfo() throws Exception {
        /*
         * Invoke the business connector framework's eHealthBox's getBoxInfo operation
         */
        GetBoxInfoResponse response = ServiceFactory.getEhealthBoxServiceV3().getBoxInfo(BuilderFactory.getRequestBuilder().createBoxInfoRequest(mailBoxToUse));

        /*
         * Verify the response
         */
        // check if there are no errors
        Assert.assertEquals("100", response.getStatus().getCode());
        // check if there is any content
        Assert.assertNotNull(response.getBoxId());
    }


    /**
     * Test the getMessageList operation from the eHealthBox Service through the use of the Business Connector. This test retrieves the
     * messageId's of the message's in a given folder, correct values for the folder are: INBOX | SENT | BIN
     * 
     * The following main steps are done: - Create the parameters of the new message - Invoke the business connector - Verify the response
     * 
     * @throws Exception
     */
    @Test
    public void testGetMessageList() throws Exception {
        /*
         * Create the parameters of the new message
         */
        // set the folder (INBOX | SENTBOX | BININBOX | BINSENTBOX)
        String source = "INBOX";

        GetMessageListResponseType response = ServiceFactory.getEhealthBoxServiceV3().getMessageList(BuilderFactory.getRequestBuilder().createGetMessagesListRequest(source));

        /*
         * Verify the response
         */
        // check if there are no errors
        Assert.assertEquals("100", response.getStatus().getCode());
        // check if a messageId has been returned
        Assert.assertNotNull(response.getMessages());
        // print out the messageId's for the given folder
        List<Message> list = response.getMessages();
        LOG.info("Messages in " + source);

        for (Message message : list) {
            // JAXB.marshal(message, System.out);
            String messageId = message.getMessageId();
            try {
                be.ehealth.businessconnector.ehbox.api.domain.Message<Message> msg = BuilderFactory.getConsultationMessageBuilder().buildMessage(message);
                checkMessageIdEqualsCreatedMessage(messageId, msg, mailBoxToUse);
            } catch (Exception e) {
                LOG.info("\t" + messageId);
            }
        }
    }

    /**
     * Test the getFullMessage operation from the eHealthBox Service through the use of the Business Connector. This test retrieves a full
     * message based on a given messageId's. Usually the messageId is retrieved first from the getMessageList operation.
     * 
     * The following main steps are done: - Create the parameters of the new message - Invoke the business connector - Verify the response
     * 
     * @throws Exception
     */

    @Test
    public void testGetFullMessage() throws Exception {
        /*
         * Create the parameters of the new message
         */
        try {

            /*
             * Invoke the business connector framework's eHealthBox's getFullMessage operation
             */

            GetFullMessageRequest request = new GetFullMessageRequest();

            GetMessagesListRequest listRequest = new GetMessagesListRequest();
            listRequest.setSource("INBOX");
            GetMessageListResponseType listResponse = ServiceFactory.getEhealthBoxServiceV3().getMessageList(listRequest);
            List<Message> messages = listResponse.getMessages();
            Assert.assertFalse("no messages found", messages.isEmpty());
            request.setSource("INBOX");
            request.setMessageId(getMessageId(messages));
            EhboxTestUtilities.waitForProcessing();
            GetFullMessageResponse response = ServiceFactory.getEhealthBoxServiceV3().getFullMessage(request);

            /*
             * Verify the response
             */

            // check if the message has been returned
            Assert.assertNotNull(response.getMessage().getPublicationId());
            be.ehealth.businessconnector.ehbox.api.domain.Message<GetFullMessageResponse> msg = BuilderFactory.getConsultationMessageBuilder().buildFullMessage(response);
            if (msg instanceof DocumentMessage) {
                DocumentMessage<GetFullMessageResponse> message = (DocumentMessage<GetFullMessageResponse>) msg;
                LOG.debug(message.getDocumentTitle());
                LOG.debug(message.toString());
            } else if (msg instanceof NewsMessage) {
                NewsMessage<GetFullMessageResponse> message = (NewsMessage<GetFullMessageResponse>) msg;
                Assert.assertNotNull(message);
                LOG.debug(message.toString());
            }
        } catch (Exception e) {
            EhboxTestUtilities.logStackTrace(e);
            Assert.fail(e.getMessage());
        }
    }


    /**
     * Test the getFullMessage operation from the eHealthBox Service through the use of the Business Connector. This test retrieves a full
     * message based on a given messageId's. Usually the messageId is retrieved first from the getMessageList operation.
     * 
     * The following main steps are done: - Create the parameters of the new message - Invoke the business connector - Verify the response
     * 
     * @throws Exception
     */

    @Test
    public void testGetFullMessageDifferentMimeTypes() throws Exception {
        /*
         * Create the parameters of the new message
         */
        try {

            Addressee addresseeForBoxIdType = EhboxTestUtilities.mapBoxIdTypeToAddressee(mailBoxToUse);
            DocumentMessage<Message> fullExampleMessage = EhboxTestDataBuilder.buildFullExampleDocumentMessage(true, true, addresseeForBoxIdType);
            fullExampleMessage.getAnnexList().add(EhboxTestDataBuilder.generateDocument("DocWithmimeType text xml", "text/xml"));
            fullExampleMessage.getAnnexList().add(EhboxTestDataBuilder.generateDocument("DocWithmimeType application/octet-stream", "application/octet-stream"));
            fullExampleMessage.getAnnexList().add(EhboxTestDataBuilder.generateDocument("DocWithmimeType application/pdf", "application/pdf"));
            fullExampleMessage.getAnnexList().add(EhboxTestDataBuilder.generateDocument("DocWithmimeType text plain", "text/plain"));
            fullExampleMessage.getAnnexList().add(EhboxTestDataBuilder.generateDocument("DocWithmimeType unknown", "unknown"));
            fullExampleMessage.getDocument().setTitle("ConnectorTest : testMessageWithAnnexesInDifferentMimeTypes:boxId=" + mailBoxToUse);
            SendMessageResponse sendMessageResponse = ServiceFactory.getEhealthBoxServiceV3().sendMessage(BuilderFactory.getSendMessageBuilder().buildMessage(fullExampleMessage));
            String multipartMessageId = sendMessageResponse.getId();
            EhboxTestUtilities.waitForProcessing();

            /*
             * Invoke the business connector framework's eHealthBox's getFullMessage operation
             */

            GetFullMessageRequest request = new GetFullMessageRequest();

            GetMessagesListRequest listRequest = new GetMessagesListRequest();
            listRequest.setSource("INBOX");
            GetMessageListResponseType listResponse = ServiceFactory.getEhealthBoxServiceV3().getMessageList(listRequest);
            List<Message> messages = listResponse.getMessages();

            Assert.assertFalse("no messages found", messages.isEmpty());
            request.setSource("INBOX");

            request.setMessageId(multipartMessageId);

            EhboxTestUtilities.waitForProcessing();
            GetFullMessageResponse response = ServiceFactory.getEhealthBoxServiceV3().getFullMessage(request);

            /*
             * Verify the response
             */

            // check if the message has been returned
            Assert.assertNotNull(response.getMessage().getPublicationId());
            be.ehealth.businessconnector.ehbox.api.domain.Message<GetFullMessageResponse> msg = BuilderFactory.getConsultationMessageBuilder().buildFullMessage(response);
            if (msg instanceof DocumentMessage) {
                DocumentMessage<GetFullMessageResponse> message = (DocumentMessage<GetFullMessageResponse>) msg;
                LOG.debug(message.getDocumentTitle());
                LOG.debug(message.toString());
            } else if (msg instanceof NewsMessage) {
                NewsMessage<GetFullMessageResponse> message = (NewsMessage<GetFullMessageResponse>) msg;
                Assert.assertNotNull(message);
                LOG.debug(message.toString());
            }
        } catch (Exception e) {
            EhboxTestUtilities.logStackTrace(e);
            Assert.fail(e.getMessage());
        }
    }


    /**
     * 
     */
    private String getMessageId(List<Message> list) {
        for (Message message : list) {
            if ("DOCUMENT".equals(message.getContentSpecification().getContentType()) || "NEWS".equals(message.getContentSpecification().getContentType())) {
                return message.getMessageId();
            }
        }
        Assert.fail("no publication found, try again");
        return null;
    }

    /**
     * Test the getHistory operation from the eHealthBox Service through the use of the Business Connector. This test retrieves the history
     * based on a given messageId. the list that is returned is a list of Strings containing messageId's.
     * 
     * The following main steps are done: - Create the parameters of the new message - Invoke the business connector - Verify the response
     * 
     * @throws Exception
     */
    @Test
    public void testGetHistory() throws Exception {

        String expectedOldMessageId = null;
        /*
         * Create the parameters of the new message
         */
        GetHistoryRequest request = new GetHistoryRequest();
        // set the messageId, use the messageId of the message that has been sent in testSendMessage
        request.setMessageId(getCreatedNewsIdForMailbox(mailBoxToUse));
        expectedOldMessageId = getCreatedOldNewsIdForMailbox(mailBoxToUse);
        request.setSource("INBOX");

        /*
         * Invoke the business connector framework's eHealthBox's getFullMessage operation
         */
        GetHistoryResponse response = ServiceFactory.getEhealthBoxServiceV3().getMessageHistory(request);

        Assert.assertEquals(1, response.getMessageIds().size());
        Assert.assertEquals(expectedOldMessageId, response.getMessageIds().get(0));

        /*
         * Verify the response
         */
        // check if there are no errors
        Assert.assertEquals("100", response.getStatus().getCode());
        // check if the message has been returned
        Assert.assertNotNull(response.getMessageIds());
    }


    @Test
    public void testGetAllMessagesForAllEhboxes() throws Exception {
        String source = "SENTBOX";
        List<String> retrievedListOfIds = retrieveAllMessageIdsForAllEHboxesForSource(source);

        for (String expectedMessageId : getAllCreatedMessageIds()) {
            final boolean doesListContainMessage = retrievedListOfIds.contains(expectedMessageId);
            final String errorDescription = "expected messageId  " + expectedMessageId + "not found in list " + StringUtils.join(retrievedListOfIds.iterator(), ", ");
            if (!doesListContainMessage) {
                LOG.error(errorDescription);
            }
            Assert.assertTrue(errorDescription, doesListContainMessage);
        }
    }

    /**
     * @param source
     * @return
     * @throws ConnectorException
     * @throws TechnicalConnectorException
     */
    public List<String> retrieveAllMessageIdsForAllEHboxesForSource(String source) throws ConnectorException, TechnicalConnectorException {
        List<String> fullListOfIds = new ArrayList<String>();
        int cursor = 1;
        int increment = 99;
        GetMessageListResponseType allEhboxesMessagesList = ServiceFactory.getEhealthBoxServiceV3().getAllEhboxesMessagesList(BuilderFactory.getRequestBuilder().createAllEhboxesMessagesListRequest(source, cursor, cursor + increment));
        List<String> retrievedListOfIds = getListOfMessageIdsFromResponse(allEhboxesMessagesList);
        fullListOfIds.addAll(retrievedListOfIds);
        while (retrievedListOfIds.size() > increment) {
            cursor = cursor + increment;
            allEhboxesMessagesList = ServiceFactory.getEhealthBoxServiceV3().getAllEhboxesMessagesList(BuilderFactory.getRequestBuilder().createAllEhboxesMessagesListRequest(source, cursor, cursor + increment));
            retrievedListOfIds = getListOfMessageIdsFromResponse(allEhboxesMessagesList);
            fullListOfIds.addAll(retrievedListOfIds);
        }
        logList("ALL MAILBOXES for source " + source, fullListOfIds);
        return fullListOfIds;
    }

    /**
     * Test the moveToInbox operation from the eHealthBox Service through the use of the Business Connector. This moves a message from the
     * BIN folder to the INBOX folder based on a given messageId.
     * 
     * The following main steps are done: - Create the parameters of the new message - Invoke the business connector - Verify the response
     * 
     * @throws Exception
     */
    @Test
    public void testMoveToInbox() throws Exception {
        try {

            String messageIdToUse = null;
            List<String> originalMessagesInInbox = getListOfMessagesForSource("SENTBOX", mailBoxToUse);
            Assert.assertFalse(originalMessagesInInbox.isEmpty());
            List<String> originalMessagesInBinInbox = getListOfMessagesForSource("BINSENTBOX", mailBoxToUse);

            // ServiceFactory.getEhealthBoxServiceV3().

            messageIdToUse = getCreatedDocumentIdForMailbox(mailBoxToUse);


            Assert.assertTrue("message " + messageIdToUse + " not found in originallistOfMessages", originalMessagesInInbox.contains(messageIdToUse));
            Assert.assertFalse("message " + messageIdToUse + " found in new listOfMessages after move , should have moved", originalMessagesInBinInbox.contains(messageIdToUse));

            /*
             * Invoke the business connector framework's eHealthBox's getFullMessage operation
             */
            ResponseType response = ServiceFactory.getEhealthBoxServiceV3().moveMessage(BuilderFactory.getRequestBuilder().createMoveMessageRequest("SENTBOX", "BINSENTBOX", mailBoxToUse, messageIdToUse));

            List<String> newMessagesInInbox = getListOfMessagesForSource("SENTBOX", mailBoxToUse);
            List<String> newMessagesInBinInbox = getListOfMessagesForSource("BINSENTBOX", mailBoxToUse);
            Assert.assertFalse(newMessagesInBinInbox.isEmpty());


            Assert.assertTrue("message " + messageIdToUse + " not found in new location after move ", newMessagesInBinInbox.contains(messageIdToUse));
            Assert.assertFalse("message " + messageIdToUse + "  found in old location after move", newMessagesInInbox.contains(messageIdToUse));

            // restore previous state :
            ServiceFactory.getEhealthBoxServiceV3().moveMessage(BuilderFactory.getRequestBuilder().createMoveMessageRequest("BINSENTBOX", "SENTBOX", mailBoxToUse, messageIdToUse));

            /*
             * Verify the response
             */
            // check if there are no errors
            Assert.assertEquals("100", response.getStatus().getCode());
        } catch (Exception e) {
            EhboxTestUtilities.logStackTrace(e);
            Assert.fail(e.getMessage());
        }
    }


    @Test
    public void testGetAcknowledgement() throws Exception {
        String messageIdToUse = null;
        messageIdToUse = getCreatedDocumentIdForMailbox(mailBoxToUse);
        GetMessageAcknowledgmentsStatusResponse messageAcknowledgmentsStatusResponse = ServiceFactory.getEhealthBoxServiceV3().getMessageAcknowledgmentsStatusRequest(BuilderFactory.getRequestBuilder().createGetMessageAcknowledgmentsStatusRequest(messageIdToUse, 1, 100, mailBoxToUse));
        AcknowledgmentsStatus acknowledgmentsStatus = messageAcknowledgmentsStatusResponse.getAcknowledgmentsStatus();
        Assert.assertFalse(acknowledgmentsStatus.getRows().isEmpty());
        StringBuilder sb = new StringBuilder("messageAcknowledgmentStatus : ");
        for (Row status : acknowledgmentsStatus.getRows()) {
            sb.append("\n receiver :").append(status.getRecipient().getId()).append(status.getRecipient().getType());
            sb.append(" readTime :").append(status.getRead());
            sb.append(", published :").append(status.getPublished());
            sb.append(" received : ").append(status.getReceived());
            if (EhboxTestDataBuilder.getInssUser1().equals(status.getRecipient().getId())) {
                Assert.assertNotNull(status.getReceived());
            }
        }
        LOG.debug(sb.toString());
        Assert.assertEquals("100", messageAcknowledgmentsStatusResponse.getStatus().getCode());
    }

    @Test
    public void testProblemGetAllMessages() throws Exception {
        for (String source : EhboxTestUtilities.getListOfAllSources()) {
            List<String> listOfMessagesForSource = getListOfMessagesForSource(source, mailBoxToUse);
            List<String> retrieveAllMessageIdsForAllEHboxesForSource = retrieveAllMessageIdsForAllEHboxesForSource(source);
            for (String messageId : retrieveAllMessageIdsForAllEHboxesForSource) {
                LOG.debug("testProblemGetAllMessages : source " + source + " messageId:" + messageId);
            }
            for (String messageId : listOfMessagesForSource) {
                Assert.assertTrue(retrieveAllMessageIdsForAllEHboxesForSource.contains(messageId));
            }
        }

    }
}
