package com.atlassian.bitbucket.installer.scripts;

import com.atlassian.bitbucket.installer.InstallationDetails;
import com.atlassian.bitbucket.installer.InstallationException;
import com.atlassian.bitbucket.installer.InstallationType;
import com.atlassian.bitbucket.installer.utils.InstallerUtils;
import com.atlassian.bitbucket.server.ApplicationMode;
import com.atlassian.stash.internal.ApplicationSettings;
import com.install4j.api.Util;
import com.install4j.api.context.InstallerContext;
import com.install4j.api.windows.SpecialFolder;
import com.install4j.api.windows.WinFileSystem;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.IOException;

import static com.atlassian.bitbucket.installer.InstallerConstants.KEY_ELASTICSEARCH_LOG_PATH;
import static com.atlassian.bitbucket.installer.InstallerConstants.KEY_STASH_LOG_PATH;

public class InstallationScript extends AbstractInstallerScript {

    public InstallationScript(InstallerContext context, InstallerUtils utils) {
        super(context, utils);
    }

    /**
     * Setting log path to context, so that we will be able to know the full path to the Bitbucket log.
     * This path will be used when Bitbucket cannot start correctly.
     */
    public void preActivationScriptOnInstallation() {
        String logPath = getInstallationDetails().getHomeDirectory().getAbsolutePath() + File.separator + ApplicationSettings.LOG_PATH + File.separator;
        getContext().setVariable(KEY_STASH_LOG_PATH, logPath);
        getUtils().logInfo(String.format("The log path is set to - %s.", logPath));

        String elasticsearchLogPath = logPath + "search" + File.separator;
        getContext().setVariable(KEY_ELASTICSEARCH_LOG_PATH, elasticsearchLogPath);
        getUtils().logInfo(String.format("The Elasticsearch log path is set to - %s.", elasticsearchLogPath));
    }

    /**
     * Creates the Home Directory specified in the InstallationDetails
     * @return true if the directory was created or already exists.
     */
    public boolean createHomeDirectoryIfMissing() {
        return createDirectoryIfMissing(getInstallationDetails().getHomeDirectory());
    }

    /**
     * Creates the shared home directory specified in the InstallationDetails.
     * @return true if the directory was created or already exists.
     */
    public boolean createSharedHomeDirectoryIfMissing() {
        return createDirectoryIfMissing(getInstallationDetails().getHomeSharedDirectory());
    }

    /**
     * Output any {@code bitbucket.properties} needed by the install.
     */
    public boolean writeBitbucketProperties() {
        try {
            InstallationDetails installationDetails = getInstallationDetails();
            InstallationType installType = installationDetails.getInstallType();

            //We only try to change the bitbucket.properties on mirror install where it is required to set the
            //application mode (default is standard bitbucket).
            if (installType == InstallationType.MIRROR_INSTALL) {
                BitbucketConfig.fromFile(installationDetails.getHomeSharedDirectory())
                        .setApplicationMode(ApplicationMode.MIRROR)
                        .write();
            }
            return true;
        } catch (InstallationException e) {
            InstallerUtils utils = getUtils();
            utils.logError("Unable to write bitbucket.properties.", e);
            utils.showErrorMessage(e.getMessage(getContext()));
            return false;
        }
    }

    /**
     * Deletes a program group, doesn't delete if {@code name} is same as the current program group name.
     *
     * @param name the name of the program group to create
     * @return      true if the program group was deleted,
     *              false if {@code name} is equal to the current program group name or if an error occurred while deleting
     */
    public boolean deleteProgramGroup(String name) {
        String configuredProgramGroupName = (String) getContext().getVariable("app.programGroupName");
        if (!StringUtils.equals(name, configuredProgramGroupName)) {
            File programGroupDir = WinFileSystem.getSpecialFolder(SpecialFolder.PROGRAMS, false);
            File programGroup = new File(programGroupDir, name);
            try {
                FileUtils.deleteDirectory(programGroup);
                getUtils().logInfo(String.format("Deleted program group - %s.", programGroup.getAbsolutePath()));
                return true;
            } catch (IOException e) {
                getUtils().logInfo(String.format("Failed to delete start menu - %s. Error: %s", name, e.getMessage()));
                return false;
            }
        }
        return false;
    }

    /**
     * Updates the response varfile generated by install4j. Adds "app.defaultInstallDir" and removes "sys.installationDir"
     * from the response varfile, we don't want two variables defining the same thing, this results in unnecessary
     * complicated code.
     *
     * @return true
     */
    public boolean updateResponseVarfile() {
        getContext().setVariable("app.defaultInstallDir", getContext().getVariable("sys.installationDir"));
        getContext().registerResponseFileVariable("app.defaultInstallDir");
        getContext().unregisterResponseFileVariable("sys.installationDir");
        getContext().unregisterResponseFileVariable("elasticsearch.install.service");
        return true;
    }

    /**
     * Creates a directory if it does not yet exist.
     * @param directory the directory to be created
     * @return true if the directory was created or already exists. false otherwise.
     */
    private boolean createDirectoryIfMissing(File directory) {
        if (directory.exists()) {
            Util.logInfo(this, String.format("Directory \"%s\" already exists. Skipping.", directory.getAbsolutePath()));
            return true;
        }
        return directory.mkdirs();
    }
}
