package be.ehealth.technicalconnector.ws;

import be.ehealth.technicalconnector.exception.TechnicalConnectorException;
import be.ehealth.technicalconnector.service.sts.SAMLTokenFactory;
import be.ehealth.technicalconnector.service.sts.STSService;
import be.ehealth.technicalconnector.service.sts.STSServiceFactory;
import be.ehealth.technicalconnector.service.sts.domain.SAMLAttribute;
import be.ehealth.technicalconnector.service.sts.domain.SAMLAttributeDesignator;
import be.ehealth.technicalconnector.service.sts.security.Credential;
import be.ehealth.technicalconnector.service.sts.security.SAML11Token;
import be.ehealth.technicalconnector.service.sts.security.SAMLToken;
import be.ehealth.technicalconnector.service.sts.security.impl.KeyStoreCredential;
import be.ehealth.technicalconnector.utils.DateUtils;
import be.ehealth.technicalconnector.ws.GenericWsSender;
import be.ehealth.technicalconnector.ws.domain.GenericRequest;
import be.ehealth.technicalconnector.ws.domain.GenericResponse;
import be.ehealth.technicalconnector.ws.domain.TokenType;
import be.fgov.ehealth.technicalconnector.tests.junit.rule.SessionRule;
import org.joda.time.DateTime;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

import java.time.Instant;
import java.util.ArrayList;
import java.util.List;

/**
 * The Class TestGenericWsSender.
 *
 * @author EHP
 */
public class GenericWsSAMLSecuredIntegrationTest {

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

    /**
     * This operation tests the invocation of a SAML Assertion / STS Token secured web service. It is clear that as pre-requisite a valid
     * STS Token retrieved via the STS Service is required.
     * <p>
     * This test includes the following main steps: <li>1. Set the URL to the web service end-point <li>2. Requesting a new STS Token/SAML
     * Assertion via the STS Service <li>3. Creation of the body pay-load to send to the secured web service <li>4. Invoke the web service
     * and obtain the response as {@link String}
     */
    @Test
    public void testSendSAMLSecured() throws Exception {
        Assert.assertNotNull(invoke().asString());
    }

    /**
     * This operation tests the invocation of a SAML Assertion / STS Token secured web service. It is clear that as pre-requisite a valid
     * STS Token retrieved via the STS Service is required.
     * <p>
     * This test includes the following main steps: <li>1. Set the URL to the web service end-point <li>2. Requesting a new STS Token/SAML
     * Assertion via the STS Service <li>3. Creation of the body pay-load to send to the secured web service <li>4. Invoke the web service
     * and obtain the response as {@link Node}
     */
    @Test
    public void testSendSAMLSecuredDOM() throws Exception {
        Assert.assertNotNull(invoke().asNode());
    }

    /**
     * Helper method to generate and invoke the service.
     */
    private GenericResponse invoke() throws Exception {
        GenericRequest request = new GenericRequest();
        request.setEndpoint(rule.getSessionProperty("test.secured.endpoint"));
        request.setCredential(getToken(), TokenType.SAML);
        request.addDefaulHandlerChain();
        request.setPayload(getPayload());

        GenericWsSender sender = be.ehealth.technicalconnector.ws.ServiceFactory.getGenericWsSender();

        return sender.send(request);
    }

    /**
     * Helper method obtain an SAML Assertion
     */
    private SAMLToken getToken() throws TechnicalConnectorException {
        Credential authentication = new KeyStoreCredential(rule.getSessionProperty("test.keystore.location"), rule.getSessionProperty("test.keystore.alias"), rule.getSessionProperty("test.keystore.password"));
        Credential service = new KeyStoreCredential(rule.getSessionProperty("test.keystore.location"), rule.getSessionProperty("test.keystore.alias"), rule.getSessionProperty("test.keystore.password"));


        // create the list of SAML Attributes Designators
        List<SAMLAttributeDesignator> designators = new ArrayList<SAMLAttributeDesignator>();
        designators.add(new SAMLAttributeDesignator("urn:be:fgov:ehealth:1.0:certificateholder:person:ssin", "urn:be:fgov:identification-namespace"));
        designators.add(new SAMLAttributeDesignator("urn:be:fgov:person:ssin", "urn:be:fgov:identification-namespace"));

        // create the list of SAML Attributes
        List<SAMLAttribute> attributes = new ArrayList<SAMLAttribute>();
        attributes.add(new SAMLAttribute("urn:be:fgov:person:ssin", "urn:be:fgov:identification-namespace", rule.getSessionProperty("test.INSS")));
        attributes.add(new SAMLAttribute("urn:be:fgov:ehealth:1.0:certificateholder:person:ssin", "urn:be:fgov:identification-namespace", rule.getSessionProperty("test.INSS")));

        // get the token
        STSService sts = STSServiceFactory.getInstance(SAMLToken.Version.SAML1_1);
        Element assertion = sts.getToken(authentication, service, attributes, designators, SAML11Token.HOK_METHOD, 24);

        return SAMLTokenFactory.getInstance()
                .createSamlToken(assertion, service);
    }


    /**
     * Builds the payload (string) for the request.
     */
    private String getPayload() {
        StringBuilder sb = new StringBuilder();
        sb.append("<CheckBrokeredAccessControlRequest xmlns=\"urn:be:fgov:ehealth:platformintegrationconsumertest:v1\">");
        sb.append("<Message xmlns=\"urn:be:fgov:ehealth:platformintegrationconsumertest:types:v1\">Hello this is your captain speeking</Message>");
        sb.append("<Timestamp xmlns=\"urn:be:fgov:ehealth:platformintegrationconsumertest:types:v1\">");
        sb.append(DateUtils.printDateTime(DateTime.now()));
        sb.append("</Timestamp>");
        sb.append("</CheckBrokeredAccessControlRequest>");
        return sb.toString();

    }


}
