/*
 * Copyright (c) eHealth
 */
package be.ehealth.businessconnector.wsconsent.service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.joda.time.DateTime;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.SAXException;

import be.ehealth.business.kmehrcommons.CDConsentBuilderUtil;
import be.ehealth.business.kmehrcommons.helper.ErrorTypeManagement;
import be.ehealth.businessconnector.wsconsent.builders.RequestObjectBuilderFactory;
import be.ehealth.businessconnector.wsconsent.exception.WsConsentBusinessConnectorException;
import be.ehealth.businessconnector.wsconsent.session.WsConsentSessionServiceFactory;
import be.ehealth.businessconnector.wsconsent.utils.TestUtils;
import be.ehealth.technicalconnector.exception.ConnectorException;
import be.ehealth.technicalconnector.exception.SessionManagementException;
import be.ehealth.technicalconnector.exception.TechnicalConnectorException;
import be.ehealth.technicalconnector.session.Session;
import be.fgov.ehealth.hubservices.core.v2.AuthorWithPatientType;
import be.fgov.ehealth.hubservices.core.v2.ConsentType;
import be.fgov.ehealth.hubservices.core.v2.GetPatientConsentRequest;
import be.fgov.ehealth.hubservices.core.v2.GetPatientConsentResponse;
import be.fgov.ehealth.hubservices.core.v2.PatientIdType;
import be.fgov.ehealth.hubservices.core.v2.PutPatientConsentRequest;
import be.fgov.ehealth.hubservices.core.v2.PutPatientConsentResponse;
import be.fgov.ehealth.hubservices.core.v2.RevokePatientConsentRequest;
import be.fgov.ehealth.hubservices.core.v2.RevokePatientConsentResponse;
import be.fgov.ehealth.hubservices.core.v2.SelectGetPatientConsentType;
import be.fgov.ehealth.standards.kmehr.cd.v1.CDCONSENT;
import be.fgov.ehealth.standards.kmehr.cd.v1.CDCONSENTvalues;
import be.fgov.ehealth.standards.kmehr.id.v1.IDHCPARTY;
import be.fgov.ehealth.standards.kmehr.id.v1.IDHCPARTYschemes;
import be.fgov.ehealth.standards.kmehr.schema.v1.ErrorType;
import be.fgov.ehealth.standards.kmehr.schema.v1.HcpartyType;
import be.fgov.ehealth.technicalconnector.tests.session.SessionInitializer;
import be.fgov.ehealth.technicalconnector.tests.utils.XmlAsserter;


/**
 * integration test for the wsconsent connector. will test some scenarios.
 * 
 * @author EHP
 * 
 * 
 * 
 */
public abstract class AbstractWsConsentServiceIntegrationTest {

    protected static final Logger LOG = LoggerFactory.getLogger(AbstractWsConsentServiceIntegrationTest.class);


    @BeforeClass
    public static void init() throws Exception {
        SessionInitializer.init("/be.ehealth.businessconnector.wsconsent.test.properties");
    }

    @AfterClass
    public static void tearDown() throws Exception {
        Session.getInstance().unloadSession();
    }

    /**
     * @return
     * @throws TechnicalConnectorException
     * @throws WsConsentBusinessConnectorException
     * @throws InstantiationException
     * @throws SessionManagementException
     */
    protected PutPatientConsentResponse putConsentForCurrentPatient(ConsentType consentType) throws TechnicalConnectorException, WsConsentBusinessConnectorException, InstantiationException, SessionManagementException {
        PutPatientConsentRequest consentRequest = RequestObjectBuilderFactory.getRequestObjectBuilder().createPutRequest(getAuthor(), consentType);
        TestUtils.logJaxbObject(consentRequest);
        // Service
        WsConsentService service = ServiceFactory.getWsConsentService();
        PutPatientConsentResponse response = service.putPatientConsent(Session.getInstance().getSession().getSAMLToken(), consentRequest);
        return response;
    }


    /**
     * @return
     * @throws TechnicalConnectorException
     * @throws WsConsentBusinessConnectorException
     * @throws InstantiationException
     */
    protected AuthorWithPatientType getAuthor() throws TechnicalConnectorException, WsConsentBusinessConnectorException, InstantiationException {
        AuthorWithPatientType author = RequestObjectBuilderFactory.getAuthorBuilder().createAuthor();
        return author;
    }


    /**
     * @return
     * @throws TechnicalConnectorException
     */
    protected PatientIdType getPatient() throws TechnicalConnectorException {
        PatientIdType patient = RequestObjectBuilderFactory.getPatientInfoBuilder().readFromEidCard();
        return patient;
    }


    /**
     * @return
     * @throws TechnicalConnectorException
     * @throws WsConsentBusinessConnectorException
     * @throws InstantiationException
     * @throws SessionManagementException
     */
    protected GetPatientConsentResponse retrievePatientConsentResponse() throws TechnicalConnectorException, WsConsentBusinessConnectorException, InstantiationException, SessionManagementException {
        List<CDCONSENT> consentList = new ArrayList<CDCONSENT>();
        consentList.add(CDConsentBuilderUtil.createCDConsent("1.0", CDCONSENTvalues.RETROSPECTIVE));

        SelectGetPatientConsentType consentType = RequestObjectBuilderFactory.getConsentBuilder().createSelectGetPatientConsent(getPatient(), consentList);
        AuthorWithPatientType author = getAuthor();

        GetPatientConsentRequest consentRequest = RequestObjectBuilderFactory.getRequestObjectBuilder().createGetRequest(author, consentType);

        TestUtils.logJaxbObject(consentRequest);
        // Service
        WsConsentService service = ServiceFactory.getWsConsentService();
        GetPatientConsentResponse response = service.getPatientConsent(Session.getInstance().getSession().getSAMLToken(), consentRequest);

        TestUtils.logJaxbObject(response);
        return response;
    }


    /**
     * @return
     * @throws TechnicalConnectorException
     * @throws WsConsentBusinessConnectorException
     * @throws InstantiationException
     * @throws SessionManagementException
     */
    protected RevokePatientConsentResponse revokePatientconsent(ConsentType consentType) throws TechnicalConnectorException, WsConsentBusinessConnectorException, InstantiationException, SessionManagementException {

        consentType.setRevokedate(new DateTime());
        AuthorWithPatientType author = getAuthor();

        RevokePatientConsentRequest consentRequest = RequestObjectBuilderFactory.getRequestObjectBuilder().createRevokeRequest(author, consentType);

        TestUtils.logJaxbObject(consentRequest);

        // Service
        WsConsentService service = ServiceFactory.getWsConsentService();
        RevokePatientConsentResponse response = service.revokePatientConsent(Session.getInstance().getSession().getSAMLToken(), consentRequest);

        TestUtils.logJaxbObject(response);

        LOG.debug("Response: " + response);
        LOG.debug("isComplete =" + response.getAcknowledge().isIscomplete());
        List<ErrorType> errors = response.getAcknowledge().getErrors();
        for (ErrorType error : errors) {
            LOG.debug("URL : " + error.getUrl() + " errorMessage : " + error.getDescription().getValue());
        }
        return response;
    }


    /**
     * @return
     * @throws TechnicalConnectorException
     * @throws WsConsentBusinessConnectorException
     * @throws InstantiationException
     */
    protected ConsentType createNewConsentTypeForCurrentPatientAndAuthor() throws TechnicalConnectorException, WsConsentBusinessConnectorException, InstantiationException {
        List<CDCONSENT> consentList = new ArrayList<CDCONSENT>();
        consentList.add(CDConsentBuilderUtil.createCDConsent("1.0", CDCONSENTvalues.RETROSPECTIVE));
        ConsentType consentType = RequestObjectBuilderFactory.getConsentBuilder().createNewConsent(getPatient(), consentList, new DateTime(), getAuthor());
        return consentType;
    }

    /**
     * @return
     * @throws InstantiationException
     * @throws ConnectorException
     */
    protected PutPatientConsentResponse putConsentForCurrentPatientWithSessionService(ConsentType consentType) throws InstantiationException, ConnectorException {
        PutPatientConsentRequest consentRequest = RequestObjectBuilderFactory.getRequestObjectBuilder().createPutRequest(getAuthor(), consentType);
        TestUtils.logJaxbObject(consentRequest);
        // Service
        be.ehealth.businessconnector.wsconsent.session.WsConsentService service = WsConsentSessionServiceFactory.getWsConsentService();
        PutPatientConsentResponse response = service.putPatientConsent(consentRequest);
        return response;
    }


    /**
     * @return
     * @throws InstantiationException
     * @throws ConnectorException
     */
    protected GetPatientConsentResponse retrievePatientConsentResponseWithSessionService() throws InstantiationException, ConnectorException {
        List<CDCONSENT> consentList = new ArrayList<CDCONSENT>();
        consentList.add(CDConsentBuilderUtil.createCDConsent("1.0", CDCONSENTvalues.RETROSPECTIVE));

        SelectGetPatientConsentType consentType = RequestObjectBuilderFactory.getConsentBuilder().createSelectGetPatientConsent(getPatient(), consentList);
        AuthorWithPatientType author = getAuthor();

        GetPatientConsentRequest consentRequest = RequestObjectBuilderFactory.getRequestObjectBuilder().createGetRequest(author, consentType);

        TestUtils.logJaxbObject(consentRequest);
        // Service
        be.ehealth.businessconnector.wsconsent.session.WsConsentService service = WsConsentSessionServiceFactory.getWsConsentService();
        GetPatientConsentResponse response = service.getPatientConsent(consentRequest);

        TestUtils.logJaxbObject(response);
        return response;
    }


    /**
     * @return
     * @throws InstantiationException
     * @throws ConnectorException
     */
    protected RevokePatientConsentResponse revokePatientconsentWithSessionService(ConsentType consentType) throws InstantiationException, ConnectorException {

        consentType.setRevokedate(new DateTime());
        AuthorWithPatientType author = getAuthor();

        RevokePatientConsentRequest consentRequest = RequestObjectBuilderFactory.getRequestObjectBuilder().createRevokeRequest(author, consentType);

        TestUtils.logJaxbObject(consentRequest);

        // Service
        be.ehealth.businessconnector.wsconsent.session.WsConsentService service = WsConsentSessionServiceFactory.getWsConsentService();
        RevokePatientConsentResponse response = service.revokePatientConsent(consentRequest);

        TestUtils.logJaxbObject(response);

        LOG.debug("Response: " + response);
        LOG.debug("isComplete =" + response.getAcknowledge().isIscomplete());
        List<ErrorType> errors = response.getAcknowledge().getErrors();
        for (ErrorType error : errors) {
            LOG.debug("URL : " + error.getUrl() + " errorMessage : " + error.getDescription().getValue());
        }
        return response;
    }

    /**
     * @param revokePatientconsentResponse
     * @param string
     */
    protected void checkErrorMessage(RevokePatientConsentResponse revokePatientconsentResponse, String... errorMessages) {
        Assert.assertNotNull(revokePatientconsentResponse);
        Assert.assertNotNull(revokePatientconsentResponse.getAcknowledge());
        Assert.assertFalse(revokePatientconsentResponse.getAcknowledge().isIscomplete());
        Assert.assertTrue(revokePatientconsentResponse.getAcknowledge().getErrors().size() == errorMessages.length);
        for (int i = 0; i < errorMessages.length; i++) {
            String errorMessage = errorMessages[i];
            Assert.assertEquals(errorMessage, revokePatientconsentResponse.getAcknowledge().getErrors().get(i).getDescription().getValue());

        }
        LOG.info("checkErrorMessageContainsTheFollowingErrors for revokePatientconsentResponse: OK");
    }


    /**
     * @param revokePatientconsentResponse
     */
    protected void checkIfSuccesFull(RevokePatientConsentResponse revokePatientconsentResponse) {
        Assert.assertNotNull(revokePatientconsentResponse);
        Assert.assertNotNull(revokePatientconsentResponse.getAcknowledge());
        Assert.assertTrue(revokePatientconsentResponse.getAcknowledge().isIscomplete());
        List<ErrorType> errors = revokePatientconsentResponse.getAcknowledge().getErrors();
        Assert.assertTrue(ErrorTypeManagement.printErrors(errors), errors.isEmpty());
        LOG.info("checkIfSuccesFull : revokePatientResponse indicates success");
    }


    /**
     * @param firstConsent
     * @param consent
     */
    protected void checkIfEqual(ConsentType expected, ConsentType actual) throws TechnicalConnectorException, SAXException, IOException {
        // name and firstname will not be returned !! -> this info is discarted in the backend
        // signDate : truncated to date level + different type in return type
        stripConsentType(expected);
        stripConsentType(actual);

        XmlAsserter.assertSimilar(expected, actual);
    }

    private void stripConsentType(ConsentType content) {
        content.getPatient().setFirstname(null);
        content.getPatient().setFamilyname(null);
        for (HcpartyType hcparty : content.getAuthor().getHcparties()) {
            List<IDHCPARTY> filteredList = new ArrayList<IDHCPARTY>();
            for (IDHCPARTY idhcparty : hcparty.getIds()) {
                if (idhcparty.getS() != IDHCPARTYschemes.INSS) {
                    filteredList.add(idhcparty);
                }
            }
            hcparty.getIds().clear();
            hcparty.getIds().addAll(filteredList);
        }
    }


    /**
     * @param secondPutConsentResponse
     * @param errorMessages Array of expected errorMessages
     */
    protected void checkErrorMessage(PutPatientConsentResponse secondPutConsentResponse, String... errorMessages) {
        Assert.assertNotNull(secondPutConsentResponse);
        Assert.assertNotNull(secondPutConsentResponse.getAcknowledge());
        Assert.assertFalse(secondPutConsentResponse.getAcknowledge().isIscomplete());
        Assert.assertTrue(secondPutConsentResponse.getAcknowledge().getErrors().size() == errorMessages.length);
        for (int i = 0; i < errorMessages.length; i++) {
            String errorMessage = errorMessages[i];
            Assert.assertEquals(errorMessage, secondPutConsentResponse.getAcknowledge().getErrors().get(i).getDescription().getValue());
        }
        LOG.info("checkErrorMessageContainsTheFollowingErrors : checked if response contained error messages : OK");
    }


    /**
     * @param putConsentResponse
     */
    protected void checkIfSuccessFull(PutPatientConsentResponse putConsentResponse) {
        Assert.assertNotNull(putConsentResponse);
        Assert.assertNotNull(putConsentResponse.getAcknowledge());
        Assert.assertTrue(putConsentResponse.getAcknowledge().isIscomplete());
        List<ErrorType> errors = putConsentResponse.getAcknowledge().getErrors();
        Assert.assertTrue(ErrorTypeManagement.printErrors(errors), errors.isEmpty());
        LOG.info("checkIfSuccessFull : putConsentResponse was successFull");
    }


    /**
     * @throws InstantiationException
     * @throws WsConsentBusinessConnectorException
     * @throws TechnicalConnectorException
     * @throws SessionManagementException
     * 
     */
    protected void cleanConsents() throws SessionManagementException, TechnicalConnectorException, WsConsentBusinessConnectorException, InstantiationException {
        GetPatientConsentResponse retrievedPatientConsentResponse = retrievePatientConsentResponse();
        ConsentType consent = retrievedPatientConsentResponse.getConsent();
        if (consent != null) {
            LOG.info("cleanConsents : revoking existing consent");
            revokePatientconsent(consent);
        }
    }


}
