package com.atlassian.bitbucket.installer.scripts;

import com.atlassian.bitbucket.installer.InstallationDetails;
import com.atlassian.bitbucket.installer.Platform;
import com.atlassian.bitbucket.installer.validation.HardValidationException;
import com.atlassian.bitbucket.installer.validation.SoftValidationException;
import com.install4j.api.Util;
import com.install4j.api.context.InstallerContext;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.ArgumentCaptor;

import java.io.File;

import static com.atlassian.bitbucket.installer.InstallerConstants.*;
import static com.atlassian.bitbucket.installer.validation.HardValidationException.*;
import static org.junit.Assert.*;
import static org.junit.Assume.assumeTrue;
import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class InitializationScriptTest extends BaseInstallerTest {

    private static final String WARNING = "WARNING";
    @Rule
    public final TemporaryFolder temporaryFolder = new TemporaryFolder();

    private InitializationScript initializationScript;

    @Before
    public void setup() {
        InstallerContext context = getContext();

        when(context.getVariable(KEY_INSTALLATION_DETAILS)).thenReturn(null);
        when(getUtils().showConfirmDialog(any(String.class), any(String.class), any(String.class), any(Boolean.class))).thenReturn(true);

        InstallationDetails.setContext(context);
        initializationScript = new InitializationScript(context, getUtils());

    }

    @After
    public void after() {
        InstallationDetails.setContext(null);
    }

    @Test
    public void testShouldFailIfInstallationDirectoryIsNotEmpty() throws Exception {
        when(getContext().getMessage(eq(HardValidationException.ERROR_DIRECTORY_USED), aryEq(new String[]{"installation"})))
                .thenReturn(WARNING);

        temporaryFolder.newFile("garbage");
        when(getContext().getVariable("app.defaultInstallDir")).thenReturn(temporaryFolder.getRoot().getAbsolutePath());
        assertFalse(initializationScript.initialiseInstallation());

        verify(getUtils()).showWarningMessage(WARNING);
    }

    @Test
    public void testShouldFailIfInstallationDirectoryPathHasSpaces() throws Exception {
        when(getContext().getMessage(eq(ERROR_DIRECTORY_SPACES), aryEq(new String[]{"installation"})))
                .thenReturn(WARNING);

        File installationDirectory = temporaryFolder.newFolder("path with spaces");
        when(getContext().getVariable("app.defaultInstallDir")).thenReturn(installationDirectory.getAbsolutePath());

        assertFalse(initializationScript.initialiseInstallation());

        verify(getUtils()).showWarningMessage(WARNING);
    }

    @Test
    public void testShouldNotFailIfHomeDirectoryIsNotEmpty() throws Exception {
        when(getContext().getMessage(eq(SoftValidationException.ERROR_DIRECTORY_USED), aryEq(new String[]{"home"})))
                .thenReturn(WARNING);

        temporaryFolder.newFile("garbage");
        when(getContext().getVariable(KEY_APP_BITBUCKET_HOME)).thenReturn(temporaryFolder.getRoot().getAbsolutePath());

        assertTrue(initializationScript.initialiseInstallation());

        verify(getUtils()).showWarningMessage(WARNING);
    }

    @Test
    public void testShouldFailIfHomeDirectoryPathHasSpaces() throws Exception {
        when(getContext().getMessage(eq(ERROR_DIRECTORY_SPACES), aryEq(new String[]{"home"})))
                .thenReturn(WARNING);

        File installationDirectory = temporaryFolder.newFolder("path with spaces");
        when(getContext().getVariable(KEY_APP_BITBUCKET_HOME)).thenReturn(installationDirectory.getAbsolutePath());

        assertFalse(initializationScript.initialiseInstallation());

        verify(getUtils()).showWarningMessage(WARNING);
    }

    @Test
    public void testIfAppDefaultInstallationDirVariablePresentItShouldBeUsedAsInstallDirectory() throws Exception {
        String installDir = Platform.current().getAdminInstallPrefix() + File.separator + "custom-folder";
        when(getContext().getVariable("app.defaultInstallDir")).thenReturn(installDir);

        assertTrue(initializationScript.initialiseInstallation());

        validateInstallDir(installDir);
    }

    @Test
    public void testShouldFailIfHttpPortIsNotANumber() throws Exception {
        when(getContext().getMessage(I18N_ERROR_HTTP_PORT_NUMBER_INVALID)).thenReturn(WARNING);

        when(getContext().getVariable("portChoice")).thenReturn("custom");
        when(getContext().getVariable(KEY_HTTP_PORT)).thenReturn("garbage");

        assertFalse(initializationScript.initialiseInstallation());

        verify(getUtils()).showWarningMessage(WARNING);
    }

    @Test
    public void testShouldFailIfControlPortIsNotANumber() throws Exception {
        when(getContext().getMessage(I18N_ERROR_HTTP_PORT_NUMBER_INVALID)).thenReturn(WARNING);

        when(getContext().getVariable("portChoice")).thenReturn("custom");
        when(getContext().getVariable(KEY_CONTROL_PORT)).thenReturn("garbage");

        assertFalse(initializationScript.initialiseInstallation());

        verify(getUtils()).showWarningMessage(WARNING);
    }

    @Test
    public void testShouldFailIfPortsFailValidation() throws Exception {
        when(getContext().getMessage(ERROR_PORT_SAME)).thenReturn(WARNING);

        when(getContext().getVariable("portChoice")).thenReturn("custom");

        String commonPort = "7990";
        when(getContext().getVariable(KEY_HTTP_PORT)).thenReturn(commonPort);
        when(getContext().getVariable(KEY_CONTROL_PORT)).thenReturn(commonPort);

        assertFalse(initializationScript.initialiseInstallation());

        verify(getUtils()).showWarningMessage(WARNING);
    }

    @Test
    public void testShouldFailIfServiceInstallationForNonAdminUser() throws Exception {
        when(getContext().getMessage(ERROR_SERVICE_ADMIN, new String[]{getContext().getCompilerVariable("sys.shortName")})).thenReturn(WARNING);

        when(getContext().getVariable(KEY_APP_INSTALL_SERVICE)).thenReturn(Boolean.TRUE);
        when(getContext().getBooleanVariable(KEY_APP_INSTALL_SERVICE)).thenReturn(Boolean.TRUE);

        when(getUtils().hasFullAdminRights()).thenReturn(false);

        assertFalse(initializationScript.initialiseInstallation());

        verify(getUtils()).showWarningMessage(WARNING);
    }

    private void validateInstallDir(String installDir) {
        ArgumentCaptor<InstallationDetails> objectCaptor = ArgumentCaptor.forClass(InstallationDetails.class);
        verify(getContext()).setVariable(eq("installation.details"), objectCaptor.capture());
        InstallationDetails installationDetails = objectCaptor.getValue();
        assertEquals(installDir, installationDetails.getInstallationDirectory().getAbsolutePath());
    }
}
