/*
 * Copyright OpenSearch Contributors
 * SPDX-License-Identifier: Apache-2.0
 */

package org.opensearch.performanceanalyzer.plugins;


import com.google.common.annotations.VisibleForTesting;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.opensearch.performanceanalyzer.decisionmaker.actions.ActionListener;
import org.opensearch.performanceanalyzer.decisionmaker.deciders.Publisher;

public class PluginController {

    private static final Logger LOG = LogManager.getLogger(PluginController.class);
    private final Publisher publisher;
    private List<Plugin> plugins;
    private PluginControllerConfig pluginControllerConfig;

    public PluginController(PluginControllerConfig pluginConfig, Publisher publisher) {
        this.pluginControllerConfig = pluginConfig;
        this.publisher = publisher;
        this.plugins = new ArrayList<>();
    }

    public void initPlugins() {
        loadFrameworkPlugins();
        registerActionListeners();
    }

    private void loadFrameworkPlugins() {
        for (Class<?> pluginClass : pluginControllerConfig.getFrameworkPlugins()) {
            final Constructor<?>[] constructors = pluginClass.getConstructors();
            if (constructors.length == 0) {
                throw new IllegalStateException(
                        "no public constructor found for plugin class: ["
                                + pluginClass.getName()
                                + "]");
            }
            if (constructors.length > 1) {
                throw new IllegalStateException(
                        "unique constructor expected for plugin class: ["
                                + pluginClass.getName()
                                + "]");
            }
            if (constructors[0].getParameterCount() != 0) {
                throw new IllegalStateException(
                        "default constructor expected for plugin class: ["
                                + pluginClass.getName()
                                + "]");
            }

            try {
                plugins.add((Plugin) constructors[0].newInstance());
                LOG.info("loaded plugin: [{}]", plugins.get(plugins.size() - 1).name());
            } catch (InstantiationException
                    | IllegalAccessException
                    | InvocationTargetException e) {
                LOG.error("Failed to instantiate plugin", e);
                throw new IllegalStateException(
                        "Failed to instantiate plugin: [" + pluginClass.getName() + "]", e);
            }
        }
    }

    private void registerActionListeners() {
        for (Plugin plugin : plugins) {
            if (ActionListener.class.isAssignableFrom(plugin.getClass())) {
                publisher.addActionListener((ActionListener) plugin);
            }
        }
    }

    @VisibleForTesting
    List<Plugin> getPlugins() {
        return plugins;
    }
}