package com.mobify.astro.messaging;

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

import java.util.ArrayList;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;

public class EventManagerTest {

    // This needs to be public for testTriggerStar() to pass
    @SuppressWarnings("WeakerAccess")
    public class DummyJSONCallback implements JSONCallback {
        @Override
        public void run(JSONObject data) {
        }
    }

    private EventManager eventManager;

    @Before
    public void setUp() {
        eventManager = new EventManager();
    }

    /**
     * Returns a new anonymous JSONCallback instance with a do-nothing run method implementation.
     */
    private static JSONCallback createJSONCallback() {
        return new JSONCallback() {
            @Override
            public void run(JSONObject data) { }
        };
    }

    @Test
    public void testRegisterAndTriggerEvent() throws Exception {
        final JSONObject testData = new JSONObject("{\"test\": \"test\"}");

        eventManager.on("test", new JSONCallback() {
            @Override
            public void run(JSONObject data) {
                assertEquals(testData, data);
            }
        });
        boolean result = eventManager.trigger("test", testData);
        assertEquals(result, true);
    }

    @Test
    public void testTriggerStar() throws Exception {
        JSONCallback dummyHandler = new DummyJSONCallback();
        JSONObject data = new JSONObject();
        JSONCallback handlerSpy = spy(dummyHandler);

        eventManager.on("*", handlerSpy);
        eventManager.trigger("foo", data);

        verify(handlerSpy).run(data);
    }

    @Test
    public void testOn() throws Exception {
        JSONCallback handler = createJSONCallback();

        eventManager.on("foo", handler);

        assertEquals(eventManager.events.get("foo").get(0), handler);
    }

    @Test
    public void testRemoveHandler() {
        String eventName = "foo";
        JSONCallback handler = createJSONCallback();

        eventManager.on(eventName, handler);
        // verify handler was added
        assertEquals(eventManager.events.get(eventName).get(0), handler);

        eventManager.removeHandlers(eventName);
        // event is gone from events map
        assertEquals(eventManager.events.get(eventName), null);
        // repeat the call and assert for the empty handlers case
        assertFalse(eventManager.removeHandlers(eventName));
    }

    @Test
    public void testRemoveAllHandlers() {
        String[] eventNames = {"foo", "bar"};
        ArrayList<JSONCallback> callbacks = new ArrayList<>();
        callbacks.add(createJSONCallback());
        callbacks.add(createJSONCallback());

        eventManager.on(eventNames[0], callbacks.get(0));
        eventManager.on(eventNames[1], callbacks.get(1));

        assertEquals(eventManager.events.size(), 2);

        eventManager.removeAllHandlers();

        assertEquals(eventManager.events.get(eventNames[0]), null);
        assertEquals(eventManager.events.size(), 0);
    }

    @Test
    public void testThrowingHandler() {
        JSONCallback handler = new JSONCallback() {
            @Override
            public void run(JSONObject data) {
                throw new RuntimeException("just for funsies");
            }
        };
        String eventName = "foo";
        eventManager.on(eventName, handler);
        // should not throw
        eventManager.trigger(eventName, new JSONObject());
    }
}
