/*
 * 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.kamelets.catalog;

import java.util.List;
import java.util.Map;


import io.github.classgraph.ClassGraph;
import org.apache.camel.kamelets.catalog.model.KameletTypeEnum;
import org.apache.camel.tooling.model.ComponentModel;
import org.apache.camel.v1.Kamelet;
import org.apache.camel.v1.kameletspec.DataTypes;
import org.apache.camel.v1.kameletspec.Definition;
import org.apache.camel.v1.kameletspec.Template;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

public class KameletsCatalogTest {
    static KameletsCatalog catalog;

    @BeforeAll
    public static void createKameletsCatalog() {
        catalog = new KameletsCatalog();
    }

    @Test
    void testGetKameletsName() throws Exception {
        List<String> names = catalog.getKameletsName();
        assertFalse(names.isEmpty());
    }

    @Test
    void testGetKamelets() throws Exception {
        Map<String, Kamelet> kamelets = catalog.getKamelets();
        assertFalse(kamelets.isEmpty());
    }

    @Test
    void testGetKameletsDefinition() throws Exception {
        Definition props = catalog.getKameletDefinition("aws-sqs-source");
        assertEquals(21, props.getProperties().keySet().size());
        assertTrue(props.getProperties().containsKey("queueNameOrArn"));
        assertTrue(props.getProperties().containsKey("deleteAfterRead"));
        assertTrue(props.getProperties().containsKey("accessKey"));
        assertTrue(props.getProperties().containsKey("secretKey"));
        assertTrue(props.getProperties().containsKey("region"));
        assertTrue(props.getProperties().containsKey("autoCreateQueue"));
        assertTrue(props.getProperties().containsKey("amazonAWSHost"));
        assertTrue(props.getProperties().containsKey("protocol"));
        assertTrue(props.getProperties().containsKey("queueURL"));
        assertTrue(props.getProperties().containsKey("useDefaultCredentialsProvider"));
        assertTrue(props.getProperties().containsKey("uriEndpointOverride"));
        assertTrue(props.getProperties().containsKey("overrideEndpoint"));
        assertTrue(props.getProperties().containsKey("delay"));
        assertTrue(props.getProperties().containsKey("greedy"));
        assertTrue(props.getProperties().containsKey("maxMessagesPerPoll"));
        assertTrue(props.getProperties().containsKey("waitTimeSeconds"));
        assertTrue(props.getProperties().containsKey("visibilityTimeout"));
    }

    @Test
    void testGetKameletsRequiredProperties() throws Exception {
        List<String> props = catalog.getKameletRequiredProperties("aws-sqs-source");
        assertEquals(2, props.size());
        assertTrue(props.contains("queueNameOrArn"));
        assertTrue(props.contains("region"));
    }

    @Test
    void testGetKameletsDefinitionNotExists() throws Exception {
        Definition props = catalog.getKameletDefinition("word");
        assertNull(props);
    }

    @Test
    void testGetKameletsByProvider() throws Exception {
        List<Kamelet> c = catalog.getKameletByProvider("Apache Software Foundation");
        assertFalse(c.isEmpty());
        c = catalog.getKameletByProvider("Eclipse");
        assertTrue(c.isEmpty());
    }

    @Test
    void testGetKameletsByType() throws Exception {
        List<Kamelet> c = catalog.getKameletsByType(KameletTypeEnum.SOURCE.type());
        assertFalse(c.isEmpty());
        c = catalog.getKameletsByType(KameletTypeEnum.SINK.type());
        assertFalse(c.isEmpty());
        c = catalog.getKameletsByType(KameletTypeEnum.ACTION.type());
        assertFalse(c.isEmpty());
    }

    @Test
    void testGetKameletsByGroup() throws Exception {
        List<Kamelet> c = catalog.getKameletsByGroups("AWS S3");
        assertFalse(c.isEmpty());
        c = catalog.getKameletsByGroups("AWS SQS");
        assertFalse(c.isEmpty());
        c = catalog.getKameletsByGroups("Not-existing-group");
        assertTrue(c.isEmpty());
    }

    @Test
    void testGetKameletsByNamespace() throws Exception {
        List<Kamelet> c = catalog.getKameletsByNamespace("AWS");
        assertFalse(c.isEmpty());
        assertEquals(31, c.size());
        c = catalog.getKameletsByGroups("Not-existing-group");
        assertTrue(c.isEmpty());
    }

    @Test
    void testGetKameletsDependencies() throws Exception {
        List<String> deps = catalog.getKameletDependencies("aws-sqs-source");
        assertEquals(3, deps.size());
        deps = catalog.getKameletDependencies("cassandra-sink");
        assertEquals(4, deps.size());
        assertEquals("camel:jackson", deps.get(0));
    }

    @Test
    void testGetKameletsTemplate() throws Exception {
        Template template = catalog.getKameletTemplate("aws-sqs-source");
        assertNotNull(template);
    }

    @Test
    void testAllKameletFilesLoaded() throws Exception {
        int numberOfKameletFiles = new ClassGraph().acceptPaths("/" + KameletsCatalog.KAMELETS_DIR + "/").scan().getAllResources().size();
        assertEquals(numberOfKameletFiles, catalog.getKameletsName().size(), "Some embedded kamelet definition files cannot be loaded.");
    }

    @Test
    void testAllKameletDependencies() throws Exception {
        catalog.getAllKameletDependencies();
    }

    @Test
    void testSupportedHeaders() throws Exception {
        verifyHeaders("aws-s3-source", 26);
        verifyHeaders("aws-s3-sink", 47);
        verifyHeaders("aws-cloudtrail-source", 4);
        verifyHeaders("aws-redshift-source", 0);
        verifyHeaders("aws-not-exists", 0);
        verifyHeaders("azure-eventhubs-sink", 2);
        verifyHeaders("azure-functions-sink", 8);
        verifyHeaders("azure-servicebus-source", 21);
        verifyHeaders("azure-storage-blob-source", 34);
        verifyHeaders("azure-storage-blob-sink", 37);
        verifyHeaders("azure-storage-blob-changefeed-source", 34);
        verifyHeaders("azure-storage-datalake-source", 25);
        verifyHeaders("azure-storage-datalake-sink", 37);
        verifyHeaders("azure-storage-queue-source", 6);
        verifyHeaders("azure-storage-queue-sink", 16);
        verifyHeaders("cassandra-sink", 1);
        verifyHeaders("cassandra-source", 1);
        verifyHeaders("couchbase-sink", 2);
        verifyHeaders("dropbox-source", 0);
        verifyHeaders("dropbox-source", 0);
        verifyHeaders("elasticsearch-index-sink", 10);
        verifyHeaders("elasticsearch-search-source", 10);
        verifyHeaders("exec-sink", 0);
        verifyHeaders("fhir-source", 0);
        verifyHeaders("file-watch-source", 10);
        verifyHeaders("ftp-source", 15);
        verifyHeaders("ftp-sink", 6);
        verifyHeaders("ftps-source", 15);
        verifyHeaders("ftps-sink", 6);
        verifyHeaders("github-commit-source", 7);
        verifyHeaders("github-event-source", 7);
        verifyHeaders("github-pullrequest-comment-source", 7);
        verifyHeaders("github-pullrequest-source", 7);
        verifyHeaders("github-tag-source", 7);
        verifyHeaders("google-bigquery-sink", 4);
        verifyHeaders("google-calendar-source", 1);
        verifyHeaders("google-functions-sink", 5);
        verifyHeaders("google-mail-source", 6);
        verifyHeaders("google-pubsub-sink", 3);
        verifyHeaders("google-pubsub-source", 6);
        verifyHeaders("google-sheets-source", 6);
        verifyHeaders("google-storage-source", 21);
        verifyHeaders("google-storage-sink", 15);
        verifyHeaders("http-source", 5);
        verifyHeaders("http-sink", 14);
        verifyHeaders("http-secured-source", 5);
        verifyHeaders("http-secured-sink", 14);
        verifyHeaders("infinispan-source", 6);
        verifyHeaders("infinispan-sink", 14);
        verifyHeaders("jira-add-comment-sink", 17);
        verifyHeaders("jira-add-issue-sink", 17);
        verifyHeaders("jira-source", 3);
        verifyHeaders("jira-oauth-source", 3);
        verifyHeaders("jms-amqp-10-source", 15);
        verifyHeaders("jms-amqp-10-sink", 18);
        verifyHeaders("jms-apache-artemis-source", 15);
        verifyHeaders("jms-apache-artemis-sink", 18);
        verifyHeaders("jms-ibm-mq-source", 15);
        verifyHeaders("jms-ibm-mq-sink", 18);
        verifyHeaders("kafka-source", 9);
        verifyHeaders("kafka-sink", 5);
        verifyHeaders("kubernetes-namespaces-source", 2);
        verifyHeaders("kubernetes-nodes-source", 2);
        verifyHeaders("kubernetes-pods-source", 2);
        verifyHeaders("log-sink", 0);
        verifyHeaders("mail-source", 0);
        verifyHeaders("mail-sink", 8);
        verifyHeaders("mariadb-source", 0);
        verifyHeaders("mariadb-sink", 9);
        verifyHeaders("minio-source", 15);
        verifyHeaders("minio-sink", 23);
        verifyHeaders("mongodb-changes-stream-source", 3);
        verifyHeaders("mongodb-sink", 12);
        verifyHeaders("mongodb-source", 3);
        verifyHeaders("mqtt-sink", 3);
        verifyHeaders("mqtt-source", 2);
        verifyHeaders("mqtt5-sink", 3);
        verifyHeaders("mqtt5-source", 2);
        verifyHeaders("mysql-sink", 9);
        verifyHeaders("mysql-source", 0);
        verifyHeaders("nats-sink", 5);
        verifyHeaders("nats-source", 8);
        verifyHeaders("oracle-database-sink", 9);
        verifyHeaders("oracle-database-source", 0);
        verifyHeaders("postgresql-sink", 9);
        verifyHeaders("postgresql-source", 0);
        verifyHeaders("pulsar-sink", 5);
        verifyHeaders("pulsar-source", 11);
        verifyHeaders("redis-sink", 29);
        verifyHeaders("redis-source", 28);
        verifyHeaders("rest-openapi-sink", 0);
        verifyHeaders("salesforce-create-sink", 1);
        verifyHeaders("salesforce-delete-sink", 1);
        verifyHeaders("salesforce-update-sink", 1);
        verifyHeaders("salesforce-composite-upsert-sink", 1);
        verifyHeaders("salesforce-source", 21);
        verifyHeaders("scp-sink", 0);
        verifyHeaders("sftp-sink", 6);
        verifyHeaders("sftp-source", 15);
        verifyHeaders("slack-sink", 0);
        verifyHeaders("slack-source", 0);
        verifyHeaders("splunk-hec-sink", 1);
        verifyHeaders("splunk-sink", 0);
        verifyHeaders("splunk-source", 0);
        verifyHeaders("sqlserver-sink", 9);
        verifyHeaders("sqlserver-source", 0);
        verifyHeaders("ssh-sink", 4);
        verifyHeaders("ssh-source", 4);
        verifyHeaders("telegram-sink", 6);
        verifyHeaders("telegram-source", 5);
        verifyHeaders("timer-source", 2);
        verifyHeaders("twitter-directmessage-source", 2);
        verifyHeaders("twitter-timeline-source", 1);
        verifyHeaders("twitter-search-source", 7);
        verifyHeaders("webhook-source", 0);
        verifyHeaders("wttrin-source", 5);
    }

    void verifyHeaders(String name, int expected) {
        List<ComponentModel.EndpointHeaderModel> headers = catalog.getKameletSupportedHeaders(name);
        assertEquals(expected, headers.size(), "Failure checking " + name);
    }

    @Test
    void testGetKameletScheme() throws Exception {
        assertEquals("aws2-s3", catalog.getKameletScheme("aws-s3"));
        assertEquals("aws2-sqs", catalog.getKameletScheme("aws-sqs"));
        assertNull(catalog.getKameletScheme("not-known"));
    }

    @Test
    void testHasDataTypes() throws Exception {
        assertTrue(catalog.hasDataTypes("aws-s3-source"));
        assertFalse(catalog.hasDataTypes("splunk-hec-source"));
    }

    @Test
    void testGetDataTypes() throws Exception {
        Map<String, DataTypes> dataTypes = catalog.getDataTypes("aws-s3-source");
        assertTrue(dataTypes.size() == 1);
        assertNotNull(dataTypes.get("out").getTypes().get("binary"));
        assertNotNull(dataTypes.get("out").getTypes().get("cloudevents"));
        dataTypes = catalog.getDataTypes("splunk-hec-sink");
        assertNull(dataTypes);
    }
}
