/*
 * 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.seatunnel.datasource.plugin.mqtt;

import org.apache.seatunnel.api.configuration.util.OptionRule;
import org.apache.seatunnel.common.utils.SeaTunnelException;
import org.apache.seatunnel.datasource.plugin.api.DataSourceChannel;
import org.apache.seatunnel.datasource.plugin.api.DataSourcePluginException;
import org.apache.seatunnel.datasource.plugin.api.model.TableField;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import org.eclipse.paho.client.mqttv3.MqttMessage;

import com.alibaba.fastjson2.JSONObject;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

@Slf4j
public class MqttDataSourceChannel implements DataSourceChannel {

    private static final String DATABASE = "default";

    private InitCallback initCallback;

    @Override
    public OptionRule getDataSourceOptions(@NonNull String pluginName) {
        return MqttOptionRule.optionRule();
    }

    @Override
    public OptionRule getDatasourceMetadataFieldsByDataSourceName(@NonNull String pluginName) {
        return MqttOptionRule.metadataRule();
    }

    @Override
    public List<String> getTables(
            @NonNull String pluginName,
            Map<String, String> requestParams,
            String database,
            Map<String, String> option) {
        // checkArgument(StringUtils.equalsIgnoreCase(database, DATABASE), "database must be
        // default");
        String topic = requestParams.get("topic");
        if (StringUtils.isNotBlank(topic)) {
            return Arrays.asList(topic);
        } else if (StringUtils.isNotBlank(database)) {
            return Arrays.asList(database);
        } else {
            return new ArrayList<>();
        }
    }

    @Override
    public List<String> getDatabases(
            @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
        String suffix = requestParams.get("topic");
        if (StringUtils.isNotEmpty(suffix)) {
            return Arrays.asList(suffix);
        }
        return DEFAULT_DATABASES;
    }

    @Override
    public boolean checkDataSourceConnectivity(
            @NonNull String pluginName, @NonNull Map<String, String> requestParams) {
        if (requestParams.isEmpty()) {
            throw new SeaTunnelException("requestParmas 为空！");
        }
        try {
            MqttClientService mqttClient = createMqttClient(requestParams);
            // just test the connection
            mqttClient.getMqttClient().connect();

            return StringUtils.isNotEmpty(mqttClient.getTopic());
        } catch (Exception ex) {
            throw new DataSourcePluginException(
                    "check mqtt connectivity failed, " + ex.getMessage(), ex);
        }
    }

    @Override
    public List<TableField> getTableFields(
            @NonNull String pluginName,
            @NonNull Map<String, String> requestParams,
            @NonNull String database,
            @NonNull String table) {
        // checkArgument(StringUtils.equalsIgnoreCase(database, DATABASE), "database must be
        // default");
        try {
            List<TableField> tableFields = new ArrayList<>();
            MqttClientService mqttClient = createMqttClient(requestParams);
            // just test the connection
            mqttClient.getMqttClient().connect();
            List<MqttMessage> mqttMessageList = initCallback.getMqttMessageList();
            if (CollectionUtils.isNotEmpty(mqttMessageList)) {
                MqttMessage mqttMessage = mqttMessageList.get(0);
                String s = new String(mqttMessage.getPayload());
                JSONObject jsonObject = JSONObject.parseObject(s);
                // 获取所有key的迭代器
                Iterator<String> keys = jsonObject.keySet().iterator();
                while (keys.hasNext()) {
                    String key = keys.next();
                    Object value = jsonObject.get(key);
                    TableField tableField = new TableField();
                    tableField.setName(key);
                    tableField.setType(value.getClass().getTypeName());
                    tableFields.add(tableField);
                }
                return tableFields;
            }

            return Collections.emptyList();
        } catch (Exception ex) {
            throw new DataSourcePluginException(
                    "check mqtt getTableFields failed, " + ex.getMessage(), ex);
        }
    }

    @Override
    public Map<String, List<TableField>> getTableFields(
            @NonNull String pluginName,
            @NonNull Map<String, String> requestParams,
            @NonNull String database,
            @NonNull List<String> tables) {
        //  checkArgument(StringUtils.equalsIgnoreCase(database, DATABASE), "database must be
        // default");
        if (CollectionUtils.isEmpty(tables)) {
            return Collections.emptyMap();
        }
        Map<String, List<TableField>> map = new HashMap<>();

        for (String table : tables) {

            map.put(table, getTableFields(pluginName, requestParams, database, table));
        }

        return map;
    }

    private MqttClientService createMqttClient(Map<String, String> requestParams) {

        String host = requestParams.get("host") + "";
        String port = requestParams.get("port") + "";
        String username = requestParams.get("username") + "";
        String password = requestParams.get("password") + "";
        String topic = requestParams.get("topic") + "";
        initCallback = new InitCallback();
        try {
            MqttClientService mqttClientService =
                    new MqttClientService(
                            host, Integer.parseInt(port), username, password, topic, initCallback);
            return mqttClientService;
        } catch (Exception e) {
            throw new SeaTunnelException("创建Mqtt客户端错误！");
        }
    }
}
