/*
t * Copyright (c) eHealth
 */
package be.ehealth.businessconnector.ehbox.v3.builders.impl;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.security.cert.X509Certificate;
import java.util.HashSet;
import java.util.Set;

import javax.security.auth.x500.X500Principal;

import org.easymock.EasyMock;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

import be.ehealth.businessconnector.ehbox.api.domain.Addressee;
import be.ehealth.businessconnector.ehbox.api.domain.Document;
import be.ehealth.businessconnector.ehbox.api.domain.DocumentMessage;
import be.ehealth.businessconnector.ehbox.api.domain.NewsMessage;
import be.ehealth.businessconnector.ehbox.api.utils.QualityType;
import be.ehealth.businessconnector.ehbox.v3.EhboxTestDataBuilder;
import be.ehealth.businessconnector.ehbox.v3.builders.SendMessageBuilder;
import be.ehealth.technicalconnector.service.etee.Crypto;
import be.ehealth.technicalconnector.service.etee.Crypto.SigningPolicySelector;
import be.ehealth.technicalconnector.service.etee.domain.EncryptionToken;
import be.ehealth.technicalconnector.service.keydepot.KeyDepotManagerFactory;
import be.ehealth.technicalconnector.service.keydepot.KeyDepotService;
import be.ehealth.technicalconnector.service.keydepot.impl.KeyDepotServiceImpl;
import be.ehealth.technicalconnector.service.sts.security.Credential;
import be.ehealth.technicalconnector.session.Session;
import be.ehealth.technicalconnector.session.SessionItem;
import be.ehealth.technicalconnector.session.SessionManager;
import be.ehealth.technicalconnector.utils.ConnectorIOUtils;
import be.ehealth.technicalconnector.utils.IdentifierType;
import be.ehealth.technicalconnector.utils.SessionUtil;
import be.fgov.ehealth.ehbox.consultation.protocol.v3.Message;
import be.fgov.ehealth.ehbox.publication.protocol.v3.SendMessageRequest;


/**
 * Test class for {@link SendMessageBuilderImpl}.
 * 
 * @author EHP
 * 
 */
public class SendMessageBuilderImplTest {


    private static final String ANNEX_MIME_TYPE = "annex mimeType";

    private static final String ANNEX_TITLE = "annex title";

    private static final String ANNEX_FILENAME = "annex filename";

    private static final String PATIENT_INSS = "patientInss";

    private static final String TITLE = "title";

    private static final String MIME_TYPE = "mimeType";

    private static final QualityType QUALITY = QualityType.PHARMACY_NIHII;

    private static final String ORGANIZATION_NAME = "organizationName";

    private static final String LAST_NAME = "lastName";

    private static final String ID = "1";

    private static final String FIRST_NAME = "firstName";

    private static final String APPLICATION_ID = "applicationId";

    private static final String FILENAME = "newFilename";

    private static final String FREE_TEXT = "freeText";

    private Credential encryptionCredential;

    private SendMessageBuilder builder;

    private KeyDepotService mockKeyDepotService;

    private static Set<EncryptionToken> etkSet;

    private X509Certificate cert;

    private SessionItem mockSession;

    private SessionManager mockSessionManager;

    private Crypto mockCrypto;

    /**
     * Reinitialise the KeyDepotService with a new one and EncryptionCredentials with null.
     * 
     * @throws Exception
     */
    @AfterClass
    public static void tearDown() throws Exception {
        KeyDepotManagerFactory.getKeyDepotManager().setKeyDepotService(new KeyDepotServiceImpl());
        Session.setSessionManager(null);
    }


    /**
     * Creates required mocks.
     * 
     * @throws java.lang.Exception
     */
    @Before
    public void setUp() throws Exception {
        encryptionCredential = EasyMock.createMock(Credential.class);
        mockCrypto = EasyMock.createMock(Crypto.class);
        mockKeyDepotService = EasyMock.createMock(KeyDepotService.class);
        mockSession = EasyMock.createMock(SessionItem.class);
        mockSessionManager = EasyMock.createMock(SessionManager.class);

        Session.setSessionManager(mockSessionManager);

        EasyMock.expect(mockSessionManager.getSession()).andReturn(mockSession).anyTimes();
        EasyMock.expect(mockSessionManager.hasValidSession()).andReturn(true).anyTimes();
        EasyMock.expect(mockSession.getEncryptionCrypto()).andReturn(mockCrypto).anyTimes();
        EasyMock.expect(mockSession.getHolderOfKeyCrypto()).andReturn(mockCrypto).anyTimes();
        EasyMock.expect(mockSession.getEncryptionCredential()).andReturn(encryptionCredential).anyTimes();
        EasyMock.expect(mockSession.getHolderOfKeyCredential()).andReturn(encryptionCredential).anyTimes();

        KeyDepotManagerFactory.getKeyDepotManager().setKeyDepotService(mockKeyDepotService);
        builder = new SendMessageBuilderImpl(KeyDepotManagerFactory.getKeyDepotManager());
    }

    /**
     * Test method for
     * {@link be.ehealth.businessconnector.ehboxV2.builders.impl.SendMessageBuilderImpl#buildMessage(be.ehealth.businessconnector.ehbox.api.domain.DocumentMessage)}
     * .
     * 
     * @throws Exception
     */
    @Test
    public void testBuildMessageDocumentMessage() throws Exception {

        DocumentMessage<Message> message = new DocumentMessage<Message>();
        message.setFreeText(FREE_TEXT);

        message.setPatientInss(PATIENT_INSS);

        // destination
        Addressee destination = new Addressee(QUALITY.getIdentifierType());
        destination.setApplicationId(APPLICATION_ID);
        destination.setFirstName(FIRST_NAME);
        destination.setId(ID);
        destination.setLastName(LAST_NAME);
        destination.setOrganizationName(ORGANIZATION_NAME);
        destination.setQuality(QUALITY);
        message.getDestinations().add(destination);

        //
        message.setImportant(true);
        message.setEncrypted(true);

        // document
        message.setDocument(new Document());
        message.getDocument().setFilename(FILENAME);
        message.getDocument().setMimeType(MIME_TYPE);
        message.getDocument().setTitle(TITLE);
        message.getDocument().setContent("blabla".getBytes());

        // annex
        Document annex = new Document();
        annex.setFilename(ANNEX_FILENAME);
        annex.setMimeType(ANNEX_MIME_TYPE);
        annex.setTitle(ANNEX_TITLE);
        annex.setContent("blabla".getBytes());

        message.getAnnexList().add(annex);

        // Setup Easymock

        if (message.isEncrypted()) {
            IdentifierType identifierTypeHelper = destination.getIdentifierTypeHelper();
            EasyMock.expect(mockKeyDepotService.getETKSet(identifierTypeHelper, identifierTypeHelper.formatIdentifierValue(destination.getId()), destination.getApplicationId())).andReturn(prepareEtkSet());
            EasyMock.expect(mockCrypto.seal(EasyMock.eq(SigningPolicySelector.WITH_NON_REPUDIATION), EasyMock.eq(prepareEtkSet()), (byte[]) EasyMock.anyObject())).andReturn("freeText".getBytes()).anyTimes();
            cert = EasyMock.createMock(X509Certificate.class);
            EasyMock.expect(encryptionCredential.getCertificate()).andReturn(cert).anyTimes();
            String subject = "CN=SSIN=" + EhboxTestDataBuilder.getInssUser1() + ", OU=eHealth-platform Belgium, OU=HANNES DE CLERCQ, OU=SSIN=" + EhboxTestDataBuilder.getInssUser1() + ",O=Federal Government, C=BE";
            EasyMock.expect(cert.getSubjectX500Principal()).andReturn(new X500Principal(subject));
            EasyMock.expect(mockKeyDepotService.getETKSet(EasyMock.eq(IdentifierType.SSIN), EasyMock.eq(EhboxTestDataBuilder.getInssUser1()), EasyMock.eq(""))).andReturn(prepareEtkSet());
        }
        EasyMock.replay(mockKeyDepotService, mockCrypto, encryptionCredential, cert, mockSession, mockSessionManager);
        // build message
        SendMessageRequest result = builder.buildMessage(message);
        Assert.assertNotNull(result);

        Assert.assertNotNull(FREE_TEXT, result.getContentContext().getContent().getFreeInformations().getEncryptableFreeText());
        Assert.assertNotNull(PATIENT_INSS, result.getContentContext().getContent().getEncryptableINSSPatient());

        Assert.assertTrue(result.getContentContext().getContentSpecification().isIsEncrypted());
        Assert.assertTrue(result.getContentContext().getContentSpecification().isIsImportant());

        // check destination
        Assert.assertEquals(1, result.getDestinationContexts().size());
        Assert.assertEquals(ID, result.getDestinationContexts().get(0).getId());
        Assert.assertEquals(QUALITY.getQuality(), result.getDestinationContexts().get(0).getQuality());

        // check document
        Assert.assertEquals(FILENAME, result.getContentContext().getContent().getDocument().getDownloadFileName());
        Assert.assertEquals(MIME_TYPE, result.getContentContext().getContent().getDocument().getMimeType());
        Assert.assertEquals(TITLE, result.getContentContext().getContent().getDocument().getTitle());

        // check annexes
        Assert.assertEquals(ANNEX_FILENAME, result.getContentContext().getContent().getAnnices().get(0).getDownloadFileName());
        Assert.assertEquals(ANNEX_MIME_TYPE, result.getContentContext().getContent().getAnnices().get(0).getMimeType());

        EasyMock.verify(mockKeyDepotService, mockCrypto);

    }

    /**
     * Test method for
     * {@link be.ehealth.businessconnector.ehboxV2.builders.impl.SendMessageBuilderImpl#buildMessage(be.ehealth.businessconnector.ehbox.api.domain.NewsMessage)}
     * .
     * 
     * @throws Exception
     */
    @Test
    public void testBuildMessageNewsMessage() throws Exception {

        NewsMessage<Message> message = new NewsMessage<Message>();
        Document news = new Document();

        // destination
        Addressee destination = new Addressee(QUALITY.getIdentifierType());

        destination.setApplicationId(APPLICATION_ID);
        destination.setFirstName(FIRST_NAME);
        destination.setId(ID);
        destination.setLastName(LAST_NAME);
        destination.setOrganizationName(ORGANIZATION_NAME);
        destination.setQuality(QUALITY);


        message.getDestinations().add(destination);

        news.setTitle(FILENAME);
        message.setDocument(news);
        news.setContent("blabla".getBytes());


        if (message.isEncrypted()) {
            EasyMock.expect(mockKeyDepotService.getETKSet(destination.getIdentifierTypeHelper(), destination.getId(), destination.getApplicationId())).andReturn(prepareEtkSet());
            EasyMock.expect(mockCrypto.seal(EasyMock.eq(SigningPolicySelector.WITHOUT_NON_REPUDIATION), EasyMock.eq(prepareEtkSet()), EasyMock.aryEq(toByteArray(message.getDocument().getContent())))).andReturn("freeText".getBytes());
        }
        EasyMock.replay(mockKeyDepotService, mockCrypto, mockSession, mockSessionManager);

        SendMessageRequest result = builder.buildMessage(message);
        Assert.assertNotNull(result);
        Assert.assertEquals(FILENAME, result.getContentContext().getContent().getDocument().getTitle());

        EasyMock.verify(mockKeyDepotService, SessionUtil.getEncryptionCrypto());
    }


    /**
     * Returns EtkSet and initialise it with kgss.etk file if not already done
     * 
     * @return
     * @throws Exception
     */
    private Set<EncryptionToken> prepareEtkSet() throws Exception {
        if (etkSet == null) {
            InputStream is = SendMessageBuilderImplTest.class.getResourceAsStream("/etee/kgss.etk");

            byte[] etkByteArray = ConnectorIOUtils.getBytes(is);

            etkSet = new HashSet<EncryptionToken>();
            etkSet.add(new EncryptionToken(etkByteArray));
        }
        return etkSet;
    }

    /**
     * Tranforms a content object into a byte[] using OutputStreams
     * 
     * @param content
     * @return
     * @throws Exception
     */
    private byte[] toByteArray(Object content) throws Exception {
        ByteArrayOutputStream bStream = new ByteArrayOutputStream();
        ObjectOutputStream oStream = new ObjectOutputStream(bStream);
        oStream.writeObject(content);
        byte[] byteVal = bStream.toByteArray();
        return byteVal;
    }
}
