package com.atlassian.bitbucket.mesh.network;

import com.atlassian.bitbucket.mesh.junit.LogListener;
import com.google.common.collect.ImmutableSet;
import org.junit.Rule;
import org.junit.Test;

import static com.atlassian.bitbucket.mesh.util.MoreCollectors.toImmutableSet;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.*;

public class TopologyTest {

    private static final MeshNode NODE1 = node("1", "node1", "http://node-1:7777", false);
    private static final MeshNode NODE2 = node("2", "node2", "http://node-2:7777", true);
    private static final MeshNode NODE3 = node("3", "node3", "http://node-3:7777", false);

    @Rule
    public final LogListener logListener = new LogListener(Topology.class);

    @Test
    public void testBuilder() {
        Topology topology = new Topology.Builder()
                .partition(new Partition.Builder("0001")
                        .replicas(new PartitionReplica.Builder("replica-1", NODE2).build())
                        .replicas(new PartitionReplica.Builder("replica-2", NODE3).build())
                        .build())
                .partition(new Partition.Builder("0002")
                        .replicas(new PartitionReplica.Builder("replica-3", NODE1).build())
                        .replicas(new PartitionReplica.Builder("replica-4", NODE3).build())
                        .build())
                .build();

        //Verify that the local node is set correctly
        MeshNode localNode = topology.getLocalNode();
        assertEquals(NODE2, localNode);
        assertTrue(localNode.isLocal());
        assertEquals(NODE2.getId(), localNode.getId());
        assertEquals(NODE2.getName(), localNode.getName());
        assertEquals(NODE2.getRpcUrl(), localNode.getRpcUrl());

        //Verify that all nodes from the partitions are known at the topology level
        assertThat(topology.getNodes(), containsInAnyOrder(NODE1, NODE2, NODE3));

        //Verify that getNodeById works
        assertNull(topology.findNodeById("does-not-exist"));
        assertEquals(NODE1, topology.findNodeById(NODE1.getId()));

        //Verify that the partitions are as registered as expected
        assertPartition(topology.getPartition("0001"), "0001", NODE2, NODE3);
        assertPartition(topology.getPartition("0002"), "0002", NODE1, NODE3);
    }

    private static void assertPartition(Partition partition, String id, MeshNode... expectedNodes) {
        assertNotNull("Partition not found", partition);
        assertEquals("Partition ID does not match", id, partition.getId());
        assertEquals("Partition replicas do not match", ImmutableSet.copyOf(expectedNodes),
                partition.getReplicas().stream()
                        .map(PartitionReplica::getNode)
                        .collect(toImmutableSet()));
    }

    private static MeshNode node(String id, String name, String rpcUrl, boolean local) {
        return new MeshNode.Builder(id)
                .local(local)
                .name(name)
                .rpcUrl(rpcUrl)
                .build();
    }
}