package com.atlassian.bitbucket.mesh.app;

import com.atlassian.bitbucket.mesh.junit.LogListener;
import com.atlassian.bitbucket.mesh.junit.TemporaryDirectory;
import com.atlassian.bitbucket.mesh.util.Version;
import org.junit.Rule;
import org.junit.Test;

import static com.atlassian.bitbucket.mesh.app.HomeValidator.PATH_PROPERTIES;
import static com.atlassian.bitbucket.mesh.layout.StorageLayout.HOME_LAYOUT_VERSION;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThrows;

public class HomeValidatorTest {

    private static final Version BUILD = new Version(5, 14);

    @Rule
    public final LogListener logListener = new LogListener(HomeValidator.class);
    @Rule
    public final TemporaryDirectory tempDir = new TemporaryDirectory();

    @Test
    public void testValidate() throws Exception {
        prepareProperties(BUILD, HOME_LAYOUT_VERSION);

        validate();

        assertProperties(BUILD, HOME_LAYOUT_VERSION);
    }

    @Test
    public void testValidateAllowsDowngradesBetweenMinorVersions() throws Exception {
        Version newBuild = new Version(BUILD.getMajor(), BUILD.getMinor() + 1);
        Version newLayout = new Version(HOME_LAYOUT_VERSION.getMajor(), HOME_LAYOUT_VERSION.getMinor() + 1);

        prepareProperties(newBuild, newLayout);

        validate();

        assertProperties(newBuild, newLayout);
        logListener.assertWarn("will not roll back the home directory layout");
    }

    @Test
    public void testValidateAllowsDowngradesToSameLayout() throws Exception {
        Version newBuild = new Version(BUILD.getMajor(), BUILD.getMinor() + 1);

        prepareProperties(newBuild, HOME_LAYOUT_VERSION);

        validate();

        assertProperties(newBuild, HOME_LAYOUT_VERSION);
        logListener.assertWarn("Downgrading Mesh");
    }

    @Test
    public void testValidateBlocksDowngradesBetweenMajorVersions() throws Exception {
        Version newBuild = new Version(BUILD.getMajor() + 1);
        Version newLayout = new Version(HOME_LAYOUT_VERSION.getMajor() + 1);

        prepareProperties(newBuild, newLayout);

        IncompatibleHomeDirectoryException e = assertThrows(IncompatibleHomeDirectoryException.class, this::validate);
        assertThat(e.getMessage(), containsString("no longer compatible"));

        //If the versions in the files are newer than the ones the validator is applying, the
        //validator shouldn't update them prior to throwing an exception
        assertProperties(newBuild, newLayout);
        logListener.assertWarn("requires restoring from a backup");
    }

    @Test
    public void testValidateCreatesFiles() throws Exception {
        validate();

        //home.properties and .version should both be created
        assertProperties(BUILD, HOME_LAYOUT_VERSION);
    }

    @Test
    public void testValidateUpdatesFilesOnUpgrade() throws Exception {
        Version oldBuild = new Version(BUILD.getMajor() - 1);
        Version oldLayout = new Version(HOME_LAYOUT_VERSION.getMajor() - 1);

        prepareProperties(oldBuild, oldLayout);

        validate();

        assertProperties(BUILD, HOME_LAYOUT_VERSION);
    }

    private void assertProperties(Version buildVersion, Version layoutVersion) throws Exception {
        HomeProperties properties = HomeProperties.load(tempDir.getRoot().resolve(PATH_PROPERTIES));
        assertThat(properties, allOf(
                hasProperty("build", equalTo(buildVersion)),
                hasProperty("layout", equalTo(layoutVersion))));
    }

    private void prepareProperties(Version buildVersion, Version layoutVersion) throws Exception {
        new HomeProperties(buildVersion, layoutVersion)
                .store(tempDir.newFile(PATH_PROPERTIES));
    }

    private void validate() {
        new HomeValidator(tempDir.getRoot(), BUILD).validate();
    }
}