package be.ehealth.technicalconnector.service.timestamp;

import org.bouncycastle.tsp.TimeStampToken;
import org.joda.time.DateTime;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;

import be.ehealth.technicalconnector.idgenerator.IdGeneratorFactory;
import be.ehealth.technicalconnector.service.ServiceFactory;
import be.ehealth.technicalconnector.service.sts.security.Credential;
import be.ehealth.technicalconnector.service.sts.security.impl.KeyStoreCredential;
import be.fgov.ehealth.technicalconnector.tests.junit.rule.SessionRule;
import be.fgov.ehealth.timestamping.protocol.v2.*;
import oasis.names.tc.dss._1_0.core.schema.*;

/**
 * Time Stamping Service Integration Tests This test shows the use of the Time Stamping Consult Service through the use of the Technical
 * Connector.
 * 
 * All Tests require the following pre-requisites to be met: - An eHealth certificate with access to the Time Stamping service
 * 
 * @author EHP
 */
public class TimeStampingConsultationIntegrationTest {

    @ClassRule
    public static SessionRule rule = SessionRule.withActiveSession()
                                                .build();

    /**
     * Test the SignRequest operation from the Time Stamping Authority Service through the use of the Technical Connector. This test creates
     * a time stamp for a given document which can be used later to validate the document.
     * 
     * The following main steps are done: <lu> <li>Create the parameters of the new message</li> <li>Invoke the technical connector</li> <li>
     * Verify the response</li> </lu>
     * 
     */
    public byte[] obtainRFC3161Token() throws Exception {

        String requestId = IdGeneratorFactory.getIdGenerator()
                                             .generateId();
        String profile = "urn:ehealth:profiles:timestamping:1.1";
        byte[] content = "test".getBytes();
        String mimeType = "text/plain";

        SignRequest request = new SignRequest();
        request.setRequestID(requestId);
        request.setProfile(profile);

        // input document (can be either documents or document hashes)
        InputDocuments inputDocuments = new InputDocuments();
        DocumentType document = new DocumentType();
        Base64Data inputDocument = new Base64Data();
        inputDocument.setMimeType(mimeType);
        inputDocument.setValue(content);

        // set the base64 encoded document in the document element
        document.setBase64Data(inputDocument);

        // add the document element to the list of documents to be timestamped
        inputDocuments.getDocument()
                      .add(document);

        // add the input document object to the request
        request.setInputDocuments(inputDocuments);

        /*
         * Invoke the technical connector framework's Time Stamping Authority Service's SignRequest operation
         */
        // get the X509 certificate and private key from the test keystore. (retrieved from property file)
        Credential credential = new KeyStoreCredential(rule.getSessionProperty("test.timestamping.location"), rule.getSessionProperty("test.timestamping.alias"), rule.getSessionProperty("test.timestamping.password"));

        SignResponse response = ServiceFactory.getAuthorityService()
                                              .signRequest(credential.getCertificate(), credential.getPrivateKey(), request);

        /*
         * Verify the response
         */
        // check if response has been received
        Assert.assertNotNull(response);
        // check if the timestamping was a success
        Assert.assertEquals("urn:oasis:names:tc:dss:1.0:resultmajor:Success", response.getResult()
                                                                                      .getResultMajor());

        // retrieve the timestamping token from the response
        return response.getSignatureObject()
                       .getTimestamp()
                       .getRFC3161TimeStampToken();
    }

    /**
     * Test the GetTimeStamp operation from the Time Stamping Consult Service through the use of the Technical Connector. This test
     * retrieves a time stamp from eHealth that has been created for a given hospital, for a given sequenceNr on a given time
     * 
     * The following main steps are done: - Create the parameters of the new message - Invoke the technical connector - Verify the response
     */
    @Test
    public void testGetTimeStamp() throws Exception {

        String idHospital = rule.getSessionProperty("test.timestamping.tsclientid");
        TimeStampToken timestampToken = TimestampUtil.getTimestamp(obtainRFC3161Token());
        long time = timestampToken.getTimeStampInfo()
                                  .getGenTime()
                                  .getTime();
        String seqNr = timestampToken.getTimeStampInfo()
                                     .getSerialNumber()
                                     .toString();


        TSConsultTSBagRequest request = new TSConsultTSBagRequest();
        request.setIDHospital(idHospital);

        TimeStampIdentification tsIdent = new TimeStampIdentification();
        tsIdent.setDateTime(time);
        tsIdent.setSequenceNumber(seqNr);
        request.getTSLists()
               .add(tsIdent);

        /*
         * Invoke the technical connector framework's Time Stamp Consult Service's getTimestamp operation
         */
        Credential credential = new KeyStoreCredential(rule.getSessionProperty("test.timestamping.location"), rule.getSessionProperty("test.timestamping.alias"), rule.getSessionProperty("test.timestamping.password"));

        // Retrieve the timestamp of the document
        TSConsultTSBagResponse response = ServiceFactory.getConsultServiceV2()
                                                        .getTimestamp(credential.getCertificate(), credential.getPrivateKey(), request);

        /*
         * Verify the response
         */
        // Check if response has been received
        Assert.assertNotNull(response);
        Assert.assertNotNull(response.getTSBags());
        Assert.assertFalse(response.getTSBags()
                                   .isEmpty());
        Assert.assertEquals("urn:oasis:names:tc:dss:1.0:resultmajor:Success", response.getTSBags()
                                                                                      .get(0)
                                                                                      .getSignResponse()
                                                                                      .getResult()
                                                                                      .getResultMajor());
        // this is byte array representation of the document that has been timestamped
        Assert.assertNotNull(response.getTSBags()
                                     .get(0)
                                     .getTSBagValue());
        // Check if the Time Stamp Token is present
        Assert.assertNotNull(response.getTSBags()
                                     .get(0)
                                     .getSignResponse()
                                     .getSignatureObject());
    }

    /**
     * Test the CheckCompleteness operation from the Time Stamping Consult Service through the use of the Technical Connector. This test
     * checks if all time stamps that you request are in the eHealth repository.
     * 
     * The following main steps are done: - Create the parameters of the new message - Invoke the technical connector - Verify the response
     */
    @Test
    public void testCheckCompleteness() throws Exception {

        String idHospital = rule.getSessionProperty("test.timestamping.tsclientid");
        // get the time on which the time stamp was generated
        TimeStampToken timestampToken = TimestampUtil.getTimestamp(obtainRFC3161Token());
        long timestamp = timestampToken.getTimeStampInfo()
                                       .getGenTime()
                                       .getTime();
        // get the sequence number of the timestamp
        String seqNr = timestampToken.getTimeStampInfo()
                                     .getSerialNumber()
                                     .toString();

        // Set the period between which to check the completeness of the timestamps. In this example we will check all timestamps from the
        // past minute

        DateTime startDate = new DateTime(timestamp);
        DateTime endDate = new DateTime(timestamp);
        endDate.plusMinutes(1);

        TSConsultRequest request = new TSConsultRequest();
        request.setIDHospital(idHospital);
        PeriodType periodType = new PeriodType();
        periodType.setStart(startDate.getMillis());
        periodType.setEnd(endDate.getMillis());
        request.setPeriod(periodType);

        // The Time Stamp Identification: the time of the timestamp and the sequence number
        TimeStampIdentification tsIdent = new TimeStampIdentification();
        tsIdent.setDateTime(timestamp);
        tsIdent.setSequenceNumber(seqNr);
        request.getTSLists()
               .add(tsIdent);

        /*
         * Invoke the technical connector framework's Time Stamp Consult Service's checkCompleteness operation
         */
        // get the X509 certificate and private key from the test keystore. (retrieved from property file)
        Credential credential = new KeyStoreCredential(rule.getSessionProperty("test.timestamping.location"), rule.getSessionProperty("test.timestamping.alias"), rule.getSessionProperty("test.timestamping.password"));

        // Check if the timestamps are in the eHealth repository
        TSConsultResponse response = ServiceFactory.getConsultServiceV2()
                                                   .checkCompleteness(credential.getCertificate(), credential.getPrivateKey(), request);

        /*
         * Verify the response
         */
        // Check if response has been received
        Assert.assertNotNull(response);
        // Check if the timestamp could be retrieved
        Assert.assertEquals("OK", response.getStatus());
    }
}
