package com.priusis.client.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.priusis.client.extensions.ExtensionService;
import com.priusis.client.extensions.http.DefaultHttpService;
import com.priusis.client.extensions.http.HttpService;
import com.priusis.client.service.conf.PcExtensionConfiguration;
import com.priusis.client.service.core.MqttService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by priusis on 29.09.17.
 */
@Slf4j
@Data
public class TenantServiceRegistry implements ExtensionServiceCreation {

    private MqttService service;
    private final Map<String, ExtensionService> extensions;
    private final Map<String, HttpService> httpServices;

    private static final String STATUS_INIT = "Initialized";
    private static final String STATUS_UPDATE = "Updated";
    private static final String STATUS_DELETE = "Removed";
    private static final String HTTP_EXTENSION = "HTTP";
    private static final String OPC_EXTENSION = "OPC UA";
    private static final String MQTT_EXTENSION = "MQTT";
    private static final String FILE_EXTENSION = "FILE";

    public TenantServiceRegistry() {
        this.extensions = new HashMap<>();
        this.httpServices = new HashMap<>();
    }

    public void updateExtensionConfiguration(String config) {
        log.info("[{}] Updating extension configuration", service.getTenantLabel());
        ObjectMapper mapper = new ObjectMapper();
        try {
            List<PcExtensionConfiguration> updatedConfigurations = new ArrayList<>();
            for (JsonNode updatedExtension : mapper.readTree(config)) {
                updatedConfigurations.add(mapper.treeToValue(updatedExtension, PcExtensionConfiguration.class));
            }
            for (String existingExtensionId : extensions.keySet()) {
                if (!extensionIdContainsInArray(existingExtensionId, updatedConfigurations)) {
                    log.info("Destroying extension: [{}]", existingExtensionId);
                    extensions.get(existingExtensionId).destroy();
                    extensions.remove(existingExtensionId);
                    httpServices.remove(existingExtensionId);
                    service.onConfigurationStatus(existingExtensionId, STATUS_DELETE);
                }
            }
            for (PcExtensionConfiguration updatedConfiguration : updatedConfigurations) {
                if (!extensions.containsKey(updatedConfiguration.getId())) {
                    log.info("Initializing extension: [{}][{}]", updatedConfiguration.getId(), updatedConfiguration.getType());
                    ExtensionService extension = createExtensionServiceByType(service, updatedConfiguration.getType());
                    service.onConfigurationStatus(updatedConfiguration.getId(), STATUS_INIT);
                    if (HTTP_EXTENSION.equals(updatedConfiguration.getType())) {
                        httpServices.put(updatedConfiguration.getId(), (HttpService) extension);
                    }
                    extensions.put(updatedConfiguration.getId(), extension);
                } else {
                    if (!updatedConfiguration.equals(extensions.get(updatedConfiguration.getId()).getCurrentConfiguration())) {
                        log.info("Updating extension: [{}][{}]", updatedConfiguration.getId(), updatedConfiguration.getType());
                        extensions.get(updatedConfiguration.getId()).update(updatedConfiguration);
                        service.onConfigurationStatus(updatedConfiguration.getId(), STATUS_UPDATE);
                    }
                }
            }
        } catch (Exception e) {
            log.info("Failed to read configuration attribute", e);
            throw new RuntimeException("Failed to update configuration", e);
        }
    }

    private boolean extensionIdContainsInArray(String extensionId, List<PcExtensionConfiguration> array) {
        for (PcExtensionConfiguration configuration : array) {
            if (configuration.getId().equalsIgnoreCase(extensionId)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public ExtensionService createExtensionServiceByType(MqttService gateway, String type) {
        switch (type) {
            case HTTP_EXTENSION:
                return new DefaultHttpService(gateway);
            default:
                throw new IllegalArgumentException("Extension: " + type + " is not supported!");
        }
    }

}
