package dst.ass3.messaging.impl;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;

import java.util.concurrent.TimeUnit;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;

import dst.ass3.messaging.Constants;
import dst.ass3.messaging.IMessagingFactory;
import dst.ass3.messaging.IQueueManager;
import dst.ass3.messaging.IRequestGateway;
import dst.ass3.messaging.RabbitResource;
import dst.ass3.messaging.RequestType;
import dst.ass3.messaging.UploadRequest;

public class RequestGatewayTest {

    private static final Logger LOG = LoggerFactory.getLogger(RequestGatewayTest.class);

    @Rule
    public RabbitResource rabbit = new RabbitResource();

    @Rule
    public Timeout timeout = new Timeout(10, TimeUnit.SECONDS);

    private IMessagingFactory factory;
    private IQueueManager queueManager;
    private IRequestGateway requestGateway;

    @Before
    public void setUp() throws Exception {
        factory = new MessagingFactory();
        queueManager = factory.createQueueManager();
        requestGateway = factory.createRequestGateway();

        queueManager.setUp();
    }

    @After
    public void tearDown() throws Exception {
        queueManager.tearDown();

        requestGateway.close();
        queueManager.close();
        factory.close();
    }

    @Test
    public void uploadRequest_routesRequestsToCorrectQueues() throws Exception {
        UploadRequest r1 = new UploadRequest("id1", RequestType.DOCUMENT, "urn:material:document:1");
        UploadRequest r2 = new UploadRequest("id2", RequestType.DOCUMENT, "urn:material:document:2");
        UploadRequest r3 = new UploadRequest("id3", RequestType.VIDEO, "urn:material:video:1");

        LOG.info("Sending request {}", r1);
        requestGateway.uploadRequest(r1);
        LOG.info("Sending request {}", r2);
        requestGateway.uploadRequest(r2);
        LOG.info("Sending request {}", r3);
        requestGateway.uploadRequest(r3);

        LOG.info("Taking request from queue {}", Constants.QUEUE_DOCUMENT);
        Message m1 = rabbit.getClient().receive(Constants.QUEUE_DOCUMENT, 1000);
        assertThat(m1, notNullValue());

        LOG.info("Taking request from queue {}", Constants.QUEUE_DOCUMENT);
        Message m2 = rabbit.getClient().receive(Constants.QUEUE_DOCUMENT, 1000);
        assertThat(m2, notNullValue());

        LOG.info("Taking request from queue {}", Constants.QUEUE_VIDEO);
        Message m3 = rabbit.getClient().receive(Constants.QUEUE_VIDEO, 1000);
        assertThat(m3, notNullValue());

        assertThat("Expected queue to be empty as no request for that type were issued",
                rabbit.getClient().receive(Constants.QUEUE_QUIZ, 1000), nullValue());
    }

    @Test
    public void uploadRequest_serializesIntoJsonFormat() throws Exception {
        UploadRequest r1 = new UploadRequest("id1", RequestType.QUIZ, "urn:material:quiz:1");
        UploadRequest r2 = new UploadRequest("id2", RequestType.DOCUMENT, "urn:material:document:1");
        UploadRequest r3 = new UploadRequest("id3", RequestType.VIDEO, "urn:material:video:1");

        LOG.info("Sending request {}", r1);
        requestGateway.uploadRequest(r1);
        LOG.info("Sending request {}", r2);
        requestGateway.uploadRequest(r2);
        LOG.info("Sending request {}", r3);
        requestGateway.uploadRequest(r3);

        LOG.info("Taking request from queue {}", Constants.QUEUE_QUIZ);
        Message m1 = rabbit.getClient().receive(Constants.QUEUE_QUIZ, 1000);
        assertThat(m1, notNullValue());
        assertThat(new String(m1.getBody()),
                equalTo("{\"id\":\"id1\",\"type\":\"QUIZ\",\"urn\":\"urn:material:quiz:1\"}"));

        LOG.info("Taking request from queue {}", Constants.QUEUE_DOCUMENT);
        Message m2 = rabbit.getClient().receive(Constants.QUEUE_DOCUMENT, 1000);
        assertThat(m2, notNullValue());
        assertThat(new String(m2.getBody()),
                equalTo("{\"id\":\"id2\",\"type\":\"DOCUMENT\",\"urn\":\"urn:material:document:1\"}"));

        LOG.info("Taking request from queue {}", Constants.QUEUE_VIDEO);
        Message m3 = rabbit.getClient().receive(Constants.QUEUE_VIDEO, 1000);
        assertThat(m3, notNullValue());
        assertThat(new String(m3.getBody()),
                equalTo("{\"id\":\"id3\",\"type\":\"VIDEO\",\"urn\":\"urn:material:video:1\"}"));
    }
}