/*
 * Copyright (c) eHealth
 */
package be.fgov.ehealth.technicalconnector.signature.impl;

import java.io.File;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.transform.dom.DOMSource;

import org.joda.time.DateTime;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import be.ehealth.technicalconnector.config.ConfigFactory;
import be.ehealth.technicalconnector.exception.TechnicalConnectorException;
import be.ehealth.technicalconnector.service.etee.CertificateChecker;
import be.ehealth.technicalconnector.service.sts.security.Credential;
import be.ehealth.technicalconnector.session.Session;
import be.ehealth.technicalconnector.utils.ConnectorIOUtils;
import be.ehealth.technicalconnector.utils.ConnectorXmlUtils;
import be.fgov.ehealth.technicalconnector.signature.SignatureBuilder;
import be.fgov.ehealth.technicalconnector.signature.domain.SignatureVerificationError;
import be.fgov.ehealth.technicalconnector.signature.domain.SignatureVerificationResult;
import be.fgov.ehealth.technicalconnector.signature.domain.XadesOption;
import be.fgov.ehealth.technicalconnector.tests.junit.rule.SessionRule;
import be.fgov.ehealth.technicalconnector.tests.utils.XmlAsserter;


/**
 * Abstract class used to execute signature.
 *
 * @author EHP
 */
public abstract class AbstractSignatureTest {

    /**
     * {@link CertificateChecker} that internally 'validates' the certificate.
     *
     * @author EHP
     */
    public static class CertificateCheckerStub implements CertificateChecker {

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

        @Override
        public boolean isCertificateRevoked(File certFile) throws TechnicalConnectorException {
            LOG.warn("isCertificateRevoked: No real revocation check!");
            return false;
        }

        @Override
        public boolean isCertificateRevoked(X509Certificate cert) throws TechnicalConnectorException {
            LOG.warn("isCertificateRevoked: No real revocation check!");
            return false;
        }

        @Override
        public boolean isValidCertificateChain(List<X509Certificate> certificateChain) throws TechnicalConnectorException {
            LOG.warn("isValidCertificateChain: No real revocation check!");
            return true;
        }

        @Override
        public boolean isValidCertificateChain(List<X509Certificate> certificateChain, DateTime validOn) throws TechnicalConnectorException {
            LOG.warn("isValidCertificateChain: No real revocation check!");
            return true;
        }

        @Override
        public boolean isCertificateRevoked(File certFile, DateTime validOn) throws TechnicalConnectorException {
            LOG.warn("isCertificateRevoked: No real revocation check!");
            return false;
        }

        @Override
        public boolean isCertificateRevoked(X509Certificate cert, DateTime validOn) throws TechnicalConnectorException {
            LOG.warn("isCertificateRevoked: No real revocation check!");
            return false;
        }


    }

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

    protected static byte[] textToSign;

    protected static byte[] textToSign_bis;

    protected static Credential credential;

    protected static SignatureBuilder builder;

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

    @BeforeClass
    public static void init() throws Exception {

        Session.getInstance()
               .loadEncryptionKeys(rule.getSessionProperty("test.session.encryption.password"));
        ConfigFactory.getConfigValidator()
                     .setProperty("crypto.certificatechecker.classname", CertificateCheckerStub.class.getName());
        credential = Session.getInstance()
                            .getSession()
                            .getEncryptionCredential();

        textToSign = ConnectorIOUtils.getBytes(ConnectorIOUtils.getResourceAsStream("/examples/example.xml"));
        textToSign_bis = ConnectorIOUtils.getBytes(ConnectorIOUtils.getResourceAsStream("/examples/example_bis.xml"));

    }


    @Test(expected = Exception.class)
    public void testBuildSignatureWithEmptyCredential() throws Exception {
        builder.sign(null, textToSign);
    }

    @Test(expected = Exception.class)
    public void testBuildSignatureWithByteArrayNull() throws Exception {
        builder.sign(credential, null);
    }

    public void verifyXades(byte[] signature, byte[] signedByteArray, SignatureVerificationError... errors) throws Exception {
        String signed = new String(signedByteArray);
        Map<String, Object> options = new HashMap<String, Object>();
        options.put(XadesOption.FOLLOWNESTEDMANIFEST, Boolean.TRUE);
        SignatureVerificationResult result = builder.verify(signed.getBytes(), signature, options);
        for (SignatureVerificationError error : errors) {
            result.getErrors()
                  .remove(error);
        }
        for (SignatureVerificationError error : result.getErrors()) {
            LOG.debug(error.getErrorName());
        }
        Assert.assertTrue("Xades(T) is not valid.", result.isValid());
        DOMSource actual = new DOMSource(ConnectorXmlUtils.toDocument(new String(signature))
                                                          .getElementsByTagNameNS("http://uri.etsi.org/01903/v1.3.2#", "QualifyingProperties")
                                                          .item(0));
        DOMSource expected = new DOMSource(ConnectorXmlUtils.toDocument(ConnectorIOUtils.getResourceAsString(getVerificationTemplate())));
        XmlAsserter.assertSimilar(expected, actual);
    }

    public static void verifyCades(byte[] signature, byte[] signedByteArray, SignatureVerificationError... errors) throws Exception {

        Map<String, Object> options = new HashMap<String, Object>();
        SignatureVerificationResult result = builder.verify(signedByteArray, signature, options);
        for (SignatureVerificationError error : errors) {
            result.getErrors()
                  .remove(error);
        }
        for (SignatureVerificationError error : result.getErrors()) {
            LOG.debug(error.getErrorName());
        }
        Assert.assertTrue("Cades(T) is not valid.", result.isValid());
    }

    String getVerificationTemplate() {
        return null;
    }
}
