/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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 org.apache.camel.kafkaconnector.maven.docs;

import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

import org.apache.camel.kafkaconnector.maven.docs.dto.CamelKafkaConnectorTableModel;
import org.apache.camel.kafkaconnector.maven.docs.dto.CamelKafkaConnectorTableOptionModel;
import org.apache.camel.maven.packaging.MvelHelper;
import org.apache.camel.tooling.util.Strings;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.apache.commons.io.filefilter.RegexFileFilter;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.mvel2.templates.TemplateRuntime;

import static org.apache.camel.tooling.util.PackageHelper.loadText;
import static org.apache.camel.tooling.util.PackageHelper.writeText;

/**
 * Generates the table of connectors in: - docs/modules/ROOT/pages/reference/index.adoc
 */
@Mojo(name = "update-doc-connectors-list", threadSafe = true)
public class UpdateDocComponentsListMojo extends AbstractMojo {
    //TODO: make these configurable:
    private static final String SINK_CONNECTOR_LINK_SUFFIX_ADOC = "kafka-sink-connector.adoc[Sink Docs]";
    private static final String SOURCE_CONNECTOR_LINK_SUFFIX_ADOC = "kafka-source-connector.adoc[Source Docs]";
    private static final String XREF_CONNECTOR_LINK_PREFIX = "xref:reference/connectors/";

    /**
     * The maven project.
     */
    @Parameter(property = "project", required = true, readonly = true)
    protected MavenProject project;

    /**
     * The directory for components catalog
     */
    @Parameter(defaultValue = "${project.directory}/../../connectors/")
    protected File connectorsDir;

    /**
     * The project directory
     */
    @Parameter(defaultValue = "${basedir}/../")
    protected File projectBaseDir;
    
    /**
     * The Apache Repository
     */
    @Parameter(defaultValue = "https://repo.maven.apache.org/maven2/org/apache/camel/kafkaconnector/")
    protected String repositoryPath;
    
    
    /**
     * The Last Released version
     */
    @Parameter
    protected String lastReleasedVersion;
    
    /**
     * The maven project.
     */
    @Parameter(property = "connectors-project-name", defaultValue = "connectors", readonly = true)
    protected String connectorsProjectName;

    /**
     * Maven ProjectHelper.
     */
    @Component
    private MavenProjectHelper projectHelper;

    /**
     * Execute goal.
     *
     * @throws MojoExecutionException execution of the main class or one of the
     *             threads it generated failed.
     * @throws MojoFailureException something bad happened...
     */
    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        if (!project.getArtifactId().equals(connectorsProjectName)) {
            getLog().debug("Skipping project " + project.getArtifactId() + " since it is not " + connectorsProjectName + " can be configured with <connectors-project-name> option.");
            return;
        }
        executeComponentsReadme();
    }

    protected void executeComponentsReadme() throws MojoExecutionException, MojoFailureException {
        CamelKafkaConnectorTableModel tableModel = new CamelKafkaConnectorTableModel();
        ArrayList<CamelKafkaConnectorTableOptionModel> options = new ArrayList<>();

        if (connectorsDir != null && connectorsDir.isDirectory()) {
            File[] files = connectorsDir.listFiles();
            if (files != null) {
                for (int i = 0; i < files.length; i++) {
                    File file = files[i];
                    if (file.isDirectory()) {
                        Collection sinkConnector = FileUtils.listFiles(file, new RegexFileFilter(".*SinkTask.*"), DirectoryFileFilter.DIRECTORY);
                        Collection sourceConnector = FileUtils.listFiles(file, new RegexFileFilter(".*SourceTask.*"), DirectoryFileFilter.DIRECTORY);
                        if (!sinkConnector.isEmpty() || !sourceConnector.isEmpty()) {
                            CamelKafkaConnectorTableOptionModel singleConnector = new CamelKafkaConnectorTableOptionModel();
                            singleConnector.setName(file.getName());
                            if (!sinkConnector.isEmpty()) {
                                singleConnector.setSink(true);
                                String connectorFinal = StringUtils.removeEnd(file.getName(), "kafka-connector");
                                if (connectorFinal.equalsIgnoreCase("camel-coap-tcp-")) {
                                    singleConnector.setDocsSink(XREF_CONNECTOR_LINK_PREFIX + "camel-coap+tcp-" + SINK_CONNECTOR_LINK_SUFFIX_ADOC);
                                } else if (connectorFinal.equalsIgnoreCase("camel-coaps-tcp-")) {
                                    singleConnector.setDocsSink(XREF_CONNECTOR_LINK_PREFIX + "camel-coaps+tcp-" + SINK_CONNECTOR_LINK_SUFFIX_ADOC);
                                } else if (connectorFinal.equalsIgnoreCase("camel-solrcloud-")) {
                                    singleConnector.setDocsSink(XREF_CONNECTOR_LINK_PREFIX + "camel-solrCloud-" + SINK_CONNECTOR_LINK_SUFFIX_ADOC);
                                } else {
                                    singleConnector.setDocsSink(XREF_CONNECTOR_LINK_PREFIX + connectorFinal + SINK_CONNECTOR_LINK_SUFFIX_ADOC);
                                }
                            }
                            if (!sourceConnector.isEmpty()) {
                                singleConnector.setSource(true);
                                String connectorFinal = StringUtils.removeEnd(file.getName(), "kafka-connector");
                                if (connectorFinal.equalsIgnoreCase("camel-coap-tcp-")) {
                                    singleConnector.setDocsSource(XREF_CONNECTOR_LINK_PREFIX + "camel-coap+tcp-" + SOURCE_CONNECTOR_LINK_SUFFIX_ADOC);
                                } else if (connectorFinal.equalsIgnoreCase("camel-coaps-tcp-")) {
                                    singleConnector.setDocsSource(XREF_CONNECTOR_LINK_PREFIX + "camel-coaps+tcp-" + SOURCE_CONNECTOR_LINK_SUFFIX_ADOC);
                                } else if (connectorFinal.equalsIgnoreCase("camel-solrcloud-")) {
                                    singleConnector.setDocsSource(XREF_CONNECTOR_LINK_PREFIX + "camel-solrCloud-" + SOURCE_CONNECTOR_LINK_SUFFIX_ADOC);
                                } else {
                                    singleConnector.setDocsSource(XREF_CONNECTOR_LINK_PREFIX + connectorFinal + SOURCE_CONNECTOR_LINK_SUFFIX_ADOC);
                                }
                            }
                            String downloadLinkTar = repositoryPath + singleConnector.getName() + "/" + lastReleasedVersion + "/" + singleConnector.getName() + "-" + lastReleasedVersion + "-package.tar.gz[Download]";
                            singleConnector.setDownloadLinkTar(downloadLinkTar);
                            options.add(singleConnector);
                        }
                    }
                }
                options.sort((model1, model2) -> {
                    String name1 = model1.getName();
                    String name2 = model2.getName();
                    int res = String.CASE_INSENSITIVE_ORDER.compare(name1, name2);
                    return (res != 0) ? res : name1.compareTo(name2);
                });
                tableModel.setOptions(options);
            }
        }
        //TODO: make these configurable:
        File docFolderWebsite = new File(projectBaseDir, "docs/modules/ROOT/");
        File docFileWebsite = new File(docFolderWebsite, "pages/reference/index.adoc");
        String changed = templateConnnectorsTable(tableModel);
        boolean updated = updateConnectorsTable(docFileWebsite, changed);
        if (updated) {
            getLog().info("Updated connectors table file: " + docFileWebsite);
        } else {
            getLog().debug("No changes to connectors table file: " + docFileWebsite);
        }
        File navWebsite = new File(docFolderWebsite, "nav.adoc");
        boolean navUpdated = updateNav(navWebsite, tableModel);
        if (navUpdated) {
            getLog().info("Updated nav file: " + navWebsite);
        } else {
            getLog().debug("No changes to nav file: " + navWebsite);
        }
    }

    private String templateConnnectorsTable(CamelKafkaConnectorTableModel model) throws MojoExecutionException {

        try {
            String template = null;
            template = loadText(UpdateDocComponentsListMojo.class.getClassLoader().getResourceAsStream("connectors.mvel"));
            String out = (String)TemplateRuntime.eval(template, model, Collections.singletonMap("util", MvelHelper.INSTANCE));
            return out;
        } catch (Exception e) {
            throw new MojoExecutionException("Error processing mvel template. Reason: " + e, e);
        }
    }

    private boolean updateConnectorsTable(File file, String changed) throws MojoExecutionException {
        try {
            if (!file.exists()) {
                // include markers for new files
                changed = "// kafka-connectors list: START\n" + changed + "\n// kafka-connectors list: END\n";
                writeText(file, changed);
                return true;
            }

            String text = loadText(new FileInputStream(file));

            String existing = Strings.between(text, "// kafka-connectors list: START", "// kafka-connectors list: END");
            if (existing != null) {
                // remove leading line breaks etc
                existing = existing.trim();
                changed = changed.trim();
                if (existing.equals(changed)) {
                    return false;
                } else {
                    String before = Strings.before(text, "// kafka-connectors list: START");
                    String after = Strings.after(text, "// kafka-connectors list: END");
                    text = before + "// kafka-connectors list: START\n" + changed + "\n// kafka-connectors list: END" + after;
                    writeText(file, text);
                    return true;
                }
            } else {
                getLog().warn("Cannot find markers in file " + file);
                getLog().warn("Add the following markers");
                getLog().warn("\t// kafka-connectors list: START");
                getLog().warn("\t// kafka-connectors list: END");
                return false;
            }
        } catch (Exception e) {
            throw new MojoExecutionException("Error reading file " + file + " Reason: " + e, e);
        }
    }

    private boolean updateNav(File file, CamelKafkaConnectorTableModel model) throws MojoExecutionException {
        String changed = null;
        try {
            String template = loadText(UpdateDocComponentsListMojo.class.getClassLoader().getResourceAsStream("nav.mvel"));
            changed = (String)TemplateRuntime.eval(template, model, Collections.singletonMap("util", MvelHelper.INSTANCE));
        } catch (Exception e) {
            throw new MojoExecutionException("Error processing mvel template. Reason: " + e, e);
        }

        try {
            String text = loadText(file);

            String existing = Strings.between(text, "// connectors: START", "// connectors: END");
            if (existing != null) {
                // remove leading line breaks etc
                existing = existing.trim();
                changed = changed.trim();
                if (existing.equals(changed)) {
                    return false;
                } else {
                    String before = Strings.before(text, "// connectors: START");
                    String after = Strings.after(text, "// connectors: END");
                    text = before + "// connectors: START\n" + changed + "\n// connectors: END" + after;
                    writeText(file, text);
                    return true;
                }
            } else {
                getLog().warn("Cannot find markers in file " + file);
                getLog().warn("Add the following markers");
                getLog().warn("\t// connectors: START");
                getLog().warn("\t// connectors: END");
                return false;
            }
        } catch (Exception e) {
            throw new MojoExecutionException("Error reading file " + file + " Reason: " + e, e);
        }
    }
}
