/*
 * Copyright (c) Smals
 */
package be.ehealth.technicalconnector.config;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import be.ehealth.technicalconnector.config.test.ConfigurationModuleTester;
import be.ehealth.technicalconnector.exception.ConfigurationException;
import be.ehealth.technicalconnector.exception.TechnicalConnectorException;
import be.ehealth.technicalconnector.utils.ConnectorIOUtils;


/**
 * tests the workings of the configFactory.
 * 
 * @author EHP
 */
public class ConfigFactoryTest {

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

    private static final String CORRECT_FILE_NAME = "/props/testPropertyFileCorrect.properties";

    private static final String EMPTY_FILE_NAME = "/props/emptyPropertyFile.properties";

    private static final String CHANGED_FILE_NAME = "/props/changedPropertyFile.properties";

    private static final String PROP_SIMILAR_PROD = "/props/propertyFileSimilarToProduction.properties";

    private static final String PROPERTY_KEY_1 = "property.first";

    private static final String PROPERTY_KEY_2 = "property.second";

    private static final String PROPERTY_KEY_3 = "property.third";

    // values in correct file
    private static final String MODULE_PROPERTY_DEFAULT = "originalModuleName";

    private static final String FIRST_VALUE_DEFAULT = "first";

    private static final String SECOND_VALUE_DEFAULT = "second";

    private static final String THIRD_VALUE_DEFAULT = "third";

    // values in changed file
    private static final String MODULE_PROPERTY_CHANGED = "changedModuleName";

    private static final String FIRST_VALUE_CHANGED = "first_changed";

    private static final String SECOND_VALUE_CHANGED = "second_changed";

    private static final String THIRD_VALUE_CHANGED = "third_changed";

    @Before
    @After
    public void resetTest() {
        ConfigFactory.invalidate();
    }

    @Test
    public void loadCorrectFileTest() throws Exception {
        ConfigFactory.setConfigLocation(CORRECT_FILE_NAME);
        checkConfigProperties("load of correct file , all properties should have those values", MODULE_PROPERTY_DEFAULT, FIRST_VALUE_DEFAULT, SECOND_VALUE_DEFAULT, THIRD_VALUE_DEFAULT, ConfigFactory.getConfigValidator());
    }

    @Test
    public void loadCorrectFileTestWithSetLocation() throws Exception {
        InputStream is = ConnectorIOUtils.getResourceAsStream(CORRECT_FILE_NAME);
        ConfigFactory.setLocation(is);
        checkConfigProperties("load of correct file , all properties should have those values", MODULE_PROPERTY_DEFAULT, FIRST_VALUE_DEFAULT, SECOND_VALUE_DEFAULT, THIRD_VALUE_DEFAULT, ConfigFactory.getConfigValidator());
    }

    @Test
    public void checkWorkingAfterInvalidateCacheTest() throws Exception {
        ConfigFactory.setConfigLocation(CORRECT_FILE_NAME);
        checkConfigProperties("load of correct file , all properties should have those values", MODULE_PROPERTY_DEFAULT, FIRST_VALUE_DEFAULT, SECOND_VALUE_DEFAULT, THIRD_VALUE_DEFAULT, ConfigFactory.getConfigValidator());
        ConfigFactory.getConfigValidator().invalidateCache();
        checkConfigProperties("load after invalidate cache", MODULE_PROPERTY_DEFAULT, FIRST_VALUE_DEFAULT, SECOND_VALUE_DEFAULT, THIRD_VALUE_DEFAULT, ConfigFactory.getConfigValidator());
    }

    /**
     * @param string
     * @param moduleProperty
     * @param firstValue
     * @param secondValue
     * @param thirdValue
     * @param configValidator
     */
    private void checkConfigProperties(String errorMessage, String moduleProperty, String firstValue, String secondValue, String thirdValue, ConfigValidator configValidator) {
        Assert.assertEquals("difference in first property " + errorMessage, firstValue, configValidator.getProperty(PROPERTY_KEY_1));
        Assert.assertEquals("difference in second property " + errorMessage, secondValue, configValidator.getProperty(PROPERTY_KEY_2));
        Assert.assertEquals("difference in third property " + errorMessage, thirdValue, configValidator.getProperty(PROPERTY_KEY_3));
        Assert.assertEquals("difference in moduleProperty " + errorMessage, moduleProperty, configValidator.getProperty(ConfigurationModuleTester.TEST_KEY));
    }

    @Test
    public void changePropertyAndCheckRetrievalWithOtherConfigValidator() throws Exception {
        ConfigFactory.setConfigLocation(CORRECT_FILE_NAME);
        checkConfigProperties("check after load correct file", MODULE_PROPERTY_DEFAULT, FIRST_VALUE_DEFAULT, SECOND_VALUE_DEFAULT, THIRD_VALUE_DEFAULT, ConfigFactory.getConfigValidator());
        String changedName = "changedPropertyName";
        ConfigFactory.getConfigValidator(getAllTestProperties()).setProperty(PROPERTY_KEY_1, changedName);
        checkConfigProperties("check retrieval of changed property with different configValidator", MODULE_PROPERTY_DEFAULT, changedName, SECOND_VALUE_DEFAULT, THIRD_VALUE_DEFAULT, ConfigFactory.getConfigValidator());
        checkConfigProperties("check retrieval of changed property with different configValidator", MODULE_PROPERTY_DEFAULT, changedName, SECOND_VALUE_DEFAULT, THIRD_VALUE_DEFAULT, ConfigFactory.getConfigValidator(getAllTestProperties()));
        String[] requiredTestPropertiesAsArray = getAllTestProperties().toArray(new String[]{});
        checkConfigProperties("check retrieval of changed property with different configValidator", MODULE_PROPERTY_DEFAULT, changedName, SECOND_VALUE_DEFAULT, THIRD_VALUE_DEFAULT, ConfigFactory.getConfigValidatorFor(requiredTestPropertiesAsArray));
    }

    @Test
    public void retrieveWithDefaultValueTest() throws Exception {
        ConfigFactory.setConfigLocation(CORRECT_FILE_NAME);
        ConfigValidator configValidator = ConfigFactory.getConfigValidator(getAllTestProperties());
        Assert.assertEquals("value should not have default value if property is present", FIRST_VALUE_DEFAULT, configValidator.getProperty(PROPERTY_KEY_1, "default"));
        Assert.assertEquals("value should not have default value if property is present", "default", configValidator.getProperty("nonExistingProperty", "default"));
    }

    private List<String> getAllTestProperties() {
        ArrayList<String> expectedValues = new ArrayList<String>();
        expectedValues.add(ConfigurationModuleTester.TEST_KEY);
        expectedValues.add(PROPERTY_KEY_1);
        expectedValues.add(PROPERTY_KEY_2);
        expectedValues.add(PROPERTY_KEY_3);
        return expectedValues;
    }

    @Test
    public void loadEmptyFileConfigValidatorShowsInvalidTest() throws Exception {
        ConfigFactory.setConfigLocation(EMPTY_FILE_NAME);
        ConfigValidator configValidator = ConfigFactory.getConfigValidator(getAllTestProperties());
        Assert.assertFalse(configValidator.isValid());
        List<Object> unfoundPropertiesAfterValidation = configValidator.getUnfoundPropertiesAfterValidation();
        for (String expectedKey : getAllTestProperties()) {
            Assert.assertTrue(unfoundPropertiesAfterValidation.contains(expectedKey));
        }
    }

    @Test
    public void reloadWithSameConfigValidatorObjectTest() throws Exception {
        ConfigFactory.setConfigLocation(EMPTY_FILE_NAME);
        ConfigValidator configValidator = ConfigFactory.getConfigValidator();
        checkConfigProperties("empty file should all empty properties", null, null, null, null, configValidator);
        ConfigFactory.setConfigLocation(CORRECT_FILE_NAME);
        checkConfigProperties("check after load correct file", MODULE_PROPERTY_DEFAULT, FIRST_VALUE_DEFAULT, SECOND_VALUE_DEFAULT, THIRD_VALUE_DEFAULT, configValidator);
        checkConfigProperties("check after load correct file", MODULE_PROPERTY_DEFAULT, FIRST_VALUE_DEFAULT, SECOND_VALUE_DEFAULT, THIRD_VALUE_DEFAULT, ConfigFactory.getConfigValidator(getAllTestProperties()));
        String[] requiredTestPropertiesAsArray = getAllTestProperties().toArray(new String[]{});
        checkConfigProperties("check retrieval of changed property with different configValidator", MODULE_PROPERTY_DEFAULT, FIRST_VALUE_DEFAULT, SECOND_VALUE_DEFAULT, THIRD_VALUE_DEFAULT, ConfigFactory.getConfigValidatorFor(requiredTestPropertiesAsArray));

    }

    @Test(expected = ConfigurationException.class)
    public void configValidatorIsInvalidAfterReloadWithEmptyValuesTest() throws Exception {
        ConfigFactory.setConfigLocation(CORRECT_FILE_NAME);
        ConfigValidator configValidator = ConfigFactory.getConfigValidator(getAllTestProperties());
        Assert.assertTrue(configValidator.isValid());
        checkConfigProperties("check after load correct file", MODULE_PROPERTY_DEFAULT, FIRST_VALUE_DEFAULT, SECOND_VALUE_DEFAULT, THIRD_VALUE_DEFAULT, configValidator);
        ConfigFactory.setConfigLocation(EMPTY_FILE_NAME);
        checkConfigProperties("empty file should all empty properties", null, null, null, null, configValidator);
        Assert.assertFalse(configValidator.isValid());
        List<Object> unfoundPropertiesAfterValidation = configValidator.getUnfoundPropertiesAfterValidation();
        for (String expectedKey : getAllTestProperties()) {
            Assert.assertTrue(unfoundPropertiesAfterValidation.contains(expectedKey));
        }
    }


    @Test
    public void reloadEmptyAndProgrammaticalyAddPropertiesTest() throws Exception {
        ConfigFactory.setConfigLocation(EMPTY_FILE_NAME);
        String dummyValueKey1 = "dummyValueKey1";
        String dummyValueKey2 = "dummyValueKey2";
        String dummyValueKey3 = "dummyValueKey3";
        String dummyValueModuleKey = "dummyValueKey4";

        ConfigFactory.getConfigValidator().setProperty(PROPERTY_KEY_1, dummyValueKey1);
        ConfigFactory.getConfigValidator().setProperty(PROPERTY_KEY_2, dummyValueKey2);
        ConfigFactory.getConfigValidator().setProperty(PROPERTY_KEY_3, dummyValueKey3);
        ConfigFactory.getConfigValidator().setProperty(ConfigurationModuleTester.TEST_KEY, dummyValueModuleKey);

        checkConfigProperties("the inserted values should be retrieved without error", dummyValueModuleKey, dummyValueKey1, dummyValueKey2, dummyValueKey3, ConfigFactory.getConfigValidator(getAllTestProperties()));
    }

    @Test
    public void loadFileAndChangePropertiesTest() throws Exception {
        ConfigFactory.setConfigLocation(CORRECT_FILE_NAME);
        ConfigValidator configValidator = ConfigFactory.getConfigValidator(getAllTestProperties());
        checkConfigProperties("check after load correct file", MODULE_PROPERTY_DEFAULT, FIRST_VALUE_DEFAULT, SECOND_VALUE_DEFAULT, THIRD_VALUE_DEFAULT, configValidator);

        String dummyValueKey1 = "dummyValueKey1";
        String dummyValueKey2 = "dummyValueKey2";
        String dummyValueKey3 = "dummyValueKey3";
        String dummyValueModuleKey = "dummyValueKey4";

        ConfigFactory.getConfigValidator().setProperty(PROPERTY_KEY_1, dummyValueKey1);
        ConfigFactory.getConfigValidator().setProperty(PROPERTY_KEY_2, dummyValueKey2);
        ConfigFactory.getConfigValidator().setProperty(PROPERTY_KEY_3, dummyValueKey3);
        ConfigFactory.getConfigValidator().setProperty(ConfigurationModuleTester.TEST_KEY, dummyValueModuleKey);

        checkConfigProperties("the inserted values should be retrieved without error", dummyValueModuleKey, dummyValueKey1, dummyValueKey2, dummyValueKey3, configValidator);

    }

    @Test
    public void reloadMultipleTimesPerformanceTest() throws Exception {
        Date before = new Date();
        for (int i = 0; i < 10; i++) {
            ConfigFactory.setConfigLocation(CORRECT_FILE_NAME);
            checkConfigProperties("check after load correct file", MODULE_PROPERTY_DEFAULT, FIRST_VALUE_DEFAULT, SECOND_VALUE_DEFAULT, THIRD_VALUE_DEFAULT, ConfigFactory.getConfigValidator(getAllTestProperties()));
            ConfigFactory.setConfigLocation(EMPTY_FILE_NAME);
            checkConfigProperties("empty file should have all empty properties", null, null, null, null, ConfigFactory.getConfigValidator());
            ConfigFactory.setConfigLocation(CHANGED_FILE_NAME);
            checkConfigProperties("check after load correct file", MODULE_PROPERTY_CHANGED, FIRST_VALUE_CHANGED, SECOND_VALUE_CHANGED, THIRD_VALUE_CHANGED, ConfigFactory.getConfigValidator(getAllTestProperties()));


        }
        Date after = new Date();
        LOG.debug("reloadMultipleTimesPerformanceTest : time : " + (after.getTime() - before.getTime()));
    }

    @Test(expected = IllegalArgumentException.class)
    public void testGetConfigValidatorForWithNullValue() throws Exception {
        ConfigFactory.getConfigValidatorFor((String[]) null);
    }

    @Test
    public void testGetConfigValidatorForWithoutValue() throws Exception {
        ConfigValidator validator = ConfigFactory.getConfigValidatorFor();
        Assert.assertNotNull(validator);
    }

    @Test
    public void reloadMultipleTimesWithRealFilePerformanceTest() throws Exception {
        Date before = new Date();
        for (int i = 0; i < 10; i++) {
            ConfigFactory.setConfigLocation(CORRECT_FILE_NAME);
            checkConfigProperties("check after load correct file", MODULE_PROPERTY_DEFAULT, FIRST_VALUE_DEFAULT, SECOND_VALUE_DEFAULT, THIRD_VALUE_DEFAULT, ConfigFactory.getConfigValidator(getAllTestProperties()));
            ConfigFactory.setConfigLocation(PROP_SIMILAR_PROD);
            checkConfigProperties("production file should not have test properties", null, null, null, null, ConfigFactory.getConfigValidator());
        }
        Date after = new Date();
        long time = after.getTime() - before.getTime();
        LOG.debug("reloadMultipleTimesWithRealFilePerformanceTest : time : " + time);
    }

    @Test(expected = TechnicalConnectorException.class)
    public void loadMissingFileFromFileSystem() throws Exception {
        ConfigFactory.setConfigLocation("/wrongLocation");
    }

    @Test(expected = TechnicalConnectorException.class)
    public void loadMissingFileFromFileSystemAbsolutePath() throws Exception {
        ConfigFactory.setConfigLocation("c:\temp/wrongLocation");
    }

    @Test(expected = TechnicalConnectorException.class)
    public void loadMissingFileFromFileSystemJustFileName() throws Exception {
        ConfigFactory.setConfigLocation("wrongLocation");
    }


}
