package com.mobify.astro.plugins;

import android.support.test.annotation.UiThreadTest;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;

import com.mobify.astro.ActivityTestBase;
import com.mobify.astro.AstroWorker;
import com.mobify.astro.PluginManager;
import com.mobify.astro.messaging.EventManager;
import com.mobify.astro.messaging.EventMessage;
import com.mobify.astro.messaging.JSONCallback;
import com.mobify.astro.messaging.JsonMessageFactory;
import com.mobify.astro.messaging.Message;
import com.mobify.astro.messaging.MessageSender;
import com.mobify.astro.plugins.webviewplugin.AstroWebViewPlugin;
import com.mobify.astro.utilities.AstroWebUtilities;

import junit.framework.Assert;

import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;

import java.lang.reflect.Method;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

public class AstroWorkerTest extends ActivityTestBase {
    PluginManager pluginManager;
    private AstroWorker testWorker;
    EventManager mockEventManager;

    @UiThreadTest
    @Before
    public void setUp() throws Exception {
        mockEventManager = mock(EventManager.class);
        MessageSender messageSender = new MessageSender(mockEventManager);
        pluginManager = new PluginManager(getActivity(), mockEventManager, messageSender);
        testWorker = new AstroWorker(getActivity(), pluginManager, mockEventManager, messageSender);
    }

    @UiThreadTest
    @Test
    public void testInitialized() {
        WebView webView = testWorker.getWebView();

        Assert.assertTrue(webView.getSettings().getJavaScriptEnabled());
        Assert.assertTrue(webView.getSettings().getUserAgentString().contains(AstroWebUtilities.ASTRO_USER_AGENT));
    }

    @UiThreadTest
    @Test
    public void testOnMessage() throws Exception {
        AstroWorker workerSpy = spy(testWorker);

        JSONObject json = new JSONObject("{\"payload\":{\"eventName\":\"someEvent\"}}");
        Message message = JsonMessageFactory.fromJSON(json);

        workerSpy.onMessage("address", message);

        String expectedJS = String.format(AstroWebViewPlugin.RECEIVE_MESSAGE_FORMAT_STRING, "address", message.toString());
        verify(workerSpy).invokeJavaScript(expectedJS);
    }

    @UiThreadTest
    @Test
    public void testExec() throws Exception {
        testWorker.exec("DestinationPlugin:123", "{\"payload\":{\"key\": \"value\"}}");
        verify(mockEventManager).trigger(eq("DestinationPlugin:123"), any(JSONObject.class));
    }

    @UiThreadTest
    @Test
    public void testExecWithSelfEventsKeyword() throws Exception {
        testWorker.exec("self:events", "{\"payload\":{\"key\": \"value\"}}");

        String expectedAddress = testWorker.getInstanceAddress() + ":" + EventMessage.EVENTS_KEYWORD;
        verify(mockEventManager).trigger(eq(expectedAddress), any(JSONObject.class));
    }

    @UiThreadTest
    @Test
    public void testSubscribeToEvents() {
        String testAddress = "TestPlugin:123";
        String expectedAddress = testAddress + ":" + EventMessage.EVENTS_KEYWORD;

        testWorker.subscribeToEvents(testAddress);

        verify(mockEventManager).on(eq(expectedAddress), any(JSONCallback.class));
    }

    @UiThreadTest
    @Test
    public void testExecJavascriptInterfaceAnnotation() {
        for (Method method : testWorker.getClass().getMethods()) {
            boolean isJavascriptInterface = method.isAnnotationPresent(JavascriptInterface.class);

            if (method.getName().equals("exec")) {
                Assert.assertTrue("exec should be decorated with JavascriptInterface.", isJavascriptInterface);
                continue;
            }

            if (isJavascriptInterface) {
                // See `RpcMessageListener` for more details.
                Assert.fail("For compatibility with API <=16, only exec can be decorated with JavascriptInterface.");
                return;
            }
        }
    }
}
