package com.cmvr.quic.gateway.core;

import com.cmvr.quic.contract.QuicEventBus;
import com.cmvr.quic.gateway.v1.MediaFrame;
import com.cmvr.quic.gateway.v1.NodeEventType;
import com.cmvr.quic.gateway.v1.NodeSnapshot;
import org.junit.Assert;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

public class NodeEventBrokerTest {

    @Test
    public void shouldIgnoreOfflineEventFromReplacedSession() {
        NodeEventBroker broker = new NodeEventBroker(4);
        try {
            broker.publishNode(NodeEventType.NODE_EVENT_TYPE_REGISTERED,
                    node("edge-1", "old-session", true));
            broker.publishNode(NodeEventType.NODE_EVENT_TYPE_REGISTERED,
                    node("edge-1", "new-session", true));
            broker.publishNode(NodeEventType.NODE_EVENT_TYPE_OFFLINE,
                    node("edge-1", "old-session", false));

            NodeSnapshot current = broker.listNodes().iterator().next();
            Assert.assertEquals("new-session", current.getSessionId());
            Assert.assertTrue(current.getOnline());
        } finally {
            broker.close();
        }
    }

    @Test
    public void shouldPublishNodeEventsDirectlyAndAllowUnsubscribe() {
        NodeEventBroker broker = new NodeEventBroker(4);
        List<NodeEventType> received = new ArrayList<NodeEventType>();
        try {
            QuicEventBus.Subscription subscription = broker.watchNodes(false,
                    event -> received.add(event.getType()));
            broker.publishNode(NodeEventType.NODE_EVENT_TYPE_REGISTERED,
                    node("edge-1", "session-1", true));
            subscription.close();
            broker.publishNode(NodeEventType.NODE_EVENT_TYPE_HEARTBEAT,
                    node("edge-1", "session-1", true));

            Assert.assertEquals(1, received.size());
            Assert.assertEquals(NodeEventType.NODE_EVENT_TYPE_REGISTERED, received.get(0));
        } finally {
            broker.close();
        }
    }

    @Test
    public void shouldPublishMatchingMediaFramesDirectly() throws Exception {
        NodeEventBroker broker = new NodeEventBroker(4);
        CountDownLatch received = new CountDownLatch(1);
        AtomicReference<MediaFrame> actual = new AtomicReference<MediaFrame>();
        try {
            QuicEventBus.Subscription subscription = broker.subscribeMedia("edge-1", 2, 4,
                    frame -> {
                        actual.set(frame);
                        received.countDown();
                    });
            broker.publishMedia(MediaFrame.newBuilder().setNodeId("edge-2").setTrackId(2).build());
            MediaFrame expected = MediaFrame.newBuilder().setNodeId("edge-1").setTrackId(2).build();
            broker.publishMedia(expected);

            Assert.assertTrue(received.await(2, TimeUnit.SECONDS));
            Assert.assertEquals(expected, actual.get());
            subscription.close();
        } finally {
            broker.close();
        }
    }

    private static NodeSnapshot node(String nodeId, String sessionId, boolean online) {
        return NodeSnapshot.newBuilder()
                .setNodeId(nodeId)
                .setSessionId(sessionId)
                .setOnline(online)
                .build();
    }
}
