package com.mobify.astro.plugins.webviewplugin;

import android.graphics.Color;
import android.net.Uri;
import android.support.test.annotation.UiThreadTest;
import android.webkit.CookieManager;

import com.mobify.astro.ActivityTestBase;
import com.mobify.astro.AstroWorker;
import com.mobify.astro.PluginResolver;
import com.mobify.astro.helpers.MockAstroWebViewFactory;
import com.mobify.astro.messaging.EventManager;
import com.mobify.astro.messaging.MessageSender;

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

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class WebViewPluginTest extends ActivityTestBase {
    private EventManager spyEventManager;
    private WebViewPlugin webViewPluginSpy;
    private LoadingContainerView containerViewMock;
    private AstroWebView mockWebView;
    private Uri mobifyUrl = Uri.parse("http://www.mobify.com");;
    private Uri localIndexUrl = Uri.parse("file://index.htm");

    @UiThreadTest
    @Before
    public void setUp() throws Exception {
        PluginResolver mockPluginResolver = mock(PluginResolver.class);
        spyEventManager = spy(new EventManager());
        MessageSender messageSender = new MessageSender(spyEventManager);
        webViewPluginSpy = spy(new WebViewPlugin(getActivity(), mockPluginResolver, spyEventManager, messageSender));


        containerViewMock = mock(LoadingContainerView.class);
        webViewPluginSpy.containerView = containerViewMock;

        mockWebView = MockAstroWebViewFactory.makeWebViewWithHistoryLength(2);
        webViewPluginSpy.webView = mockWebView;
    }

    @UiThreadTest
    @Test
    public void testInitialize() {
        assertNotNull(webViewPluginSpy.containerView);
        assertNotNull(webViewPluginSpy.webView);
        assertNotNull(webViewPluginSpy.manualShowPageHosts);
    }

    @UiThreadTest
    @Test
    public void testExecToApplicationAddress() throws Exception {
        webViewPluginSpy.exec(AstroWorker.ADDRESS, "{\"payload\":{\"key\": \"value\"}}");
        verify(spyEventManager).trigger(eq(AstroWorker.ADDRESS), any(JSONObject.class));
    }

    @UiThreadTest
    @Test
    public void testExecToEventsAddress() throws Exception {
        String address = webViewPluginSpy.getEventsAddress();
        webViewPluginSpy.exec(address, "{\"payload\":{\"key\": \"value\"}}");
        verify(spyEventManager).trigger(eq(address), any(JSONObject.class));
    }

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

    @UiThreadTest
    @Test
    public void testGetWebView() {
        assertEquals(mockWebView, webViewPluginSpy.getWebView());
    }

    @UiThreadTest
    @Test
    public void testGetView() {
        assertEquals(webViewPluginSpy.containerView, webViewPluginSpy.getView());
    }

    @UiThreadTest
    @Test
    public void testOnPause() {
        webViewPluginSpy.onPause();
        verify(mockWebView).saveState(webViewPluginSpy.getSavedPluginState());
    }

    @UiThreadTest
    @Test
    public void testDestroy() {
        webViewPluginSpy.destroy();
        verify(webViewPluginSpy).clearTimeoutTimer();
        verify(mockWebView).destroy();
    }

    @UiThreadTest
    @Test
    public void testReload() {
        webViewPluginSpy.reload();
        verify(mockWebView).reload();
    }

    @UiThreadTest
    @Test
    public void testCanGoBack() {
        // Our web view can't go back yet, so the plugin can't to back.
        doReturn(false).when(mockWebView).canGoBack();
        assertFalse(webViewPluginSpy.canGoBack());

        // If our web view can go back, then the plugin can go back.
        doReturn(true).when(mockWebView).canGoBack();
        assertTrue(webViewPluginSpy.canGoBack());
    }

    @UiThreadTest
    @Test
    public void testEnsureConnectivity() throws Exception{
        WebViewPlugin webViewPluginMock = mock(WebViewPlugin.class);
        doCallRealMethod().when(webViewPluginMock).ensureConnectivity(any(Uri.class));

        doReturn(false).when(webViewPluginMock).isOffline();
        assertTrue(webViewPluginMock.ensureConnectivity(mobifyUrl));
        assertTrue(webViewPluginMock.ensureConnectivity(localIndexUrl));

        doReturn(true).when(webViewPluginMock).isOffline();
        assertFalse(webViewPluginMock.ensureConnectivity(mobifyUrl));
        assertTrue(webViewPluginMock.ensureConnectivity(localIndexUrl));
    }

    @UiThreadTest
    @Test
    public void testIsShowPageManualForURL() {
        webViewPluginSpy.manualShowPageHosts.clear();
        webViewPluginSpy.manualShowPageHosts.add("mobify.com");

        assertTrue(webViewPluginSpy.isShowPageManualForURL("http://mobify.com/contact"));
        assertTrue(webViewPluginSpy.isShowPageManualForURL("https://mobify.com/hey/ho"));
        assertFalse(webViewPluginSpy.isShowPageManualForURL("http://www.mobify.com/astro"));
        assertFalse(webViewPluginSpy.isShowPageManualForURL("http://google.com"));
    }

    @UiThreadTest
    @Test
    public void testNavigateNoConnectivity() throws Exception {
        doReturn(false).when(webViewPluginSpy).ensureConnectivity(any(Uri.class));

        webViewPluginSpy.navigate("http://mobify.com");
        verify(mockWebView, never()).loadUrl("http://mobify.com");
        verify(webViewPluginSpy).clearTimeoutTimer();
    }

    @UiThreadTest
    @Test
    public void testNavigate() throws Exception {
        doReturn(true).when(webViewPluginSpy).ensureConnectivity(any(Uri.class));

        webViewPluginSpy.navigate("http://mobify.com");
        verify(mockWebView).loadUrl("http://mobify.com");
    }

    @UiThreadTest
    @Test
    public void testNavigateToAFile() throws Exception {
        doReturn(true).when(webViewPluginSpy).ensureConnectivity(any(Uri.class));

        webViewPluginSpy.navigate("file:///localwebpage.html");
        verify(mockWebView).loadUrl("file:///android_asset/localwebpage.html");
    }

    @UiThreadTest
    @Test
    public void testShowPage() {
        webViewPluginSpy.showPage();
        verify(containerViewMock).transitionToShowing();
    }

    @UiThreadTest
    @Test
    public void testGoBackCantGoBack() {
        when(webViewPluginSpy.canGoBack()).thenReturn(false);
        webViewPluginSpy.goBack();
        verify(mockWebView, never()).goBack();
    }

    @UiThreadTest
    @Test
    public void testGoBack() {
        doReturn(true).when(webViewPluginSpy).canGoBack();
        webViewPluginSpy.goBack();

        verify(mockWebView).goBack();
        verify(webViewPluginSpy).clearTimeoutTimer();
        verify(webViewPluginSpy).triggerBackEvent(any(String.class));
    }

    @UiThreadTest
    @Test
    public void testTriggerBackEvent() throws JSONException {
        webViewPluginSpy.triggerBackEvent("http://www.mobify.com");

        verify(webViewPluginSpy).triggerEvent(eq("back"), any(JSONObject.class));
    }

    @UiThreadTest
    @Test
    public void testSetBackgroundColor() {
        webViewPluginSpy.setBackgroundColor("#113377");
        verify(containerViewMock).setBackgroundColor(Color.parseColor("#113377"));
    }

    @UiThreadTest
    @Test
    public void testShowAndHideScrollBars() {
        webViewPluginSpy.hideScrollBars();
        assertFalse(webViewPluginSpy.areScrollbarsVisible);
        verify(mockWebView).setScrollBarVisibility(false);

        webViewPluginSpy.showScrollBars();
        assertTrue(webViewPluginSpy.areScrollbarsVisible);
        verify(mockWebView).setScrollBarVisibility(true);
    }

    @UiThreadTest
    @Test
    public void testSetScrollBarVisibilityFalse() {
        webViewPluginSpy.setScrollBarVisibility(false);

        verify(mockWebView).setScrollBarVisibility(false);
    }

    @UiThreadTest
    @Test
    public void testManuallyShowPageForHosts() throws Exception {
        webViewPluginSpy.manuallyShowPageForHosts(new JSONArray("['mobify.com']"));
        assertEquals(1, webViewPluginSpy.manualShowPageHosts.size());
        assertEquals("mobify.com", webViewPluginSpy.manualShowPageHosts.get(0));
        webViewPluginSpy.webClientPageFinished(mockWebView, "http://mobify.com");
        verify(webViewPluginSpy, times(1)).triggerEvent(eq(WebViewPlugin.EventNames.NAVIGATION_COMPLETED), any(JSONObject.class));
    }

    @UiThreadTest
    @Test
    public void testSetPageTimeoutDuration() {
        webViewPluginSpy.setPageTimeoutDuration(1351);
        assertEquals(1351, WebViewPlugin.pageTimeoutDuration);
    }

    @UiThreadTest
    @Test
    public void testGetCookie() {
        // On some devices we see previous set cookies persisting and when we look for cookie "foo"
        // we won't get null because it was previously set. Need to do a removeAllCookie (deprecated
        // but the new one is async so this is simplest).
        CookieManager cookieManager = CookieManager.getInstance();
        cookieManager.removeAllCookie();

        cookieManager.setCookie(MockAstroWebViewFactory.mockUrl, "baz=yaz;");
        cookieManager.setCookie(MockAstroWebViewFactory.mockUrl, "blah=");

        assertEquals(null, webViewPluginSpy.getCookie("foo"));
        assertEquals("yaz", webViewPluginSpy.getCookie("baz"));
        assertEquals("", webViewPluginSpy.getCookie("blah"));
    }

    @UiThreadTest
    @Test
    public void testDeleteCookie() {
        CookieManager cookieManager = CookieManager.getInstance();
        cookieManager.removeAllCookie();

        cookieManager.setCookie(MockAstroWebViewFactory.mockUrl, "foo=bar;");
        assertEquals("bar", webViewPluginSpy.getCookie("foo"));

        webViewPluginSpy.deleteCookie("foo");
        assertEquals(null, webViewPluginSpy.getCookie("foo"));
    }

    @UiThreadTest
    @Test
    public void testWebClientPageStartedWithTransitions() {
        webViewPluginSpy.webClientPageStarted(mockWebView, "http://mobify.com");
        verify(containerViewMock, times(1)).transitionToLoadingWithView(mockWebView, "http://mobify.com");
        verify(containerViewMock, never()).transitionToShowingWithView(mockWebView);
    }

    @UiThreadTest
    @Test
    public void testWebClientPageFinished() {
        webViewPluginSpy.webClientPageFinished(mockWebView, "http://mobify.com");
        verify(containerViewMock, times(1)).transitionToShowing();
        verify(webViewPluginSpy, times(1)).triggerEvent(eq(WebViewPlugin.EventNames.NAVIGATION_COMPLETED), any(JSONObject.class));
    }

    @UiThreadTest
    @Test
    public void testWebClientPageFinishedWithManualShowPageForHost() throws Exception {
        webViewPluginSpy.manuallyShowPageForHosts(new JSONArray("['mobify.com']"));
        webViewPluginSpy.webClientPageFinished(mockWebView, "http://mobify.com");
        verify(containerViewMock, never()).transitionToShowing();
    }

    @UiThreadTest
    @Test
    public void testWebClientNavigate() {
        boolean shouldOverrideNavigation;

        shouldOverrideNavigation = webViewPluginSpy.webClientNavigate(mockWebView, "http://mobify.com", false);
        assertEquals(true, shouldOverrideNavigation);

        shouldOverrideNavigation = webViewPluginSpy.webClientNavigate(mockWebView, "http://mobify.com", true);
        assertEquals(false, shouldOverrideNavigation);

        verify(webViewPluginSpy, times(2)).triggerEvent(eq(WebViewPlugin.EventNames.NAVIGATE), any(JSONObject.class));
    }

    @UiThreadTest
    @Test
    public void testDehydrate() {
        webViewPluginSpy.saveAndDestroyView();

        assertNull(webViewPluginSpy.webView);
        assertNotNull(webViewPluginSpy.webViewState);
    }

    @UiThreadTest
    @Test
    public void testRehydrate() {
        webViewPluginSpy.webView.loadUrl("http://www.example.com");
        String oldWebViewId = webViewPluginSpy.webView.toString();

        webViewPluginSpy.saveAndDestroyView();

        webViewPluginSpy.restoreInactiveViewFromSave();

        assertNull(webViewPluginSpy.webViewState);
        assertNotNull(webViewPluginSpy.webView);
        assertFalse(webViewPluginSpy.webView.toString().equalsIgnoreCase(oldWebViewId));
    }
}
