/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.trino.server;

import io.airlift.log.Logger;
import io.trino.spi.Plugin;
import picocli.CommandLine;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.stream.Stream;

import static io.trino.server.ModuleReader.mapModulesToPlugins;
import static io.trino.server.PluginLoader.loadPlugins;
import static io.trino.server.PluginLoader.printPluginFeatures;
import static java.util.function.Function.identity;
import static java.util.function.Predicate.not;
import static java.util.stream.Collectors.toMap;

@Command(name = "modulesToConnectors", mixinStandardHelpOptions = true,
        description = "Maps Trino plugin modules to connectors they provide and filters them using an impacted modules list.")
public class PluginReader
        implements Callable<Integer>
{
    private static final Logger log = Logger.get(PluginReader.class);

    @Option(names = {"-i", "--impacted-modules"}, description = "Impacted modules file generated by the gitflow-incremental-builder (GIB) Maven plugin")
    private Optional<File> impactedModulesFile;

    @Option(names = {"-p", "--plugin-dir"}, description = "Trino plugin directory")
    private File pluginDir = new File("plugin");

    @Option(names = {"-r", "--root-pom"}, description = "Trino root module pom.xml")
    private File rootPom = new File("pom.xml");

    public static void main(String... args)
    {
        int exitCode = new CommandLine(new PluginReader()).execute(args);
        System.exit(exitCode);
    }

    @Override
    public Integer call()
    {
        Optional<List<String>> impactedModules = Optional.empty();
        if (impactedModulesFile.isPresent()) {
            impactedModules = readImpactedModules(impactedModulesFile.get());
            if (impactedModules.isEmpty()) {
                return 1;
            }
        }
        Map<String, String> modulesToPlugins = mapModulesToPlugins(rootPom);
        Stream<Map.Entry<String, String>> modulesStream = modulesToPlugins.entrySet().stream();
        if (impactedModules.isPresent()) {
            List<String> nonPluginModules = impactedModules.get().stream()
                    .filter(not(modulesToPlugins::containsKey))
                    .toList();
            if (!nonPluginModules.isEmpty()) {
                log.warn("Impacted modules list includes non-plugin modules, ignoring it: %s", nonPluginModules);
            }
            else {
                List<String> finalImpactedModules = impactedModules.get();
                modulesStream = modulesStream.filter(entry -> finalImpactedModules.contains(entry.getKey()));
            }
        }

        Map<String, Plugin> plugins = loadPlugins(pluginDir).stream()
                .collect(toMap(plugin -> plugin.getClass().getName(), identity()));
        modulesStream.forEach(entry -> {
            if (!plugins.containsKey(entry.getValue())) {
                log.warn("Plugin without any connectors: %s", entry.getValue());
                return;
            }
            printPluginFeatures(plugins.get(entry.getValue()));
        });
        return 0;
    }

    private static Optional<List<String>> readImpactedModules(File gibImpactedModules)
    {
        try {
            return Optional.of(Files.readAllLines(gibImpactedModules.toPath()));
        }
        catch (IOException e) {
            log.warn(e, "Couldn't read file %s", gibImpactedModules);
            return Optional.empty();
        }
    }
}
