package com.mobify.astro.messaging;

import org.junit.Before;
import org.junit.Test;

import java.lang.reflect.Method;

import static junit.framework.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

public class RpcMethodDispatcherTest {

    private MessageSender mockMessageSender;
    private RpcMessageListenerTestHelpers.TestRpcMessageListenerClass testRpcMessageListener;
    private RpcMessageListenerTestHelpers.RpcResponseHolder responseHolder;
    private RpcMethodDispatcher spyRpcMethodDispatcher;

    @Before
    public void setup() throws Exception {
        EventManager mockEventManager = mock(EventManager.class);
        mockMessageSender = mock(MessageSender.class);
        spyRpcMethodDispatcher = spy(new RpcMethodDispatcher(mockMessageSender));
        testRpcMessageListener = new RpcMessageListenerTestHelpers.TestRpcMessageListenerClass(mockEventManager, mockMessageSender);
        responseHolder = new RpcMessageListenerTestHelpers.RpcResponseHolder(mockEventManager);
    }

    @Test
    public void testDispatchForSync() throws Exception {
        Method area = RpcMessageListenerTestHelpers.TestRpcMessageListenerClass.class.getMethod("area", int.class, int.class);
        RpcMethodWrapper testMethod = new RpcMethodWrapper(area, RpcMethodWrapper.RpcMethodType.SYNC);
        testMethod = spy(testMethod);

        RpcResponse response = RpcRequestFactory.emptyRequest().getResponse(responseHolder);
        Object[] params = new Object[]{5, 5};
        spyRpcMethodDispatcher.dispatch(testMethod, testRpcMessageListener, response, params);
        verify(spyRpcMethodDispatcher, times(1)).dispatchSync(testMethod, testRpcMessageListener, response, params);
    }

    @Test
    public void testDispatchForAsync() throws Exception {
        Method asyncTest = RpcMessageListenerTestHelpers.TestRpcMessageListenerClass.class.getMethod("asyncTest", RpcResponse.class, String.class);
        RpcMethodWrapper testMethod = new RpcMethodWrapper(asyncTest, RpcMethodWrapper.RpcMethodType.ASYNC);
        testMethod = spy(testMethod);

        RpcResponse response = RpcRequestFactory.emptyRequest().getResponse(responseHolder);
        Object[] params = new Object[]{"param1"};
        spyRpcMethodDispatcher.dispatch(testMethod, testRpcMessageListener, response, params);
        verify(spyRpcMethodDispatcher, times(1)).dispatchAsync(testMethod, testRpcMessageListener, response, params);
    }

    @Test
    public void testDispatchSync() throws Exception {
        Method area = RpcMessageListenerTestHelpers.TestRpcMessageListenerClass.class.getMethod("area", int.class, int.class);
        RpcMethodWrapper testMethod = new RpcMethodWrapper(area, RpcMethodWrapper.RpcMethodType.SYNC);

        RpcResponse response = RpcRequestFactory.emptyRequest().getResponse(responseHolder);
        Object[] params = new Object[]{5, 5};
        spyRpcMethodDispatcher.dispatchSync(testMethod, testRpcMessageListener, response, params);
        assertEquals(false, testRpcMessageListener.ranAsyncTest);
        verify(mockMessageSender, times(1)).sendRpcResponse(response);
        assertEquals(25, response.getPayload().get(RpcResponse.KeyNames.RESULT));
    }

    @Test
    public void testDispatchAsync() throws Exception {
        Method asyncTest = RpcMessageListenerTestHelpers.TestRpcMessageListenerClass.class.getMethod("asyncTest", RpcResponse.class, String.class);
        RpcMethodWrapper testMethod = new RpcMethodWrapper(asyncTest, RpcMethodWrapper.RpcMethodType.ASYNC);

        RpcResponse response = RpcRequestFactory.emptyRequest().getResponse(responseHolder);
        Object[] params = new Object[]{"bar"};
        spyRpcMethodDispatcher.dispatchAsync(testMethod, testRpcMessageListener, response, params);
        assertEquals(true, testRpcMessageListener.ranAsyncTest);

        verify(mockMessageSender, times(1)).sendRpcResponseWithResult(response, "Foo: bar");
    }
}
