/*-
 * ~~~~~~licensing~~~~~~
 * connection-manager
 * ==========
 * Copyright (C) 2020 - 2021 EmDev LLC
 * ==========
 * 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.
 * ~~~~~~/licensing~~~~~~
 */
package ru.entaxy.esb.platform.runtime.core.management.connection.impl;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.FrameworkUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.entaxy.esb.platform.runtime.base.connecting.connection.Connection;
import ru.entaxy.esb.platform.runtime.base.connecting.connection.util.ConnectionUtil;
import ru.entaxy.esb.platform.runtime.base.connecting.producer.connection.util.ConnectionProducerUtil;
import ru.entaxy.esb.platform.runtime.core.management.connection.api.ConnectionManager;
import ru.entaxy.esb.system.management.blueprint.generator.Blueprint;

import java.io.File;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;

public class ConnectionManagerImpl implements ConnectionManager {

    private static final String CONNECTION = "connection";
    private static final String GROUP_ID = "ru.entaxy.esb.";
    private static final String XML_EXTENSION = "xml";
    private static final String LOCAL_FILE_REPOSITORY = "data" + File.separator + "local-repository";
    private static final Logger log = LoggerFactory.getLogger(ConnectionManagerImpl.class);

    public List<Connection> createAndInstallConnections(String json) throws Exception {
        return createConnections(json, false, true);
    }

    private List<Connection> createConnections(String json) throws Exception {
        return createConnections(json, false, false);
    }

    private List<Connection> createConnections(String json, boolean register, boolean install) throws Exception {
        List<Connection> connectionList = new ArrayList<>();
        JsonElement jsonElement = getJsonElement(json);
        List<JsonObject> jsonConnectionList = getJsonConnectionList(jsonElement);
        for (JsonObject jsonConnection : jsonConnectionList) {
            connectionList.add(createConnection(jsonConnection, register, install));
        }
        return connectionList;
    }

    private JsonElement getJsonElement(String json) {
        return new Gson().fromJson(json, JsonElement.class);
    }

    private List<JsonObject> getJsonConnectionList(JsonElement element) {
        List<JsonObject> jsonConnectionList = new ArrayList<>();

        if (element.isJsonObject()) {
            JsonObject object = element.getAsJsonObject();
            if (object.has("nodeType") &&
                    "connection".equals(object.get("nodeType").getAsString())) {
                jsonConnectionList.add(object);
            } else {
                for (Map.Entry<String, JsonElement> entry : object.entrySet()) {
                    jsonConnectionList.addAll(getJsonConnectionList(entry.getValue()));
                }
            }
        } else if (element.isJsonArray()) {
            for (JsonElement jsonElement : element.getAsJsonArray()) {
                jsonConnectionList.addAll(getJsonConnectionList(jsonElement));
            }
        }

        return jsonConnectionList;
    }

    private Connection createConnection(JsonObject jsonConnection, boolean register, boolean install) {
        Connection connection = ConnectionProducerUtil.createConnection(jsonConnection);
        if (register) {
            // TODO: 09.07.2021 registration method
        }
        if (install) {
            try {
                installConnection(connection);
            } catch (Exception e) {
                log.error("Can't install connection: {}", e.getMessage());
            }
        }
        return connection;
    }

    public Bundle installConnection(Connection connection) throws Exception {
        String name = connection.getName();
        String type = CONNECTION;
        String version = String.valueOf(1.0);
        Blueprint blueprint = ConnectionUtil.getInit(connection);
        log.debug(new String(blueprint.getBody(), Charset.defaultCharset()));


//        return bundleManager.installAndStartBundle(name, type, version, blueprint);
        // Local bundle install needed instead of cellar install to cluster
        String bundleUrl = deployBlueprint(GROUP_ID + type, type + "-" + name,
                version, XML_EXTENSION, blueprint.getBody());
        Bundle bundle = installAndStartBundle(bundleUrl);
        return bundle;
        // TODO: 09.07.2021  callback for webeditor needed
    }

    // ru.entaxy.esb.system.deployer.file.FileSystemRepositoryImpl.deployBlueprint method copy
    public String deployBlueprint(String groupId, String name, String version,
                                  String extension, byte[] data) throws Exception {
        Path path = preparePath(groupId, name, version, extension);
        if (!Files.exists(path.getParent())) {
            Files.createDirectories(path.getParent());
        }
        Files.write(path, data);
        return "blueprint:file:" + path.normalize();
    }

    private Path preparePath(String groupId, String name, String version, String extension) {
        String gpoupIdPath = groupId.replaceAll("\\.", Matcher.quoteReplacement(File.separator));
        String fileName = name + "-" + version + "." + extension;
        Path path = Paths.get(LOCAL_FILE_REPOSITORY, gpoupIdPath, fileName);
        return path;
    }

    private Bundle installAndStartBundle(String bundleUrl) {
        BundleContext bundleContext = FrameworkUtil.getBundle(ConnectionManagerImpl.class).getBundleContext();
        Bundle bundle = null;
        try {
            bundle = bundleContext.installBundle(bundleUrl, null);
            try {
                bundle.start();
            } catch (BundleException e) {
                log.error("Unable to start bundle " + bundle.getLocation() + ": " + e, e);
            }
        } catch (BundleException e) {
            log.error("Unable to install bundle " + bundle.getLocation() + ": " + e, e);
        }
        return bundle;
    }


}
