package com.mobify.astro;

import android.os.Bundle;

import com.mobify.astro.messaging.EventManager;
import com.mobify.astro.messaging.MessageSender;
import com.mobify.astro.messaging.SenderMessage;

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

import java.util.HashMap;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;

public class PluginManagerTest extends ActivityTestBase {

    private PluginManager pluginManager;
    private EventManager mockEventManager;
    private MessageSender mockMessageSender;

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

    @After
    public void tearDown() {
        pluginManager.onDestroy();
    }

    @Test
    public void testConstructor() {
        PluginManager manager = new PluginManager(getActivity(), mockEventManager, mockMessageSender);

        assertSame(getActivity(), manager.getActivity());
    }

    @Test
    public void testGetInstanceAddress() {
        String expectedAddress = "PluginManager:0";
        String address = pluginManager.getInstanceAddress();

        assertEquals(expectedAddress, address);
    }

    @Test
    public void testCreatePlugin() throws Exception {
        pluginManager.register(TestPlugin.class);
        pluginManager.createPlugin("TestPlugin", new JSONObject());

        assertEquals(1, pluginManager.getPluginInstances().size());
    }

    @Test
    public void testOnSaveInstanceState() {
        AstroPlugin testPlugin = new TestPlugin(getActivity(), pluginManager, mockEventManager, mockMessageSender);

        // Name the test plugin instance so we can save it's state
        testPlugin.setInstanceName("TestPluginMock");
        pluginManager.addPluginToInstanceList(testPlugin);

        Bundle savedState = new Bundle();
        pluginManager.onSaveInstanceState(savedState);

        Bundle savedPluginState = savedState.getBundle(testPlugin.getInstanceName());

        assertSame(savedPluginState, testPlugin.getSavedPluginState());
    }

    @Test
    public void testRegister() {
        pluginManager.register(TestPlugin.class);

        HashMap<String, Class<? extends AstroPlugin>> classes;
        classes = pluginManager.getPluginClasses();
        Class registeredClass = classes.get("TestPlugin");

        assertEquals(TestPlugin.class, registeredClass);
    }

    @Test
    public void testCreatePluginInstanceFromNameWithoutOptions() throws Exception {
        pluginManager.register(TestPlugin.class);
        AstroPlugin plugin = pluginManager.createPluginInstanceFromName("TestPlugin", new JSONObject());

        assertEquals(TestPlugin.class, plugin.getClass());
    }

    @Test
    public void testCreatePluginInstanceFromNameWithOptions() throws Exception {
        JSONObject options = new JSONObject();
        options.put("testKey", "testValue");

        pluginManager.register(TestPluginWithInitOptions.class);
        TestPluginWithInitOptions plugin = (TestPluginWithInitOptions) pluginManager.createPluginInstanceFromName("TestPluginWithInitOptions", options);

        assertEquals(options, plugin.options);
    }

    @Test(expected=AstroException.class)
    public void testCreatePluginInstanceFromNameThrowsAstroException() throws Exception {
        pluginManager.register(TestPluginThrowsAstroException.class);
        TestPluginThrowsAstroException plugin = (TestPluginThrowsAstroException) pluginManager.createPluginInstanceFromName("TestPluginThrowsAstroException", new JSONObject());
    }

    @Test
    public void testAddPluginToInstanceList() {
        TestPlugin plugin = new TestPlugin(getActivity(), pluginManager, mockEventManager, mockMessageSender);
        pluginManager.addPluginToInstanceList(plugin);

        HashMap<String, AstroPlugin> instances = pluginManager.getPluginInstances();

        AstroPlugin retrievedPlugin = instances.get(plugin.getInstanceAddress());
        assertSame(plugin, retrievedPlugin);
    }

    @Test
    public void testInstanceForAddress() {
        AstroPlugin testPlugin = new TestPlugin(getActivity(), pluginManager, mockEventManager, mockMessageSender);

        String address = testPlugin.getInstanceAddress();

        pluginManager.addPluginToInstanceList(testPlugin);
        AstroPlugin retrievedPlugin = pluginManager.instanceForAddress(address);

        assertSame(testPlugin, retrievedPlugin);
    }

    // TODO: Make this test assert something
    @Test
    public void testOnMessage() throws JSONException {
        final String expectedString = "{\"address\":\"TestPlugin:";

        SenderMessage message = new SenderMessage() {};

        String messageString = "{\"method\":\"createPlugin\",\"pluginName\":\"TestPlugin\"}";
        JSONObject messageJson = new JSONObject(messageString);
        message.setPayload(messageJson);

        try {
            pluginManager.onMessage("address", message);
        } catch(Exception e) {
            fail();
        }
    }
}
