package com.atlassian.bitbucket.mesh.util;

import com.atlassian.bitbucket.mesh.io.MoreFiles;
import com.google.common.collect.ImmutableSet;
import org.apache.commons.io.IOUtils;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Properties;
import java.util.Set;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;

public class PropertiesUtilsTest {

    @Rule
    public final TemporaryFolder temporaryFolder = new TemporaryFolder();

    @Test
    public void testUpdateWhenFileDoesNotExist() throws IOException {
        Path path = temporaryFolder.getRoot().toPath().resolve("example.properties");

        Properties original = new Properties();
        original.put("test.key", "test.value");
        original.put("empty.key", "");

        Set<String> updatedKeys = PropertiesUtils.update(original, path, "# BANNER");
        assertTrue(Files.isRegularFile(path)); //The properties file should have been created
        String contents = MoreFiles.toString(path);
        assertTrue(contents.startsWith("# BANNER\n"));

        Properties loaded = PropertiesUtils.load(path);
        assertEquals(original.getProperty("test.key"), loaded.getProperty("test.key"));
        assertFalse(loaded.contains("empty.key")); //The empty key should have been dropped
        assertEquals(1, loaded.size());
        assertEquals(ImmutableSet.of("test.key"), updatedKeys);
    }

    @Test
    public void testUpdateWhenFileDoesNotExistAndMapIsEmpty() throws IOException {
        Path path = temporaryFolder.getRoot().toPath().resolve("example.properties");
        Properties original = new Properties();

        Set<String> updatedKeys = PropertiesUtils.update(original, path, "# BANNER");
        assertTrue(Files.isRegularFile(path)); //Properties file should have been created
        assertThat(updatedKeys, emptyCollectionOf(String.class)); //No keys should have been updated

        String contents = MoreFiles.toString(path);
        assertTrue(contents.startsWith("# BANNER\n"));

        Properties loaded = PropertiesUtils.load(path);
        assertThat(loaded, anEmptyMap());
    }

    @Test
    public void testUpdateExistingFile() throws IOException {
        Path path = temporaryFolder.getRoot().toPath().resolve("example.properties");
        //Write a properties file with tricky content to the path
        Files.write(path, loadResource("/properties-utils-test-before.properties"));

        Properties properties = loadPropertiesFromClasspath("/properties-utils-test-delta.properties");

        Set<String> updatedKeys = PropertiesUtils.update(properties, path, null);
        assertTrue(Files.isRegularFile(path));

        String expected = new String(loadResource("/properties-utils-test-expected.properties"), StandardCharsets.UTF_8);
        assertEquals(expected, MoreFiles.toString(path));
        Set<String> expectedUpdates = ImmutableSet.of("management.metrics.export.defaults.enabled", "jmx.enabled",
                "extra.property1", "extra.property2", "extra.property3");
        assertEquals(expectedUpdates, updatedKeys);
    }

    @Test
    public void testUpdateExistingFileWithEmptyMap() throws IOException {
        Path path = temporaryFolder.getRoot().toPath().resolve("example.properties");
        MoreFiles.write(path, "existing.key=existing value\n");

        Set<String> updatedKeys = PropertiesUtils.update(new Properties(), path, null);
        assertTrue(Files.isRegularFile(path));
        assertThat(updatedKeys, emptyCollectionOf(String.class));

        Properties loaded = PropertiesUtils.load(path);
        assertThat(loaded, hasEntry("existing.key", "existing value"));
    }

    private Properties loadPropertiesFromClasspath(String resource) throws IOException {
        Properties loaded = new Properties();
        try (InputStream inputStream = getClass().getResourceAsStream(resource)) {
            loaded.load(inputStream);
        }
        return loaded;
    }

    private byte[] loadResource(String classPathResource) throws IOException {
        try (InputStream inputStream = getClass().getResourceAsStream(classPathResource)) {
            return IOUtils.toByteArray(inputStream);
        }
    }
}