package be.ehealth.technicalconnector.config.impl;

import java.util.Properties;

import org.junit.Assert;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import be.ehealth.technicalconnector.config.UddiKeys;
import be.ehealth.technicalconnector.exception.ConfigurationException;
import be.ehealth.technicalconnector.exception.TechnicalConnectorException;
import be.fgov.ehealth.technicalconnector.tests.junit.rule.LoggingRule;

/**
 * Unit test to verify the behavior of the {@link RecursiveProperties}
 *
 * @author EHP
 */
public class RecursivePropertiesTest {

    @ClassRule
    public static LoggingRule logging = LoggingRule.with()
                                                   .consoleAppender()
                                                   .build();

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

    public Properties props = new RecursiveProperties(RecursivePropertiesTest.class.getResourceAsStream("/uddi/uddi-default.properties"));

    @Test
    public void unknownProperty() {
        Assert.assertNull(props.getProperty("endpoint.unknown"));
    }

    @Test
    public void circularRefence() {
        expectCircularRefence();

        props.setProperty("lookup", "${loop}");
        props.setProperty("loop", "${lookup}");

        Assert.assertEquals("found", props.getProperty("${lookup}", "nofound"));
    }

    @Test
    public void recursiveNestedStandardProp() {
        props.setProperty("lookup", "ok");
        props.setProperty("nested.lookup", "nested? ${lookup}");
        Assert.assertEquals("nested? ok", props.getProperty("${nested.lookup}", "nested? nok"));
    }

    @Test
    public void recursiveNestedSystemProp() {
        System.setProperty("lookup", "ok");
        props.setProperty("nested.lookup", "nested system? $system{lookup}");
        Assert.assertEquals("nested system? ok", props.getProperty("${nested.lookup}", "nested? nok"));
    }

    @Test
    public void nestedUnknownStandardProp() {
        props.setProperty("nested.lookup", "nested.unknown[${lookup}]");
        Assert.assertEquals("nested.unknown[]", props.getProperty("${nested.lookup}", "defaultValue"));
    }

    @Test
    public void circularReferenceThroughSystemProps() {
        expectCircularRefence();

        props.setProperty("lookup", "$system{system.loop}");
        System.setProperty("system.loop", "${loop}");
        props.setProperty("loop", "${lookup}");

        Assert.assertEquals("found", props.getProperty("${lookup}", "nofound"));
    }

    @Test
    public void recursiveLookup() {

        props.setProperty("lookup", "found");
        Assert.assertEquals("found", props.getProperty("${lookup}", "nofound"));
    }

    @Test
    public void unknownUddiProp() {
        Assert.assertNull(props.getProperty("$uddi{endpoint.unknown}"));
    }

    @Test
    public void directUddiProp() {
        Assert.assertEquals("https://services.ehealth.fgov.be/CertRa/v1", props.getProperty(UddiKeys.UDDI_CERT_RA_V1, "endpoint.unknown"));
    }

    @Test
    public void defaultUddiProp() {
        Assert.assertEquals("https://services.ehealth.fgov.be/CertRa/v1", props.getProperty("endpoint.unknown", UddiKeys.UDDI_CERT_RA_V1));
    }

    @Test
    public void unknownSystemProp() {
        Assert.assertNull(props.getProperty("$system{endpoint.unknown}"));
    }

    @Test
    public void directSystemProp() {
        Assert.assertEquals(System.getProperty("java.io.tmpdir"), props.getProperty("$system{java.io.tmpdir}", "not found"));
    }

    @Test
    public void defaultSystemProp() {
        Assert.assertEquals(System.getProperty("java.io.tmpdir"), props.getProperty("endpoint.unknown", "$system{java.io.tmpdir}"));
    }

    @Test
    public void defaultStandardProp() {
        Assert.assertEquals("not found", props.getProperty("endpoint.unknown", "not found"));
    }

    @Test
    public void directStandardProp() {
        props.setProperty("endpoint.known", "found");
        Assert.assertEquals("found", props.getProperty("endpoint.known", "not found"));
    }

    @Test
    public void nullKey() {
        Assert.assertEquals("not found", props.getProperty(null, "not found"));

    }

    @Test
    public void emptyStandardProp() {
        props.setProperty("empty", "");
        Assert.assertEquals("", props.getProperty("empty", "defaultValue"));
    }

    @Test
    public void recursiveEmptyProps() {
        props.setProperty("empty", "");
        props.setProperty("lookup.empty", "${empty}");
        Assert.assertEquals("", props.getProperty("lookup.empty", "defaultValue"));
    }

    @Test
    public void defaultConstructor() throws TechnicalConnectorException {
        new RecursiveProperties();
    }

    private void expectCircularRefence() {
        thrown.expect(ConfigurationException.class);
        thrown.expectMessage("A circular reference detected");
    }
}