package com.atlassian.bitbucket.installer;

import com.atlassian.bitbucket.installer.analytics.PingbackClient;
import com.atlassian.bitbucket.installer.scripts.*;
import com.atlassian.bitbucket.installer.utils.InstallerUtils;
import com.atlassian.bitbucket.installer.validation.*;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.install4j.api.context.InstallerContext;
import org.apache.http.impl.client.HttpClients;

import static java.util.Collections.emptySet;

/**
 * InstallerFactory handles the creation of screen scipts.
 * It's created to provide handy one-liner in .install4j xml file.
 *
 * <p>In .install4j, if you want to trigger a method in {@link HomeLocationScreen}
 * You could do something like
 * {@code
 *  <preActivation>
 *      InstallerFactory.getHomeLocationScreen(context).initHomeDirectoryScreen();
 *  </preActivation>
 * }
 */
public class InstallerFactory {
    private static InstallerUtils utils = InstallerUtils.getInstance();
    private static PingbackClient pingbackClient = new PingbackClient(HttpClients.createDefault());
    private static Tracker tracker = new Tracker(pingbackClient);

    public static ConfirmDisablePluginsScreen getConfirmDisablePluginsScreen(InstallerContext context) {
        return new ConfirmDisablePluginsScreen(context, utils);
    }

    public static HomeLocationScreen getHomeLocationScreen(InstallerContext context) {
        return new HomeLocationScreen(context, utils);
    }

    public static InitializationScript getInitialisationScript(InstallerContext context) {
        return new InitializationScript(context, utils);
    }

    public static InstallAsServiceScreen getInstallAsServiceScreen(InstallerContext context) {
        return new InstallAsServiceScreen(context, utils);
    }

    public static InstallationSetupScripts getInstallationSetupScripts(InstallerContext context) {
        return new InstallationSetupScripts(context, new GitValidator(context, utils), utils);
    }

    public static SummaryScreen getSummaryScreen(InstallerContext context) {
        return new SummaryScreen(context, utils);
    }

    public static InstallationScript getInstallationScript(InstallerContext context) {
        return new InstallationScript(context, utils);
    }

    public static InstallationLocationScreen getInstallationLocationScreen(InstallerContext context) {
        return new InstallationLocationScreen(context, utils);
    }

    public static DisplayProgressScreen getDisplayProgressScreen(InstallerContext context) {
        return new DisplayProgressScreen(context, utils);
    }

    public static Tracker getTracker() {
        return tracker;
    }

    public static DirectoryValidationStrategy getHomeDirectoryValidationStrategy(InstallationType type) {
        if (type.isUpgrade()) {
            return new HomeDirectoryValidationStrategy();
        } else {
            ImmutableList.Builder<DirectoryValidationStrategy> delegates = ImmutableList.builder();
            delegates.add(new PathSpacesValidationStrategy());
            if (type == InstallationType.MIRROR_INSTALL) {
                //We don't allow the mirror to be installed into anything other than an empty directory.
                delegates.add(new EmptyDirectoryValidationStrategy(ImmutableSet.of(),
                        file -> new HardValidationException(HardValidationException.ERROR_INSTALL_MIRROR_ON_EXISTING_HOME)));
            } else {
                delegates.add(homeDirectoryEmpty("shared"));
            }
            return new CompositeDirectoryValidationStrategy(delegates.build());
        }
    }

    public static DirectoryValidationStrategy getInstallationDirectoryValidationStrategy() {
        return new CompositeDirectoryValidationStrategy(new PathSpacesValidationStrategy(), installDirectoryEmpty());
    }

    private static EmptyDirectoryValidationStrategy installDirectoryEmpty() {
        return new EmptyDirectoryValidationStrategy(emptySet(),
                file -> new HardValidationException(HardValidationException.ERROR_DIRECTORY_USED));
    }

    private static EmptyDirectoryValidationStrategy homeDirectoryEmpty(String... exclude) {
        return new EmptyDirectoryValidationStrategy(ImmutableSet.copyOf(exclude),
                file -> new SoftValidationException(SoftValidationException.ERROR_DIRECTORY_USED));
    }
}
