package com.atlassian.bitbucket.mesh.network;

import com.atlassian.bitbucket.mesh.client.MeshClient;
import com.atlassian.bitbucket.mesh.management.SupportInfoProvider;
import com.fasterxml.jackson.core.JsonGenerator;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;

import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Provides Mesh connectivity diagnostic information.
 */
public class ConnectivitySupportInfoProvider implements SupportInfoProvider {

    private final MeshClient meshClient;
    private final MeshManager meshManager;

    public ConnectivitySupportInfoProvider(MeshClient meshClient, MeshManager meshManager) {
        this.meshClient = meshClient;
        this.meshManager = meshManager;
    }

    @Override
    public void writeSupportInfo(@Nonnull JsonGenerator json) throws IOException {
        json.writePOJOField("meshConnectivitySummary", getSummary());
    }

    private Map<String, Object> getNodeConnectivityInformation(MeshNode node, ConnectivitySummary summary) {
        return ImmutableMap.of(
                "node", getNodeInformation(node),
                "summary", getNodeSummary(summary)
        );
    }

    private Map<String, String> getNodeInformation(MeshNode node) {
        ImmutableMap.Builder<String, String> nodeInfo = ImmutableMap.builder();

        nodeInfo.put("id", node.getId());
        if (node.getName() != null) {
            nodeInfo.put("name", node.getName());
        }

        return nodeInfo.build();
    }

    private Map<String, Object> getNodeSummary(ConnectivitySummary connectivitySummary) {
        ImmutableMap.Builder<String, Object> summary = ImmutableMap.builder();
        if (connectivitySummary.isReachable()) {
            summary.put("reachable", connectivitySummary.isReachable());
            summary.put("roundTripTime", connectivitySummary.getRoundTripTime());
        } else {
            summary.put("errorMessage", connectivitySummary.getErrorMessage());
            summary.put("statusCode", connectivitySummary.getStatusCode());
        }
        return summary.build();
    }

    private Map<String, Object> getSummary() {
        ImmutableList.Builder<Map<String, Object>> connectivity = ImmutableList.builder();

        Collection<MeshNode> nodes = meshManager.getNodes().stream()
                .filter(MeshNode::isRemote)
                .collect(Collectors.toList());
        for (MeshNode node : nodes) {
            ConnectivitySummary summary = meshClient.ping(node);
            connectivity.add(getNodeConnectivityInformation(node, summary));
        }

        return ImmutableMap.of(
                "localNode", getNodeInformation(meshManager.getLocalNode()),
                "connectivity", connectivity.build()
        );
    }
}
