package be.ehealth.businessconnector.test.mycarenet.memberdataasync;

import be.ehealth.business.mycarenetdomaincommons.domain.InputReference;
import be.ehealth.businessconnector.genericasync.domain.GetRequest;
import be.ehealth.businessconnector.genericasync.domain.ProcessedGetResponse;
import be.ehealth.businessconnector.genericasync.domain.ProcessedMsgResponse;
import be.ehealth.businessconnector.mycarenet.memberdataasync.session.MemberDataSessionServiceFactory;
import be.ehealth.technicalconnector.exception.ConnectorException;
import be.ehealth.technicalconnector.exception.TechnicalConnectorException;
import be.ehealth.technicalconnector.utils.ConnectorIOUtils;
import be.fgov.ehealth.technicalconnector.tests.utils.XmlAsserter;
import org.apache.commons.io.IOUtils;
import org.junit.Assert;
import org.junit.FixMethodOrder;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runners.MethodSorters;

import javax.xml.ws.soap.SOAPFaultException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.TimeUnit;

import static org.junit.Assert.fail;


/**
 * @author EH071
 */
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public abstract class AbstractAsyncIntegrationTest {

    @Rule
    public ExpectedException exceptionRule = ExpectedException.none();

    private static final String REQUEST_FOLDER = "/examples/memberdata/async/request/";

    private static final String EXPECTED_FOLDER = "/examples/memberdata/async/response/";

    private static final String VALID_REQUEST = "anh-request-detail.xml";

    private static final String VALID_RESPONSE = "anh-response-detail.xml";

    private static final String INVALID_REQUEST = "ans-request-detail.xml";

    private static final int SLEEP_TIME = 70;

    private String enehID;

    private String enxsID;

    private String cnchID;

    private String cnxsID;

    public AbstractAsyncIntegrationTest(String enehID, String enxsID, String cnchID, String cnxsID) {
        this.enehID = enehID;
        this.enxsID = enxsID;
        this.cnchID = cnchID;
        this.cnxsID = cnxsID;
    }

    @Test
    public void postMDAEncryptedWithSoapFault() throws ConnectorException, InterruptedException {
        testInvalidPost(enxsID, true);
    }

    @Test
    public void postMDAEncrypted() throws ConnectorException, InterruptedException, UnsupportedEncodingException {
        testValidPost(enehID, true);
    }

    @Test
    public void postMDAWithSoapFault() throws ConnectorException, InterruptedException {
        testInvalidPost(cnxsID, false);
    }

    @Test
    public void postMDA() throws ConnectorException, InterruptedException, UnsupportedEncodingException {
        testValidPost(cnchID, false);
    }

    private void testValidPost(String id, boolean isEncrypted) throws ConnectorException, InterruptedException, UnsupportedEncodingException {
        confirmAllMessages(getResponse()); // we clean everything first
        postRequest(id, VALID_REQUEST, isEncrypted);
        ProcessedGetResponse<byte[]> response = getResponse();
        confirmAllMessages(response);
        verifyResponse(response);
    }

    private void testInvalidPost(String id, boolean isEncrypted) throws ConnectorException, InterruptedException {
        exceptionRule.expect(SOAPFaultException.class);
        exceptionRule.expectMessage("Error when calculating message weight.");
        postRequest(id, INVALID_REQUEST, isEncrypted);
    }

    private void verifyResponse(ProcessedGetResponse<byte[]> response) throws TechnicalConnectorException, UnsupportedEncodingException {
        InputStream is = ConnectorIOUtils.getResourceAsStream(EXPECTED_FOLDER + VALID_RESPONSE);
        String expectedresponse = ConnectorIOUtils.convertStreamToString(is);
        IOUtils.closeQuietly(is);

        Assert.assertFalse(response.getMsgResponses().isEmpty());
        String actualResponse = new String(response.getMsgResponses().get(0).getBusinessResponse(),"UTF-8");
        XmlAsserter.assertSimilar(expectedresponse,actualResponse);
    }

    private void postRequest(String id, String requestXml, boolean isEncrypted) throws ConnectorException, InterruptedException {
        byte[] bytes = ConnectorIOUtils.getResourceAsByteArray(REQUEST_FOLDER + requestXml);
        InputReference inputReference = new InputReference(id);
        if (isEncrypted) {
            MemberDataSessionServiceFactory.getMemberDataService().postEncryptedMDAXml(bytes, inputReference);
        } else {
            MemberDataSessionServiceFactory.getMemberDataService().postMDAXml(bytes, inputReference);
        }
        waitSomeTimeBeforeProceeding();
    }

    private ProcessedGetResponse<byte[]> getResponse() throws ConnectorException, InterruptedException {
        ProcessedGetResponse<byte[]> response = null;
        try {
            response = MemberDataSessionServiceFactory.getMemberDataService().getMDAXml(GetRequest.newBuilder().withDefaults().build());
        } catch (SOAPFaultException e) {
            if (e.getFault().getFaultString().equals("Not a supported care provider")) {
                fail(e.getFault().getFaultString());
            } else {
                throw e;
            }
        }
        waitSomeTimeBeforeProceeding();
        return response;
    }

    private void confirmAllMessages(ProcessedGetResponse<byte[]> response) throws ConnectorException, InterruptedException{
        for (ProcessedMsgResponse msgResponse : response.getMsgResponses()) {
            MemberDataSessionServiceFactory.getMemberDataService().confirmMessage(msgResponse);
            waitSomeTimeBeforeProceeding();
        }
    }

    private void waitSomeTimeBeforeProceeding() throws InterruptedException {
        TimeUnit.SECONDS.sleep(SLEEP_TIME);
    }

}
