package com.avg.zaap.crashlibrary.core;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;

import com.avg.zaap.crashlibrary.CrashReportingService;
import com.avg.zaap.crashlibrary.ICrashServiceCommunicator;

import org.junit.Before;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

import static junit.framework.Assert.assertEquals;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

/**
 * Unit tests for {@link ServiceConnectionHelper}.
 * <p>
 * Created by marek.osvald on 18. 5. 2016.
 */
public class ServiceConnectionHelperTests {
    private Intent mIntent;
    private Context mContext;
    private Thread mThread;
    private ServiceConnectionHelper mServiceConnectionHelper;
    private boolean mConnectionHappened;
    private ServiceConnection mServiceConnection;
    private ICrashReportingServiceCallback mCallback;

    @Before
    public void setUp() throws Exception {
        mConnectionHappened = false;
        mCallback = mock(ICrashReportingServiceCallback.class);

        mIntent = mock(Intent.class);
        mContext = mock(Context.class);
        mThread = mock(Thread.class);

        IServiceConnectionHelperFactory factory = new IServiceConnectionHelperFactory() {

            @Override
            public Intent getIntent(Context context, Class clazz) {
                assertThat(context, is(mContext));
                assertEquals(clazz, CrashReportingService.class);
                return mIntent;
            }

            @Override
            public Thread getThread(Runnable runnable) {
                assertThat(runnable, not(nullValue()));
                runnable.run();
                return mThread;
            }
        };

        mServiceConnectionHelper = new ServiceConnectionHelper(mCallback, factory);
    }

    @Test
    public void runServiceConnectionThread_serviceNotConnected_onServiceConnectedNotCalled() {
        //noinspection ResourceType
        doAnswer(new Answer() {
            @Override
            public Object answer(InvocationOnMock invocation) throws Throwable {
                Intent intent = (Intent) invocation.getArguments()[0];
                mServiceConnection = (ServiceConnection) invocation.getArguments()[1];
                int flags = (int) invocation.getArguments()[2];
                mConnectionHappened = true;

                assertThat(intent, is(mIntent));
                assertThat(flags, is(Context.BIND_AUTO_CREATE));

                return null;
            }
        }).when(mContext).bindService((Intent) anyObject(), (ServiceConnection) anyObject(), anyInt());

        //test
        mServiceConnectionHelper.startServiceConnectionThread(mContext);
        mServiceConnection.onServiceDisconnected(mock(ComponentName.class));

        //verify
        assertTrue(mConnectionHappened);
        verify(mCallback, times(0)).onServiceConnected((ICrashServiceCommunicator) anyObject());
    }

    @Test
    public void runServiceConnectionThread_serviceConnected_onServiceConnectedCalled() {
        //noinspection ResourceType
        doAnswer(new Answer() {
            @Override
            public Object answer(InvocationOnMock invocation) throws Throwable {
                Intent intent = (Intent) invocation.getArguments()[0];
                mServiceConnection = (ServiceConnection) invocation.getArguments()[1];
                int flags = (int) invocation.getArguments()[2];
                mConnectionHappened = true;

                assertThat(intent, is(mIntent));
                assertThat(flags, is(Context.BIND_AUTO_CREATE));

                return null;
            }
        }).when(mContext).bindService((Intent) anyObject(), (ServiceConnection) anyObject(), anyInt());

        //test
        mServiceConnectionHelper.startServiceConnectionThread(mContext);
        mServiceConnection.onServiceConnected(mock(ComponentName.class), mock(IBinder.class));

        //verify
        assertTrue(mConnectionHappened);
        verify(mCallback, times(1)).onServiceConnected((ICrashServiceCommunicator) anyObject());
    }
}