package com.atlassian.stash.internal.plugin;

import com.atlassian.plugin.Application;
import com.atlassian.plugin.ModuleDescriptorFactory;
import com.atlassian.plugin.main.PluginsConfiguration;
import com.atlassian.plugin.main.PluginsConfigurationBuilder;
import com.atlassian.plugin.manager.PluginPersistentStateStore;
import com.atlassian.plugin.osgi.container.PackageScannerConfiguration;
import com.atlassian.plugin.osgi.hostcomponents.HostComponentProvider;
import com.atlassian.stash.internal.ApplicationSettings;
import com.google.common.collect.ImmutableSet;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletContext;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.Set;

/**
 * Assembles properties from various inject beans and from {@link ApplicationSettings} to produce a
 * {@code PluginsConfiguration} which can be used to initialise the plugin framework.
 * <p>
 * Because the plugin framework is wired up in Spring, there is actually no requirement to use
 * the {@code PluginsConfiguration}. However, when {@code PluginConfigurationBuilder.build()} is
 * called, the overall configuration is validated, which should catch issues with the configuration
 * before it is used. For that reason, it's still desirable to build up the configuration this way
 * and then reference it in Spring using Spring EL.
 * <p>
 * This class provides a static {@code create} method instead of implementing {@code FactoryBean} to avoid
 * {@code org.springframework.beans.factory.BeanCurrentlyInCreationException} exceptions during initialization.
 */
public class PluginConfigurationFactory {

    private static final Set<String> LIST_KEYS = ImmutableSet.of("bitbucket.plugins.list", "stash.plugins.list");

    private static final Logger log = LoggerFactory.getLogger(PluginConfigurationFactory.class);

    public static PluginsConfiguration create(ApplicationSettings applicationSettings, Application hostApplication,
                                              HostComponentProvider hostComponentProvider,
                                              ModuleDescriptorFactory moduleDescriptorFactory,
                                              PackageScannerConfiguration packageScannerConfiguration,
                                              PluginPersistentStateStore pluginPersistentStateStore,
                                              ServletContext servletContext) throws IOException {
        File frameworkBundlesDir = new File(servletContext.getRealPath("/WEB-INF/osgi-framework-bundles"));
        log.debug("Using OSGi framework bundles from: {}", frameworkBundlesDir);

        return new PluginsConfigurationBuilder()
                .application(hostApplication)
                .bundledPluginUrl(getBundledPluginsUrl(servletContext))
                .bundledPluginCacheDirectory(applicationSettings.getBundledPluginsDir())
                .frameworkBundleDirectory(frameworkBundlesDir)
                .hostComponentProvider(hostComponentProvider)
                .moduleDescriptorFactory(moduleDescriptorFactory)
                .osgiPersistentCache(applicationSettings.getOsgiCacheDir())
                .packageScannerConfiguration(packageScannerConfiguration)
                .pluginDirectory(applicationSettings.getInstalledPluginsDir())
                .pluginStateStore(pluginPersistentStateStore)
                .useLegacyDynamicPluginDeployer(true)
                .build();
    }

    private static URL getBundledPluginsUrl(ServletContext servletContext) throws IOException {
        for (String listKey : LIST_KEYS) {
            String list = System.getProperty(listKey);
            if (StringUtils.isNotBlank(list)) {
                try {
                    return URI.create(list).toURL();
                } catch (MalformedURLException e) {
                    log.warn(listKey + ": [" + list + "] is not a valid URL", e);
                }
            }
        }

        //There is no longer a bundled plugins zip file as part of the standard build/distribution. Instead,
        //the bundled plugins are copied as files. This way the plugin framework doesn't need to unpack them
        //when it starts for the first time, and it doesn't need to clean them on upgrade.
        File bundledPluginsDir = new File(servletContext.getRealPath("/WEB-INF/atlassian-bundled-plugins"));
        log.debug("Using bundled plugins from: {}", bundledPluginsDir);

        return bundledPluginsDir.toURI().toURL();
    }
}
