package be.ehealth.businessconnector.test.mycarenet.memberdatav2.session;

import be.ehealth.businessconnector.mycarenet.memberdatav2.builders.ResponseObjectBuilder;
import be.ehealth.businessconnector.mycarenet.memberdatav2.builders.ResponseObjectBuilderFactory;
import be.ehealth.businessconnector.mycarenet.memberdatav2.domain.MemberDataBuilderResponse;
import be.ehealth.businessconnector.mycarenet.memberdatav2.session.MemberDataService;
import be.ehealth.businessconnector.mycarenet.memberdatav2.session.MemberDataSessionServiceFactory;
import be.ehealth.businessconnector.test.mycarenet.memberdatav2.session.builders.BuilderStrategy;
import be.ehealth.businessconnector.test.testcommons.utils.FileTestUtils;
import be.ehealth.technicalconnector.utils.ConnectorIOUtils;
import be.fgov.ehealth.mycarenet.memberdata.protocol.v1.MemberDataConsultationRequest;
import be.fgov.ehealth.mycarenet.memberdata.protocol.v1.MemberDataConsultationResponse;
import be.fgov.ehealth.technicalconnector.signature.domain.SignatureVerificationResult;
import be.fgov.ehealth.technicalconnector.tests.junit.rule.SessionRule;
import be.fgov.ehealth.technicalconnector.tests.utils.XmlAsserter;
import oasis.names.tc.saml._2_0.protocol.AttributeQuery;
import org.apache.commons.io.IOUtils;
import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

import java.io.InputStream;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

@RunWith(Parameterized.class)
public class PharmacyIntegrationTest extends AbstractIntegrationTest {

    private static final String REQUEST_LOCATION_HAPPY = "/scenarios/pharmacy-request-happy.xml";

    private static final String REQUEST_LOCATION_UNHAPPY = "/scenarios/pharmacy-request-unhappy.xml";

    private static final String RESPONSE_LOCATION_HAPPY = "/scenarios/pharmacy-response-happy.xml";

    private static final String RESPONSE_LOCATION_UNHAPPY = "/scenarios/pharmacy-response-unhappy.xml";

    private static final String PHARMACIST_NISS = "82051234978";

    private static final String PHARMACY_NIHII = "21462635001";

    @ClassRule
    public static SessionRule rule = SessionRule.withActiveSession().forEnvironment("acc").forProfession("orgpharmacy").forUser("hannes").build();

    @Parameterized.Parameters(name = "{0} : isHappy({1}) : isEncrypted({2})")
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][]{
            {"89-PHA-EMEH", true, true},
            {"90-PHA-EMCH", true, true},
            {"92-PHA-CMEH", true, false},
            {"93-PHA-CMCH", true, false},
            {"91-PHA-EMEB", false, true},
            {"94-PHA-CMCB", false, false},
        });
    }


    private boolean isEncrypted;

    private boolean isHappy;

    private String id;

    public PharmacyIntegrationTest(String id, boolean isHappy, boolean isEncrypted) {
        this.isEncrypted = isEncrypted;
        this.isHappy = isHappy;
        this.id = id;
    }

    @Test
    public void consultationTest() throws Exception {
        BuilderStrategy strategy = isEncrypted? ENCRYPTED_STRATEGY : UNENCRYPTED_STRATEGY;

        String requestLocation;
        String responseLocation;

        if (isHappy) {
            requestLocation = REQUEST_LOCATION_HAPPY;
            responseLocation = RESPONSE_LOCATION_HAPPY;
        } else {
            requestLocation = REQUEST_LOCATION_UNHAPPY;
            responseLocation = RESPONSE_LOCATION_UNHAPPY;
        }

        Map<String, Object> testFileParams = new HashMap<String, Object>();
        testFileParams.put("id", id);
        testFileParams.put("nihii", PHARMACY_NIHII);
        testFileParams.put("niss", PHARMACIST_NISS);

        AttributeQuery attributeQuery = FileTestUtils.toObject(testFileParams, requestLocation, AttributeQuery.class);

        MemberDataConsultationRequest memberDataRequest = buildRequest(id, strategy, attributeQuery);

        MemberDataService service = MemberDataSessionServiceFactory.getMemberDataSyncService();
        MemberDataConsultationResponse wsResponse = service.consultMemberData(memberDataRequest);

        ResponseObjectBuilder responseBuilder = ResponseObjectBuilderFactory.getResponseObjectBuilder();
        MemberDataBuilderResponse response = responseBuilder.handleConsultationResponse(wsResponse);

        System.out.println("RESPONSE IS: " + new String(response.getResponse(), "UTF-8"));

        boolean isValid = verifySignatures(response);
        if (isHappy) Assert.assertTrue("Signature(s) should be valid", isValid);
        else Assert.assertFalse("Signature(s) should be invalid", isValid);

        InputStream is = ConnectorIOUtils.getResourceAsStream(responseLocation);
        String expectedresponse = ConnectorIOUtils.convertStreamToString(is);
        IOUtils.closeQuietly(is);

        byte[] details = response.getResponse();

        XmlAsserter.assertSimilar(expectedresponse, new String(details, "UTF-8"));
    }

    private boolean verifySignatures(MemberDataBuilderResponse response) {
        Map<String, SignatureVerificationResult> signVerifResult = response.getSignatureVerificationResult();

        if (signVerifResult.isEmpty()) return false; // we should always have signatures
        
        assertEquals(2, signVerifResult.entrySet().size());

        String xpath01 = "/*[local-name() = 'Response' and namespace-uri()='urn:oasis:names:tc:SAML:2.0:protocol']/*[local-name() = 'Assertion' and namespace-uri()='urn:oasis:names:tc:SAML:2.0:assertion'][2]/*[local-name() = 'Signature' and namespace-uri()='http://www.w3.org/2000/09/xmldsig#']";
        assertNotNull(signVerifResult.get(xpath01));

        String xpath02 = "/*[local-name() = 'Response' and namespace-uri()='urn:oasis:names:tc:SAML:2.0:protocol']/*[local-name() = 'Assertion' and namespace-uri()='urn:oasis:names:tc:SAML:2.0:assertion'][3]/*[local-name() = 'Signature' and namespace-uri()='http://www.w3.org/2000/09/xmldsig#']";
        assertNotNull(signVerifResult.get(xpath02));

        for (Map.Entry<String, SignatureVerificationResult> entry : signVerifResult.entrySet()) {
            if (!entry.getValue().isValid()) return false; // at least one signature is invalid
        }

        return true; // all signatures are valid
    }

}
