/**
 * Copyright © 2018 Dario Balinzo ([email protected])
 * <p>
 * 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
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * 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 com.github.dariobalinzo.schema;

import com.github.dariobalinzo.utils.Utils;
import org.apache.kafka.connect.data.Schema;
import org.apache.kafka.connect.data.SchemaBuilder;

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

public class SchemaConverter {

    public static Schema convertElasticMapping2AvroSchema(Map<String, Object> doc, String name) {

        SchemaBuilder schemaBuilder = SchemaBuilder.struct().name(
                Utils.filterAvroName("", name)); //characters not valid for avro schema name
        convertDocumentSchema("", doc, schemaBuilder);
        return schemaBuilder.build();

    }


    private static void convertDocumentSchema(String prefixName, Map<String, Object> doc, SchemaBuilder schemaBuilder) {

        doc.keySet().forEach(
                k -> {
                    Object v = doc.get(k);
                    if (v instanceof String) {
                        schemaBuilder.field(Utils.filterAvroName(k), Schema.OPTIONAL_STRING_SCHEMA);
                    } else if (v instanceof Integer) {
                        schemaBuilder.field(Utils.filterAvroName(k), Schema.OPTIONAL_INT32_SCHEMA);
                    } else if (v instanceof Long) {
                        schemaBuilder.field(Utils.filterAvroName(k), Schema.OPTIONAL_INT64_SCHEMA);
                    } else if (v instanceof Float) {
                        schemaBuilder.field(Utils.filterAvroName(k), Schema.OPTIONAL_FLOAT32_SCHEMA);
                    } else if (v instanceof Double) {
                        schemaBuilder.field(Utils.filterAvroName(k), Schema.OPTIONAL_FLOAT64_SCHEMA);
                    } else if (v instanceof List) {

                        if (!((List) v).isEmpty()) {
                            //assuming that every item of the list has the same schema
                            Object item = ((List) v).get(0);
                            if (item instanceof String) {
                                schemaBuilder.field(Utils.filterAvroName(k), SchemaBuilder.array(SchemaBuilder.OPTIONAL_STRING_SCHEMA)
                                        .optional()
                                        .build()
                                ).build();
                            } else  if (item instanceof Integer) {
                                schemaBuilder.field(Utils.filterAvroName(k), SchemaBuilder.array(SchemaBuilder.OPTIONAL_INT32_SCHEMA)
                                        .optional()
                                        .build()
                                ).build();
                            } else if ( item instanceof Long) {
                                schemaBuilder.field(Utils.filterAvroName(k), SchemaBuilder.array(SchemaBuilder.OPTIONAL_INT64_SCHEMA)
                                        .optional()
                                        .build()
                                ).build();
                            } else if (item instanceof Float) {
                                schemaBuilder.field(Utils.filterAvroName(k), SchemaBuilder.array(SchemaBuilder.OPTIONAL_FLOAT32_SCHEMA)
                                        .optional()
                                        .build()
                                ).build();
                            } if (item instanceof Double ) {
                                schemaBuilder.field(Utils.filterAvroName(k), SchemaBuilder.array(SchemaBuilder.OPTIONAL_FLOAT64_SCHEMA)
                                        .optional()
                                        .build()
                                ).build();
                            } else if (item instanceof Map) {

                                SchemaBuilder nestedSchema = SchemaBuilder.struct()
                                        .name(Utils.filterAvroName(prefixName, k))
                                        .optional();
                                        convertDocumentSchema(Utils.filterAvroName(prefixName, k) + ".",
                                        (Map<String, Object>) item,
                                        nestedSchema);
                                schemaBuilder.field(Utils.filterAvroName(k), SchemaBuilder.array(nestedSchema.build()));
                            } else {
                                throw new RuntimeException("error in converting list: type not supported");
                            }

                        }

                    } else if (v instanceof Map) {

                        SchemaBuilder nestedSchema = SchemaBuilder.struct().name(Utils.filterAvroName(prefixName, k)).optional();
                        convertDocumentSchema(Utils.filterAvroName(prefixName, k) + ".",
                                (Map<String, Object>) v,
                                        nestedSchema
                                );
                        schemaBuilder.field(Utils.filterAvroName(k), nestedSchema.build());

                    } else {
                        throw new RuntimeException("type not supported " + k);
                    }
                }
        );

    }


}