package be.ehealth.technicalconnector.service.timestamp;

import org.bouncycastle.tsp.TimeStampToken;
import org.bouncycastle.util.encoders.Base64;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3._2000._09.xmldsig.DigestMethod;

import be.ehealth.technicalconnector.enumeration.MimeType;
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.ehealth.technicalconnector.utils.ConnectorCryptoUtils;
import be.ehealth.technicalconnector.validator.impl.TimeStampValidatorFactory;
import be.fgov.ehealth.technicalconnector.tests.junit.rule.SessionRule;
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.
 * <p>
 * All Tests require the following pre-requisites to be met:
 * <ul>
 * <li>An eHealth certificate with access to the Time Stamping service</li>
 * </ul>
 * <p>
 * *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:
 * <li>Create the parameters of the new message
 * <li>Invoke the technical connector
 * <li>Verify the response
 *
 * @author EHP
 */
public class TimeStampingAuthorityIntegrationTest {

    private static final Logger LOG = LoggerFactory.getLogger(TimeStampingAuthorityIntegrationTest.class);

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


    /**
     * Test the SingRequest opeation with a base64 document as input
     *
     * @author EHP
     */
    @Test
    public void signRequestWithDocument() throws Exception {

        String requestId = IdGeneratorFactory.getIdGenerator()
                                             .generateId();
        String profile = "urn:ehealth:profiles:timestamping:1.1";

        // read out a sample file
        byte[] base64Document = Base64.encode("test".getBytes());

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

        InputDocuments inputDocuments = new InputDocuments();
        DocumentType document = new DocumentType();
        Base64Data inputDocument = new Base64Data();
        inputDocument.setMimeType(MimeType.plaintext.getValue());
        inputDocument.setValue(base64Document);
        document.setBase64Data(inputDocument);

        inputDocuments.getDocument()
                      .add(document);
        request.setInputDocuments(inputDocuments);

        invoke(request);
    }

    /**
     * Test the SingRequest opeation with the hash of the document as input
     *
     * @author EHP
     */
    @Test
    public void signRequestWithDocumentHash() throws Exception {

        String requestId = IdGeneratorFactory.getIdGenerator()
                                             .generateId();
        String profile = "urn:ehealth:profiles:timestamping:2.1";
        String hashAlgo = "SHA-256";
        String xmlEncAlgo = "http://www.w3.org/2001/04/xmlenc#sha256";

        // Calculate the hash of the sample file
        byte[] hash = ConnectorCryptoUtils.calculateDigest(hashAlgo, "test".getBytes());

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

        InputDocuments inputDocuments = new InputDocuments();
        DocumentHash document = new DocumentHash();
        DigestMethod method = new DigestMethod();
        method.setAlgorithm(xmlEncAlgo);
        document.setDigestMethod(method);
        document.setDigestValue(hash);

        inputDocuments.getDocumentHash()
                      .add(document);
        request.setInputDocuments(inputDocuments);

        invoke(request);

    }

    private static void invoke(SignRequest request) throws Exception {
        /*
         * 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"));

        // Timestamp the document
        SignResponse response = ServiceFactory.getAuthorityService()
                                              .signRequest(credential.getCertificate(), credential.getPrivateKey(), request);

        // 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());

        // validate the received timestamp against the truststore
        TimeStampToken tsToken = TimestampUtil.getTimeStampToken(response.getSignatureObject()
                                                                         .getTimestamp()
                                                                         .getRFC3161TimeStampToken());
        TimeStampValidatorFactory.getInstance()
                                 .validateTimeStampToken(tsToken);
    }

}
