package com.atlassian.stash.internal.catalina.startup;

import org.junit.*;
import org.junit.rules.TemporaryFolder;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import static org.junit.Assert.assertArrayEquals;

public class BootstrapTest {
    @Rule
    public TemporaryFolder temporaryFolder = new TemporaryFolder();

    private String startingBitbucketHome;
    private String startingBitbucketSharedHome;
    private String startingStashHome;
    private String startingStashSharedHome;

    @Before
    public void captureSystemProperties() {
        startingBitbucketHome = System.getProperty("bitbucket.home");
        startingBitbucketSharedHome = System.getProperty("bitbucket.shared.home");
        startingStashHome = System.getProperty("stash.home");
        startingStashSharedHome = System.getProperty("stash.shared.home");
    }

    @BeforeClass
    public static void initLog() {
        Bootstrap.initLog();
    }

    @After
    public void restoreSystemProperties() {
        doRestoreSystemProperty("bitbucket.home", startingBitbucketHome);
        doRestoreSystemProperty("bitbucket.shared.home", startingBitbucketSharedHome);
        doRestoreSystemProperty("stash.home", startingStashHome);
        doRestoreSystemProperty("stash.shared.home", startingStashSharedHome);
    }

    @Test
    public void testEmptyArguments() throws IOException {
        assertArrayEquals(new String[]{}, new EnvVarOverridingArgumentDecorator().apply(new String[]{}));
    }

    @Test
    public void testArgumentsUntouchedIfConfigSuppliedAndPropsSet() throws IOException {
        File serverXml = new File(temporaryFolder.getRoot(), "server.xml");
        serverXml.createNewFile();
        String[] rawArgs = new String[]{"-config", "foo", "startup"};
        String[] expectedArgs = new String[]{"-config", "foo", "startup"};
        doTestArgumentsWithSystemProperty(expectedArgs, rawArgs, "bitbucket.home");
        doTestArgumentsWithSystemProperty(expectedArgs, rawArgs, "bitbucket.shared.home");
    }

    @Test
    public void testArgumentsUntouchedIfConfigSuppliedAndEnvVarsSupplied() throws IOException {
        File serverXml = new File(temporaryFolder.getRoot(), "server.xml");
        serverXml.createNewFile();
        String[] rawArgs = new String[]{"-config", "foo", "startup"};
        String[] expectedArgs = new String[]{"-config", "foo", "startup"};
        doTestArgumentsWithEnvironmentVariable(expectedArgs, rawArgs, "BITBUCKET_HOME");
        doTestArgumentsWithEnvironmentVariable(expectedArgs, rawArgs, "BITBUCKET_SHARED_HOME");
    }

    @Test
    public void testArgumentsUntouchedIfServerXmlNotFoundAndPropsSet() throws IOException {
        String[] rawArgs = new String[]{"startup"};
        String[] expectedArgs = new String[]{"startup"};
        doTestArgumentsWithSystemProperty(expectedArgs, rawArgs, "bitbucket.home");
        doTestArgumentsWithSystemProperty(expectedArgs, rawArgs, "bitbucket.shared.home");
    }

    @Test
    public void testArgumentsUntouchedIfServerXmlNotFoundAndEnvVarsSupplied() throws IOException {
        String[] rawArgs = new String[]{"startup"};
        String[] expectedArgs = new String[]{"startup"};
        doTestArgumentsWithEnvironmentVariable(expectedArgs, rawArgs, "BITBUCKET_HOME");
        doTestArgumentsWithEnvironmentVariable(expectedArgs, rawArgs, "BITBUCKET_SHARED_HOME");
    }

    @Test
    public void testServerXmlPrependedIfFoundAndPropsSet() throws IOException {
        File serverXml = new File(temporaryFolder.getRoot(), "server.xml");
        serverXml.createNewFile();
        String[] rawArgs = new String[]{"startup"};
        String[] expectedArgs = new String[]{"-config", serverXml.getCanonicalPath(), "startup"};
        doTestArgumentsWithSystemProperty(expectedArgs, rawArgs, "bitbucket.home");
        doTestArgumentsWithSystemProperty(expectedArgs, rawArgs, "bitbucket.shared.home");
    }

    @Test
    public void testServerXmlPrependedIfFoundAndEnvVarsSupplied() throws IOException {
        File serverXml = new File(temporaryFolder.getRoot(), "server.xml");
        serverXml.createNewFile();
        String[] rawArgs = new String[]{"startup"};
        String[] expectedArgs = new String[]{"-config", serverXml.getCanonicalPath(), "startup"};
        doTestArgumentsWithEnvironmentVariable(expectedArgs, rawArgs, "BITBUCKET_HOME");
        doTestArgumentsWithEnvironmentVariable(expectedArgs, rawArgs, "BITBUCKET_SHARED_HOME");
    }

    @Test
    public void testServerXmlPrependedIfFoundInSharedDirectoryAndPropsSet() throws IOException {
        File serverXml = new File(temporaryFolder.getRoot(), "shared/server.xml");
        serverXml.getParentFile().mkdirs();
        serverXml.createNewFile();
        String[] rawArgs = new String[]{"startup"};
        String[] expectedArgs = new String[]{"-config", serverXml.getCanonicalPath(), "startup"};
        doTestArgumentsWithSystemProperty(expectedArgs, rawArgs, "bitbucket.home");
    }

    @Test
    public void testServerXmlPrependedIfFoundInSharedDirectoryAndEnvVarsSupplied() throws IOException {
        File serverXml = new File(temporaryFolder.getRoot(), "shared/server.xml");
        serverXml.getParentFile().mkdirs();
        serverXml.createNewFile();
        String[] rawArgs = new String[]{"startup"};
        String[] expectedArgs = new String[]{"-config", serverXml.getCanonicalPath(), "startup"};
        doTestArgumentsWithEnvironmentVariable(expectedArgs, rawArgs, "BITBUCKET_HOME");
    }

    @Test
    public void testServerXmlPrependedIfFoundAndLegacyPropsSet() throws IOException {
        File serverXml = new File(temporaryFolder.getRoot(), "server.xml");
        serverXml.createNewFile();
        String[] rawArgs = new String[]{"startup"};
        String[] expectedArgs = new String[]{"-config", serverXml.getCanonicalPath(), "startup"};
        doTestArgumentsWithSystemProperty(expectedArgs, rawArgs, "stash.home");
        doTestArgumentsWithSystemProperty(expectedArgs, rawArgs, "stash.shared.home");
    }

    @Test
    public void testServerXmlPrependedIfFoundAndLegacyEnvVarsSupplied() throws IOException {
        File serverXml = new File(temporaryFolder.getRoot(), "server.xml");
        serverXml.createNewFile();
        String[] rawArgs = new String[]{"startup"};
        String[] expectedArgs = new String[]{"-config", serverXml.getCanonicalPath(), "startup"};
        doTestArgumentsWithEnvironmentVariable(expectedArgs, rawArgs, "STASH_HOME");
        doTestArgumentsWithEnvironmentVariable(expectedArgs, rawArgs, "STASH_SHARED_HOME");
    }

    @Test
    public void testServerXmlUsedIfFoundAndArgumentsEmptyAndPropsSet() throws IOException {
        File serverXml = new File(temporaryFolder.getRoot(), "server.xml");
        serverXml.createNewFile();
        String[] rawArgs = new String[]{};
        String[] expectedArgs = new String[]{"-config", serverXml.getCanonicalPath()};
        doTestArgumentsWithSystemProperty(expectedArgs, rawArgs, "bitbucket.home");
        doTestArgumentsWithSystemProperty(expectedArgs, rawArgs, "bitbucket.shared.home");
    }

    @Test
    public void testServerXmlUsedIfFoundAndArgumentsEmptyAndEnvVarsSupplied() throws IOException {
        File serverXml = new File(temporaryFolder.getRoot(), "server.xml");
        serverXml.createNewFile();
        String[] rawArgs = new String[]{};
        String[] expectedArgs = new String[]{"-config", serverXml.getCanonicalPath()};
        doTestArgumentsWithEnvironmentVariable(expectedArgs, rawArgs, "BITBUCKET_HOME");
        doTestArgumentsWithEnvironmentVariable(expectedArgs, rawArgs, "BITBUCKET_SHARED_HOME");
    }

    private void doRestoreSystemProperty(String propName, String startingValue) {
        if (startingValue == null) {
            System.clearProperty(propName);
        } else {
            System.setProperty(propName, startingValue);
        }
    }

    private void doTestArgumentsWithEnvironmentVariable(String[] expectedArgs, String[] rawArgs, String varName) throws IOException {
        assertArrayEquals(expectedArgs, new EnvVarOverridingArgumentDecorator(varName,
                temporaryFolder.getRoot().getCanonicalPath()).apply(rawArgs));
    }

    private void doTestArgumentsWithSystemProperty(String[] expectedArgs, String[] rawArgs, String propName) throws IOException {
        System.setProperty(propName, temporaryFolder.getRoot().getCanonicalPath());
        assertArrayEquals(expectedArgs, new EnvVarOverridingArgumentDecorator().apply(rawArgs));
        System.clearProperty(propName);
    }

    private static class EnvVarOverridingArgumentDecorator extends Bootstrap.ArgumentDecorator {
        private Map<String, String> envVars = new HashMap<String, String>();

        public EnvVarOverridingArgumentDecorator(String... kvp) {
            for (int i = 0; i < kvp.length; i += 2) {
                envVars.put(kvp[i], kvp[i + 1]);
            }
        }

        @Override
        protected String getEnvVariable(String name) {
            return envVars.get(name);
        }
    }
}