/*
 * Copyright (c) eHealth
 */
package be.ehealth.businessconnector.tarification.session;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.joda.time.DateTime;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import be.ehealth.business.mycarenetcommons.builders.BlobBuilderFactory;
import be.ehealth.business.mycarenetcommons.domain.Blob;
import be.ehealth.business.mycarenetcommons.domain.CareReceiverId;
import be.ehealth.business.mycarenetcommons.domain.Routing;
import be.ehealth.business.mycarenetcommons.helper.ErrorMyCarenetTypeManagement;
import be.ehealth.business.mycarenetcommons.mapper.SendRequestMapper;
import be.ehealth.businessconnector.tarification.builder.RequestBuilder;
import be.ehealth.businessconnector.tarification.builder.TarificationRequestBuilderFactory;
import be.ehealth.businessconnector.tarification.helper.ResponseHelper;
import be.ehealth.technicalconnector.config.ConfigFactory;
import be.ehealth.technicalconnector.exception.TechnicalConnectorException;
import be.ehealth.technicalconnector.handler.ConnectionTimeOutHandler;
import be.ehealth.technicalconnector.idgenerator.IdGeneratorFactory;
import be.ehealth.technicalconnector.session.Session;
import be.ehealth.technicalconnector.utils.ConnectorIOUtils;
import be.ehealth.technicalconnector.utils.MarshallerHelper;
import be.ehealth.technicalconnector.utils.SessionUtil;
import be.fgov.ehealth.messageservices.core.v1.RetrieveTransactionResponse;
import be.fgov.ehealth.mycarenet.commons.core.v2.BlobType;
import be.fgov.ehealth.mycarenet.commons.protocol.v2.TarificationConsultationRequest;
import be.fgov.ehealth.mycarenet.commons.protocol.v2.TarificationConsultationResponse;
import be.fgov.ehealth.standards.kmehr.cd.v1.CDERRORMYCARENET;
import be.fgov.ehealth.standards.kmehr.schema.v1.ErrorMyCarenetType;
import be.fgov.ehealth.standards.kmehr.schema.v1.FolderType;
import be.fgov.ehealth.standards.kmehr.schema.v1.ItemType;
import be.fgov.ehealth.standards.kmehr.schema.v1.TransactionType;
import be.fgov.ehealth.technicalconnector.tests.session.SessionInitializer;


/**
 * Integration tests for tarification.
 * 
 * @author eHealth Platform
 * 
 */
@RunWith(Parameterized.class)
public final class TarificationIntegrationTest {

    @Parameters(name = "OA{0}")
    public static Collection<Object[]> data() throws Exception {
        Collection<Object[]> testData = new ArrayList<Object[]>();
        testData.add(new Object[]{"300"});
        testData.add(new Object[]{"500"});
        testData.add(new Object[]{"900"});
        return testData;
    }

    /**
     * @param oa
     */
    public TarificationIntegrationTest(String oa) {
        super();
        this.oa = oa;
    }


    private static final Logger LOG = LoggerFactory.getLogger(TarificationIntegrationTest.class);

    private static HashMap<String, Map<String, String>> nissProvider = new HashMap<String, Map<String, String>>();

    private static HashMap<String, String> nissMatchScenario = new HashMap<String, String>();

    private String oa;


    @BeforeClass
    public static void setupsession() throws Exception {
        LOG.debug("TarificationIntegrationTest.loadSession:");
        SessionInitializer.init("/be.ehealth.businessconnector.tarification.test.properties");
        // the mycarenet tarification webservice is slow , the normal timeout of 30 seconds is not sufficient
        ConfigFactory.getConfigValidator().setProperty(ConnectionTimeOutHandler.REQUEST_TIMEOUT_PROP, "180000");
        ConfigFactory.getConfigValidator().setProperty(ConnectionTimeOutHandler.CONNECT_TIMEOUT_PROP, "180000");
    }

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

    @Test
    public void getTarificationScenario01Test() throws Exception {
        testSimpleGetTarification(oa, SCENARIO1, new ExpectedResult("130", null));
    }

    @Test
    public void getTarificationScenario02Test() throws Exception {
        testSimpleGetTarification(oa, SCENARIO2, new ExpectedResult("172", null));
    }

    @Test
    public void getTarificationScenario03Test() throws Exception {
        testSimpleGetTarification(oa, SCENARIO3, new ExpectedResult("171", null));
    }

    @Test
    public void getTarificationScenario04Test() throws Exception {
        testSimpleGetTarification(oa, SCENARIO4, true, new ExpectedResult("166", null));
    }

    @Test
    public void getTarificationScenario05Test() throws Exception {
        testSimpleGetTarification(oa, SCENARIO5, new ExpectedResult("169", null));
    }

    @Test
    public void getTarificationScenario06Test() throws Exception {
        testSimpleGetTarification(oa, SCENARIO6, new ExpectedResult("170", null));
    }

    @Test
    public void getTarificationScenario07Test() throws Exception {
        testSimpleGetTarification(oa, SCENARIO7, new ExpectedResult(null, "3"));
    }

    @Test
    public void getTarificationScenario08Test() throws Exception {
        testSimpleGetTarification(oa, SCENARIO8, new ExpectedResult(null, "3"));
    }

    @Test
    public void getTarificationScenario09Test() throws Exception {
        testSimpleGetTarification(oa, SCENARIO9, new ExpectedResult(null, "9"));
    }

    @Test
    public void getTarificationScenario10Test() throws Exception {
        testSimpleGetTarification(oa, SCENARI10, new ExpectedResult(null, "2"));
    }

    @Test
    public void getTarificationScenario11Test() throws Exception {
        testSimpleGetTarification(oa, SCENARI11, new ExpectedResult(null, "7"));
    }

    private void testSimpleGetTarification(String oa, String scenarioName, ExpectedResult expectedResult) throws Exception {
        testSimpleGetTarification(oa, scenarioName, false, expectedResult);
    }

    private void testSimpleGetTarification(String oa, String scenarioName, boolean useDateInPast, ExpectedResult expectedResult) throws Exception {
        RequestBuilder requestBuilder = TarificationRequestBuilderFactory.getRequestObjectBuilder();
        InputStream businessXmlContentStream = ConnectorIOUtils.getResourceAsStream("/examples/request/" + scenarioName + "Request.xml");
        Result result = replaceVariableTagsWithContent(oa, scenarioName, new String(ConnectorIOUtils.getBytes(businessXmlContentStream)), useDateInPast);
        String businessXml = result.textContent;
        LOG.debug("used content : \n" + businessXml);

        String inputReference = IdGeneratorFactory.getIdGenerator("xsid").generateId();
        String nissForThisScenario = nissProvider.get(oa).get(nissMatchScenario.get(scenarioName));
        TarificationConsultationRequest request = requestBuilder.buildConsultationRequest(getRoutingForNiss(nissForThisScenario), businessXml.getBytes(), inputReference);
        request.getCommonInput().setInputReference(result.kmehrId);
        request.getRouting().setReferenceDate(result.time);
        TarificationSessionService session = TarificationSessionServiceFactory.getTarificationSession();
        TarificationConsultationResponse consultTarificationResponse = session.consultTarification(request);

        BlobType detail = consultTarificationResponse.getReturn().getDetail();
        Blob blob = SendRequestMapper.mapBlobTypeToBlob(detail);
        byte[] content = BlobBuilderFactory.getBlobBuilder("mcn.tarification").checkAndRetrieveContent(blob);
        String responseContent = new String(content);
        LOG.debug("content : \n" + responseContent);

        testResultValue(expectedResult, oa, scenarioName, content);

    }

    /**
     * @param expectedResult
     * @param content
     */
    private void testResultValue(ExpectedResult expectedResult, String oa, String scenarioName, byte[] content) throws Exception {
        MarshallerHelper<RetrieveTransactionResponse, RetrieveTransactionResponse> helper = new MarshallerHelper<RetrieveTransactionResponse, RetrieveTransactionResponse>(RetrieveTransactionResponse.class, RetrieveTransactionResponse.class);
        RetrieveTransactionResponse commonInputResponse = helper.toObject(content);
        ResponseHelper.validateResponse(commonInputResponse);
        if (expectedResult.justificationCode == null) {
            Boolean flagFound = false;
            for (ErrorMyCarenetType error : commonInputResponse.getAcknowledge().getErrors()) {
                for (CDERRORMYCARENET code : error.getCds()) {
                    if (expectedResult.errorCode.equals(code.getValue())) {
                        flagFound = true;
                    }
                }
            }
            if (!flagFound) {
                Assert.fail("Wait for error code " + expectedResult.errorCode + " but not found in response");
            }
        } else {
            List<ErrorMyCarenetType> errors = commonInputResponse.getAcknowledge().getErrors();
            Assert.assertTrue(ErrorMyCarenetTypeManagement.printErrors(errors), errors.isEmpty());

            Boolean flagFound = false;
            for (FolderType folder : commonInputResponse.getKmehrmessage().getFolders()) {
                for (TransactionType transaction : folder.getTransactions()) {
                    for (ItemType item : transaction.getItem()) {
                        try {
                            if ("justification".equals(item.getCds().get(0).getValue()) && expectedResult.justificationCode.equals(item.getContents().get(0).getCds().get(0).getValue())) {
                                flagFound = true;
                            }
                        } catch (Exception e) {
                            Assert.fail("wrong value of content");
                        }
                    }
                }
            }
            if (!flagFound) {
                Assert.fail("Wait for justification code " + expectedResult.justificationCode + " but not found in response");
            }
        }
    }

    /**
     * @param result
     * @param fileName
     * @throws FileNotFoundException
     * @throws IOException
     */
    @SuppressWarnings("unused")
    private void saveFileValue(byte[] fileValue, String fileName) throws FileNotFoundException, IOException {
        FileOutputStream fos = new FileOutputStream(fileName);
        fos.write(fileValue);
        fos.close();
    }

    /**
     * @param patientNiss
     * @param testFileContent
     * @return
     * @throws TechnicalConnectorException
     */
    private Result replaceVariableTagsWithContent(String oa, String scenarioName, String testFileContent, boolean useDateInPast) throws TechnicalConnectorException {
        DateTime dateTimeNow = new DateTime();
        String khmerId = dateTimeNow.toString("YYYYddhhmmssSS");
        testFileContent = testFileContent.replaceAll("\\$\\{currentTimestamp\\}", khmerId);
        if (useDateInPast) {
            dateTimeNow = dateTimeNow.minusMonths(3);
        }
        String date = dateTimeNow.toString("YYYY-MM-dd");
        testFileContent = testFileContent.replaceAll("\\$\\{currentDate\\}", date);
        String time = dateTimeNow.toString("hh:mm:ss");
        testFileContent = testFileContent.replaceAll("\\$\\{currentTime\\}", time);
        String userNihiiNumber = retrieveCareProviderNihii();
        testFileContent = testFileContent.replaceAll("\\$\\{nihiiNumber\\}", userNihiiNumber);
        // TODO EHCON-1083 : use careprovider properties to retrieve first and lastname , session user is not necessarely the careprovider!
        testFileContent = testFileContent.replaceAll("\\$\\{lastName\\}", SessionUtil.getLastname());
        testFileContent = testFileContent.replaceAll("\\$\\{firstName\\}", SessionUtil.getFirstname());
        String nissNumber = retrieveCareProviderNiss();
        testFileContent = testFileContent.replaceAll("\\$\\{niss\\}", nissNumber);
        testFileContent = testFileContent.replaceAll("\\$\\{professionType\\}", ConfigFactory.getConfigValidator().getProperty("main.kmehr.quality"));
        testFileContent = testFileContent.replaceAll("\\$\\{patientNiss\\}", nissProvider.get(oa).get(nissMatchScenario.get(scenarioName)));
        testFileContent = testFileContent.replaceAll("\\$\\{gmd-Nihii\\}", nissProvider.get(oa).get(GMDNIHII));
        testFileContent = testFileContent.replaceAll("\\$\\{gmd-Niss\\}", nissProvider.get(oa).get(GMDNISS));
        testFileContent = testFileContent.replaceAll("\\$\\{gmd-FirstName\\}", nissProvider.get(oa).get(GMDFIRSTNAME));
        testFileContent = testFileContent.replaceAll("\\$\\{gmd-LastName\\}", nissProvider.get(oa).get(GMDLASTNAME));
        LOG.trace("xml content after replacing tags : \n" + testFileContent);
        return new Result(testFileContent, khmerId, dateTimeNow);
    }

    /**
     * @return
     */
    private String retrieveCareProviderNiss() {
        // TODO EHCON-1083 : user careprovider property in config file , and use this property
        String mandatorNiss = ConfigFactory.getConfigValidator().getProperty("mandator.inss");
        if (mandatorNiss == null) {
            return ConfigFactory.getConfigValidator().getProperty("user.inss");
        }
        return mandatorNiss;
    }

    /**
     * @return
     */
    private String retrieveCareProviderNihii() {
        // TODO EHCON-1083 : user careprovider property in config file , and use this property
        String userNihiiNumber = "";
        userNihiiNumber = ConfigFactory.getConfigValidator().getProperty("mandator.nihii");
        if (userNihiiNumber == null || userNihiiNumber.length() < 1) {
            userNihiiNumber = ConfigFactory.getConfigValidator().getProperty("user.nihii");
        }
        return userNihiiNumber;
    }

    private static Routing getRoutingForNiss(String niss) {
        CareReceiverId careReceiver = new CareReceiverId(niss);
        return new Routing(careReceiver, new DateTime());
    }

    private static final String SCENARIO1 = "scenario01";

    private static final String SCENARIO2 = "scenario02";

    private static final String SCENARIO3 = "scenario03";

    private static final String SCENARIO4 = "scenario04";

    private static final String SCENARIO5 = "scenario05";

    private static final String SCENARIO6 = "scenario06";

    private static final String SCENARIO7 = "scenario07";

    private static final String SCENARIO8 = "scenario08";

    private static final String SCENARIO9 = "scenario09";

    private static final String SCENARI10 = "scenario10";

    private static final String SCENARI11 = "scenario11";

    private static final String OA100 = "100";

    private static final String OA500 = "500";

    private static final String OA600 = "600";

    private static final String OA900 = "900";

    private static final String OA300 = "300";

    private static final String NISSUSECASE1 = "case1";

    private static final String NISSUSECASE2 = "case2";

    private static final String NISSUSECASE3 = "case3";

    private static final String NISSUSECASE4 = "case4";

    private static final String GMDNIHII = "gmd-Nihii";

    private static final String GMDNISS = "gmd-Niss";

    private static final String GMDFIRSTNAME = "gmd-FirstName";

    private static final String GMDLASTNAME = "gmd-LastName";

    static {
        HashMap<String, String> nisss = new HashMap<String, String>();
        nisss.put(NISSUSECASE1, "45062120753");
        nisss.put(NISSUSECASE2, "60520504868");
        nisss.put(NISSUSECASE3, "86032817437");
        nisss.put(NISSUSECASE4, "13030117521");
        nisss.put(GMDNIHII, "17385467004");
        nisss.put(GMDNISS, "68010102395");
        nisss.put(GMDFIRSTNAME, "Justine");
        nisss.put(GMDLASTNAME, "Carambar");
        nissProvider.put(OA100, nisss);

        nisss = new HashMap<String, String>();
        nisss.put(NISSUSECASE1, "64032764903");
        nisss.put(NISSUSECASE2, "66021250154");
        nisss.put(NISSUSECASE3, "74042566174");
        nisss.put(NISSUSECASE4, "71010908576");
        nisss.put(GMDNIHII, "10065828004");
        nisss.put(GMDNISS, "68010102395");
        nisss.put(GMDFIRSTNAME, "Justine");
        nisss.put(GMDLASTNAME, "Carambar");
        nissProvider.put(OA300, nisss);

        nisss = new HashMap<String, String>();
        nisss.put(NISSUSECASE1, "49020508235");
        nisss.put(NISSUSECASE2, "83092618070");
        nisss.put(NISSUSECASE3, "76103012361");
        nisss.put(NISSUSECASE4, "62091405715");
        nisss.put(GMDNIHII, "16582446000");
        nisss.put(GMDNISS, "68010102395");
        nisss.put(GMDFIRSTNAME, "Justine");
        nisss.put(GMDLASTNAME, "Carambar");
        nissProvider.put(OA500, nisss);

        nisss = new HashMap<String, String>();
        nisss.put(NISSUSECASE1, "59072957042");
        nisss.put(NISSUSECASE2, "83091041227");
        nisss.put(NISSUSECASE3, "49061015930");
        nisss.put(NISSUSECASE4, "82413101990");
        nisss.put(GMDNIHII, "14372133004");
        nisss.put(GMDNISS, "68010102395");
        nisss.put(GMDFIRSTNAME, "Justine");
        nisss.put(GMDLASTNAME, "Carambar");
        nissProvider.put(OA600, nisss);

        nisss = new HashMap<String, String>();
        nisss.put(NISSUSECASE1, "59011214562");
        nisss.put(NISSUSECASE2, "73061527277");
        nisss.put(NISSUSECASE3, "90012333497");
        nisss.put(NISSUSECASE4, "78100404390");
        nisss.put(GMDNIHII, "15554246004");
        nisss.put(GMDNISS, "58041916130");
        nisss.put(GMDFIRSTNAME, "MATHIEU");
        nisss.put(GMDLASTNAME, "ALAIN");
        nissProvider.put(OA900, nisss);

        nissMatchScenario.put(SCENARIO1, NISSUSECASE1);
        nissMatchScenario.put(SCENARIO2, NISSUSECASE1);
        nissMatchScenario.put(SCENARIO3, NISSUSECASE1);
        nissMatchScenario.put(SCENARIO4, NISSUSECASE1);
        nissMatchScenario.put(SCENARIO5, NISSUSECASE3);
        nissMatchScenario.put(SCENARIO6, NISSUSECASE4);
        nissMatchScenario.put(SCENARIO7, NISSUSECASE1);
        nissMatchScenario.put(SCENARIO8, NISSUSECASE1);
        nissMatchScenario.put(SCENARIO9, NISSUSECASE2);
        nissMatchScenario.put(SCENARI10, NISSUSECASE2);
        nissMatchScenario.put(SCENARI11, NISSUSECASE2);
    }

    /**
     * 
     * used to get the content xml of the request fullfilled and the kmehr id.
     * 
     * @author EH058
     * 
     */
    private final class Result {

        private String textContent;

        private String kmehrId;

        private DateTime time;

        /**
         * @param textContent
         * @param kmehrId
         */
        private Result(String textContent, String kmehrId, DateTime time) {
            super();
            this.textContent = textContent;
            this.kmehrId = kmehrId;
            this.time = time;
        }
    }

    /**
     * 
     * use to select the value to verify on the response.
     * 
     * @author EH058
     * 
     * 
     */
    private final class ExpectedResult {

        private String errorCode;

        private String justificationCode;

        /**
         * @param errorCode
         * @param justificationCode
         */
        private ExpectedResult(String errorCode, String justificationCode) throws Exception {
            super();
            if (errorCode == null && justificationCode == null) {
                throw new IllegalArgumentException("error code = " + errorCode + " justification code = " + justificationCode + " one and only one must be filled");
            }
            this.errorCode = errorCode;
            this.justificationCode = justificationCode;
        }


    }
}
