/*
 * Copyright (c) eHealth
 */
package be.ehealth.businessconnector.therlink.mappers;

import java.io.InputStream;
import java.util.List;
import java.util.Set;

import junit.framework.AssertionFailedError;

import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.joda.time.LocalDate;
import org.joda.time.chrono.GregorianChronology;
import org.junit.Assert;
import org.junit.Test;
import org.unitils.reflectionassert.ReflectionAssert;

import be.ehealth.business.common.domain.Patient;
import be.ehealth.businessconnector.therlink.domain.Author;
import be.ehealth.businessconnector.therlink.domain.HcParty;
import be.ehealth.businessconnector.therlink.domain.OperationContext;
import be.ehealth.businessconnector.therlink.domain.Proof;
import be.ehealth.businessconnector.therlink.domain.TherapeuticLinkRequestType;
import be.ehealth.businessconnector.therlink.domain.requests.GetTherapeuticLinkRequest;
import be.ehealth.businessconnector.therlink.domain.requests.PutTherapeuticLinkRequest;
import be.ehealth.businessconnector.therlink.domain.requests.RevokeTherapeuticLinkRequest;
import be.ehealth.businessconnector.therlink.domain.responses.Acknowledge;
import be.ehealth.businessconnector.therlink.domain.responses.GeneralResponse;
import be.ehealth.businessconnector.therlink.domain.responses.GetTherapeuticLinkResponse;
import be.ehealth.businessconnector.therlink.domain.responses.PutTherapeuticLinkResponse;
import be.ehealth.businessconnector.therlink.domain.responses.RevokeTherapeuticLinkResponse;
import be.ehealth.businessconnector.therlink.domain.responses.TherapeuticLinkResponse;
import be.ehealth.businessconnector.therlink.domain.responses.TherapeuticLinkResponseError;
import be.ehealth.technicalconnector.utils.ConnectorIOUtils;


/**
 * Unit test {@link ResponseObjectMapperTest}
 * 
 * @author EHP
 * 
 */
public class ResponseObjectMapperTest {

    @Test
    public void testMapSuccessfulPutTherapeuticLinkResponse() throws Exception {
        ResponseObjectMapper responseObjectMapper = MapperFactory.getResponseObjectMapper();

        // Loading file and mapping response
        InputStream xmlFile = ConnectorIOUtils.getResourceAsStream("/examples/responses/putTherapeuticLinkSuccessful.xml");
        String xml = new String(ConnectorIOUtils.getBytes(xmlFile));
        PutTherapeuticLinkResponse actual = responseObjectMapper.mapXMLToPutTherapeuticLinkResponse(xml);

        Author expectedRequestAuthor = new Author.Builder().addHcParty(new HcParty.Builder().withType("application").withName("Physician application name").withApplicationID("1987654322").build()).addHcParty(new HcParty.Builder().withNihii("16601747004").withInss("67060405711").withType("persphysician").build()).build();

        Acknowledge expectedAcknowledge = new Acknowledge();
        expectedAcknowledge.setComplete(true);

        HcParty hcParty = new HcParty.Builder().withEhp("16601747004").withType("orgpublichealth").withName("Responder organization").build();

        PutTherapeuticLinkRequest request = new PutTherapeuticLinkRequest(new DateTime(2012, 8, 10, 9, 9, 27, 0), "1987654322.20120810090927123", expectedRequestAuthor, null, (Proof[]) null);
        PutTherapeuticLinkResponse expected = new PutTherapeuticLinkResponse(new DateTime(2012, 8, 10, 9, 9, 28, 0), new Author.Builder().addHcParty(hcParty).build(), "1820563481. 20120810090928123 ", request, expectedAcknowledge);
        checkResult(actual, expected);
    }

    /**
     * @param actual
     * @param expected
     * @throws AssertionFailedError
     */
    private void checkResult(PutTherapeuticLinkResponse actual, PutTherapeuticLinkResponse expected) throws AssertionFailedError {
        // problem : ids and cds cause error in reflectionAssert

        for (HcParty party : actual.getExternalAuthor().getHcParties()) {
            party.getIds().clear();
            party.getCds().clear();
        }
        for (HcParty expectedParty : expected.getExternalAuthor().getHcParties()) {
            expectedParty.getIds().clear();
            expectedParty.getCds().clear();
        }
        for (HcParty party : actual.getOriginalRequest().getAuthor().getHcParties()) {
            party.getIds().clear();
            party.getCds().clear();
        }
        for (HcParty expectedParty : expected.getOriginalRequest().getAuthor().getHcParties()) {
            expectedParty.getIds().clear();
            expectedParty.getCds().clear();
        }
        ReflectionAssert.assertLenientEquals(actual, expected);
    }

    /**
     * @param actual
     * @param expected
     */
    private void checkResult(GeneralResponse actual, GeneralResponse expected) {
        // problem : ids and cds cause error in reflectionAssert
        for (HcParty party : actual.getExternalAuthor().getHcParties()) {
            party.getIds().clear();
            party.getCds().clear();
        }
        for (HcParty expectedParty : expected.getExternalAuthor().getHcParties()) {
            expectedParty.getIds().clear();
            expectedParty.getCds().clear();
        }
        for (HcParty party : actual.getOriginalRequest().getAuthor().getHcParties()) {
            party.getIds().clear();
            party.getCds().clear();
        }
        for (HcParty expectedParty : expected.getOriginalRequest().getAuthor().getHcParties()) {
            expectedParty.getIds().clear();
            expectedParty.getCds().clear();
        }
        ReflectionAssert.assertLenientEquals(actual, expected);

    }

    @Test
    public void testUnsuccessfulMapPutTherapeuticLinkResponse() throws Exception {
        // Loading file and mapping response
        InputStream xmlFile = ConnectorIOUtils.getResourceAsStream("/examples/responses/putTherapeuticLinkUnsuccessful.xml");
        String xml = new String(ConnectorIOUtils.getBytes(xmlFile));
        PutTherapeuticLinkResponse actual = MapperFactory.getResponseObjectMapper().mapXMLToPutTherapeuticLinkResponse(xml);

        PutTherapeuticLinkResponse expected = new PutTherapeuticLinkResponse(new DateTime(2012, 8, 10, 9, 9, 28, 0), new Author.Builder().addHcParty(new HcParty.Builder().withEhp("16601747004").withType("orgpublichealth").withName("Responder organization").build()).build(), "1820563481. 20120810090928123 ", new PutTherapeuticLinkRequest(new DateTime(2012, 8, 10, 9, 9, 27, 0), "1987654322.20120810090927123", new Author.Builder().addHcParty(new HcParty.Builder().withApplicationID("1987654322").withType("application").withName("Physician application name").build()).addHcParty(new HcParty.Builder().withNihii("16601747004").withInss("67060405711").withType("persphysician").build()).build()), new Acknowledge.Builder().withComplete(false)
            .addError(new TherapeuticLinkResponseError.Builder().withErrorCode("TL.INPUT.06").withErrorDescription("Invalid patient identifier").build()).build());

        checkResult(actual, expected);
    }

    @Test
    public void testMapSuccessfulRevokeTherapeuticLinkResponse() throws Exception {
        // Loading file and mapping response
        InputStream xmlFile = ConnectorIOUtils.getResourceAsStream("/examples/responses/revokeTherapeuticLinkSuccessful.xml");
        String xml = new String(ConnectorIOUtils.getBytes(xmlFile));
        RevokeTherapeuticLinkResponse actual = MapperFactory.getResponseObjectMapper().mapXMLToRevokeTherapeuticLinkResponse(xml);


        HcParty hcp = new HcParty.Builder().withEhp("16601747004").withType("orgpublichealth").withName("Responder organization").build();
        HcParty application = new HcParty.Builder().withApplicationID("1987654322").withType("application").withName("Physician application name").build();
        HcParty persphysician = new HcParty.Builder().withNihii("16601747004").withInss("67060405711").withType("persphysician").build();

        RevokeTherapeuticLinkRequest request = new RevokeTherapeuticLinkRequest(new DateTime(2012, 8, 17, 9, 9, 27, 0), "1987654322.20120817090927123", new Author.Builder().addHcParty(application).addHcParty(persphysician).build(), null, (Proof[]) null);

        RevokeTherapeuticLinkResponse expected = new RevokeTherapeuticLinkResponse(new DateTime(2012, 8, 17, 9, 9, 28, 0), new Author.Builder().addHcParty(hcp).build(), "1820563481. 20120817090928123 ", request, new Acknowledge.Builder().withComplete(true).build());
        checkResult(actual, expected);

    }

    @Test
    public void testUnsuccessfulMapRevokeTherapeuticLinkResponse() throws Exception {
        // Loading file and mapping response
        InputStream xmlFile = ConnectorIOUtils.getResourceAsStream("/examples/responses/revokeTherapeuticLinkUnsuccessful.xml");
        String xml = new String(ConnectorIOUtils.getBytes(xmlFile));
        ResponseObjectMapper responseObjectMapper = MapperFactory.getResponseObjectMapper();
        RevokeTherapeuticLinkResponse actual = responseObjectMapper.mapXMLToRevokeTherapeuticLinkResponse(xml);

        HcParty application = new HcParty.Builder().withApplicationID("1987654322").withType("application").withName("Physician application name").build();
        HcParty persphysician = new HcParty.Builder().withNihii("16601747004").withInss("67060405711").withType("persphysician").build();

        RevokeTherapeuticLinkRequest request = new RevokeTherapeuticLinkRequest(new DateTime(2012, 8, 17, 9, 9, 27, 0), "1987654322.20120817090927123", new Author.Builder().addHcParty(application).addHcParty(persphysician).build(), null, (Proof[]) null);
        HcParty orgPublicHealth = new HcParty.Builder().withEhp("16601747004").withType("orgpublichealth").withName("Responder organization").build();
        TherapeuticLinkResponseError error = new TherapeuticLinkResponseError.Builder().withErrorCode("TL.INPUT.06").withErrorDescription("Invalid patient identifier").build();
        RevokeTherapeuticLinkResponse expected = new RevokeTherapeuticLinkResponse(new DateTime(2012, 8, 17, 9, 9, 28, 0), new Author.Builder().addHcParty(orgPublicHealth).build(), "1820563481. 20120817090928123 ", request, new Acknowledge.Builder().withComplete(false).addError(error).build());

        checkResult(expected, actual);
    }

    @Test
    public void testMapSuccessfulGetTherapeuticLinkResponseWithList() throws Exception {
        // Loading file and mapping response
        InputStream xmlFile = ConnectorIOUtils.getResourceAsStream("/examples/responses/getTherapeuticLinkSuccessfulWithList.xml");
        String xml = new String(ConnectorIOUtils.getBytes(xmlFile));
        ResponseObjectMapper responseObjectMapper = MapperFactory.getResponseObjectMapper();
        GetTherapeuticLinkResponse response = responseObjectMapper.mapXMLToGetTherapeuticLinkResponse(xml);

        // RESPONSE INFORMATION
        Assert.assertEquals(response.getExternalID().trim(), "1820563481.20120223090930123");
        Assert.assertEquals(response.getDateTime(), new DateTime(2012, 2, 23, 9, 1, 0, 0));
        // Response Author
        Author externalAuthor = response.getExternalAuthor();
        Assert.assertEquals(externalAuthor.getHcParties().size(), 1);
        assertHcPartyEquals((HcParty) externalAuthor.getHcParties().toArray()[0], "Responder organization", null, null, "orgpublichealth", null, null, "16601747004");
        // Ack
        Assert.assertTrue(response.getAcknowledge().isComplete());
        // Therapeutic links
        List<TherapeuticLinkResponse> listOfTherLink = response.getListOfTherapeuticLinks();
        Assert.assertEquals(listOfTherLink.size(), 3);
        TherapeuticLinkResponse firstTherLink = listOfTherLink.get(0);
        assertPatientEquals(firstTherLink.getPatient(), "Patient first name", "Patient family name", "75050431412");
        assertHcPartyEquals(firstTherLink.getHcParty(), null, "Physician first name", "Physician family name", "persphysician", null, "16601747004", null);
        Assert.assertEquals(firstTherLink.getType(), "gmd");
        Assert.assertNull(firstTherLink.getStartDate());
        Assert.assertNull(firstTherLink.getEndDate());
        Assert.assertNull(firstTherLink.getComment());
        Assert.assertEquals(firstTherLink.getOperationContexts().size(), 0);

        TherapeuticLinkResponse secondTherLink = listOfTherLink.get(1);
        assertPatientEquals(secondTherLink.getPatient(), "Patient first name", "Patient family name", "75050431412");
        assertHcPartyEquals(secondTherLink.getHcParty(), null, "Physician first name", "Physician family name", "persphysician", null, "16601747004", null);
        Assert.assertEquals(secondTherLink.getType(), "gpconsultation");
        Assert.assertEquals(secondTherLink.getStartDate(), new LocalDate(2012, 2, 1));
        Assert.assertEquals(secondTherLink.getEndDate(), new LocalDate(2012, 8, 1));
        Assert.assertEquals(secondTherLink.getComment(), "");
        Assert.assertEquals(secondTherLink.getOperationContexts().size(), 1);

        DateTime expectedDateTime = new DateTime(2012, 2, 1, 14, 8, 0, 0, GregorianChronology.getInstance(DateTimeZone.UTC));
        assertOperationContextEqual(secondTherLink.getOperationContexts().get(0), "declaration", expectedDateTime);


        // ORIGINAL REQUEST INFORMATION
        TherapeuticLinkRequestType originalRequest = response.getOriginalRequest();
        Assert.assertEquals(originalRequest.getID().trim(), "1990000332.20120223090927123");
        Assert.assertEquals(originalRequest.getDateTime(), new DateTime(2012, 2, 23, 9, 9, 27, 0));
        // Request Author
        Set<HcParty> originalHcParties = originalRequest.getAuthor().getHcParties();
        Assert.assertEquals(originalHcParties.size(), 1);
        assertHcPartyEquals((HcParty) originalHcParties.toArray()[0], "eHealthConsent", null, null, "application", null, null, "1990000332");
    }

    /**
     * @throws Exception
     */
    @Test
    public void testMapSuccessfulGetTherapeuticLinkResponseWithEmptyList() throws Exception {
        // Loading file and mapping response
        InputStream xmlFile = ConnectorIOUtils.getResourceAsStream("/examples/responses/getTherapeuticLinkSuccessfulWithEmptyList.xml");
        String xml = new String(ConnectorIOUtils.getBytes(xmlFile));
        ResponseObjectMapper responseObjectMapper = MapperFactory.getResponseObjectMapper();
        GetTherapeuticLinkResponse actual = responseObjectMapper.mapXMLToGetTherapeuticLinkResponse(xml);

        HcParty orgPublicHealth = new HcParty.Builder().withEhp("16601747004").withType("orgpublichealth").withName("Responder organization").build();
        HcParty hcp = new HcParty.Builder().withEhp("16601747004").withType("application").withName("eHealthConsent").build();
        GetTherapeuticLinkRequest request = new GetTherapeuticLinkRequest(new DateTime(2012, 2, 23, 9, 9, 27, 0), "1990000332.20120223090927123", new Author.Builder().addHcParty(hcp).build(), null, 999, (Proof[]) null);
        GetTherapeuticLinkResponse expected = new GetTherapeuticLinkResponse(new DateTime(2012, 2, 23, 9, 1, 0, 0), new Author.Builder().addHcParty(orgPublicHealth).build(), "1820563481.20120223090930123", request, null, new Acknowledge.Builder().withComplete(true).build());

        checkResult(expected, actual);

    }

    /**
     * @throws Exception
     */
    @Test
    public void testUnsuccessfulMapGetTherapeuticLinkResponse() throws Exception {
        // Loading file and mapping response
        InputStream xmlFile = ConnectorIOUtils.getResourceAsStream("/examples/responses/getTherapeuticLinkUnsuccessful.xml");
        String xml = new String(ConnectorIOUtils.getBytes(xmlFile));
        ResponseObjectMapper responseObjectMapper = MapperFactory.getResponseObjectMapper();
        GetTherapeuticLinkResponse actual = responseObjectMapper.mapXMLToGetTherapeuticLinkResponse(xml);

        HcParty hcp = new HcParty.Builder().withEhp("16601747004").withType("orgpublichealth").withName("Responder organization").build();
        HcParty application = new HcParty.Builder().withEhp("1990000332").withType("application").withName("eHealthConsent").build();
        GetTherapeuticLinkRequest request = new GetTherapeuticLinkRequest(new DateTime(2012, 2, 23, 9, 9, 27, 0), "1990000332.20120223090927123", new Author.Builder().addHcParty(application).build(), null, 999, (Proof[]) null);
        TherapeuticLinkResponseError error = new TherapeuticLinkResponseError.Builder().withErrorCode("TL.INPUT.06").withErrorDescription("Invalid patient identifier").build();
        GetTherapeuticLinkResponse expected = new GetTherapeuticLinkResponse(new DateTime(2012, 2, 23, 9, 1, 0, 0), new Author.Builder().addHcParty(hcp).build(), "1820563481.20120223090930123", request, null, new Acknowledge.Builder().withComplete(false).addError(error).build());

        checkResult(expected, actual);
    }


    private void assertOperationContextEqual(OperationContext operationContext, String operation, DateTime dateTime) {
        Assert.assertEquals(operationContext.getOperation(), operation);
        DateTime recordDateTime = operationContext.getRecordDateTime();
        Assert.assertEquals(dateTime, recordDateTime);
        Assert.assertTrue("expected date " + dateTime + " is not equal to " + recordDateTime, dateTime.isEqual(recordDateTime));
    }

    private void assertPatientEquals(Patient patient, String firstname, String lastname, String inss) {
        Assert.assertEquals(inss, patient.getInss());
        Assert.assertEquals(lastname, patient.getLastName());
        Assert.assertEquals(firstname, patient.getFirstName());

    }

    @SuppressWarnings("deprecation")
    private void assertHcPartyEquals(HcParty hcParty, String name, String firstName, String familyName, String type, String inss, String nihii, String applicationID) {
        Assert.assertEquals(hcParty.getType().trim(), type);
        if ("application".equals(type)) {
            Assert.assertEquals(hcParty.getCbe(), nihii);
        } else if ("orgpublichealth".equals(type)) {
            Assert.assertEquals(hcParty.getEHP(), applicationID);
        } else {
            Assert.assertEquals(hcParty.getNihii(), nihii);
        }
        Assert.assertEquals(hcParty.getInss(), inss);
        Assert.assertEquals(hcParty.getName(), name);
        Assert.assertEquals(hcParty.getFamilyName(), familyName);
        Assert.assertEquals(hcParty.getFirstName(), firstName);
        if (hcParty.getEHP() != null) {
            Assert.assertEquals(hcParty.getEHP(), applicationID);
        }
    }

}
