package io.cattle.platform.archaius.startup;

import io.cattle.platform.archaius.polling.RefreshableFixedDelayPollingScheduler;
import io.cattle.platform.archaius.sources.LazyJDBCSource;
import io.cattle.platform.archaius.util.ArchaiusUtil;
import io.cattle.platform.datasource.DataSourceFactory;
import io.cattle.platform.extension.impl.ExtensionManagerImpl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import javax.sql.DataSource;

import org.apache.commons.configuration.AbstractConfiguration;
import org.apache.commons.configuration.MapConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.netflix.config.ConcurrentCompositeConfiguration;
import com.netflix.config.DynamicConfiguration;
import com.netflix.config.DynamicPropertyFactory;
import com.netflix.config.sources.JDBCConfigurationSource;

public class ArchaiusStartup {

    public static final String CONFIG_KEY = "config";
    public static final String DB_CONFIG = "DatabaseConfig";

    private static Properties GLOBAL_DEFAULT = null;
    private static final Logger log = LoggerFactory.getLogger("ConsoleStatus");

    ExtensionManagerImpl extensionManager;
    ConcurrentCompositeConfiguration baseConfig;
    DataSource configDataSource;
    DataSourceFactory dataSourceFactory;
    String dataSourceName = "config";
    String query = "SELECT distinct name, value FROM setting";
    String keyColumnName = "name";
    String valueColumnName = "value";
    List<RefreshableFixedDelayPollingScheduler> schedulers;
    boolean init = false;

    @PostConstruct
    public void init() {
        if (init) {
            return;
        }

        if (GLOBAL_DEFAULT == null) {
            throw new IllegalStateException("setGlobalDefaults() must be set before init() is called");
        }

        baseConfig = new ConcurrentCompositeConfiguration();
        baseConfig.addConfiguration(new MapConfiguration(getOverride()));
        baseConfig.addConfiguration(new MapConfiguration(GLOBAL_DEFAULT));

        DynamicPropertyFactory.initWithConfigurationSource(baseConfig);

        init = true;
    }

    protected Map<String, Object> getOverride() {
        Map<String, Object> override = new HashMap<String, Object>();
        override.put(CONFIG_KEY + ".exclude", DB_CONFIG);

        return override;
    }

    public void start() {
        log.info("Loading configuration");
        load(false);
        extensionManager.reset();
        load(true);
    }

    protected void load(boolean refresh) {
        List<AbstractConfiguration> configs = extensionManager.getExtensionList(CONFIG_KEY, AbstractConfiguration.class);

        for (AbstractConfiguration config : configs) {
            config.setDelimiterParsingDisabled(true);
        }

        if (refresh) {
            for (AbstractConfiguration config : configs) {
                refresh(config);
            }
        }

        baseConfig.clear();
        for (AbstractConfiguration config : configs) {
            baseConfig.addConfiguration(config);
        }

        if (refresh) {
            for (RefreshableFixedDelayPollingScheduler scheduler : schedulers) {
                scheduler.refresh();
            }

            ArchaiusUtil.addSchedulers(schedulers);
        }
    }

    protected void refresh(AbstractConfiguration config) {
        if (config instanceof DynamicConfiguration && ((DynamicConfiguration) config).getSource() instanceof LazyJDBCSource) {
            LazyJDBCSource source = (LazyJDBCSource) ((DynamicConfiguration) config).getSource();

            if (configDataSource == null) {
                configDataSource = dataSourceFactory.createDataSource(dataSourceName);
            }

            source.setSource(new JDBCConfigurationSource(configDataSource, query, keyColumnName, valueColumnName));
        }
    }

    public ExtensionManagerImpl getExtensionManager() {
        return extensionManager;
    }

    @Inject
    public void setExtensionManager(ExtensionManagerImpl extensionManager) {
        this.extensionManager = extensionManager;
    }

    public static Properties setGlobalDefaults(Properties props) {
        return GLOBAL_DEFAULT = props;
    }

    public static Properties getGlobalDefaults() {
        return GLOBAL_DEFAULT;
    }

    public DataSourceFactory getDataSourceFactory() {
        return dataSourceFactory;
    }

    @Inject
    public void setDataSourceFactory(DataSourceFactory dataSourceFactory) {
        this.dataSourceFactory = dataSourceFactory;
    }

    public String getDataSourceName() {
        return dataSourceName;
    }

    public void setDataSourceName(String dataSourceName) {
        this.dataSourceName = dataSourceName;
    }

    public String getQuery() {
        return query;
    }

    public void setQuery(String query) {
        this.query = query;
    }

    public String getKeyColumnName() {
        return keyColumnName;
    }

    public void setKeyColumnName(String keyColumnName) {
        this.keyColumnName = keyColumnName;
    }

    public String getValueColumnName() {
        return valueColumnName;
    }

    public void setValueColumnName(String valueColumnName) {
        this.valueColumnName = valueColumnName;
    }

    public List<RefreshableFixedDelayPollingScheduler> getSchedulers() {
        return schedulers;
    }

    @Inject
    public void setSchedulers(List<RefreshableFixedDelayPollingScheduler> schedulers) {
        this.schedulers = schedulers;
    }

}
