package com.triangleleft.flashcards.ui.cards;

import com.triangleleft.flashcards.Calls;
import com.triangleleft.flashcards.Observer;
import com.triangleleft.flashcards.service.cards.FlashcardTestData;
import com.triangleleft.flashcards.service.cards.FlashcardTestResult;
import com.triangleleft.flashcards.service.cards.FlashcardWord;
import com.triangleleft.flashcards.service.cards.FlashcardWordResult;
import com.triangleleft.flashcards.service.cards.FlashcardsModule;
import com.triangleleft.flashcards.service.common.exception.ServerException;
import com.triangleleft.flashcards.util.TestUtils;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.mockito.Mockito.*;

@RunWith(JUnit4.class)
public class FlashcardsPresenterTest {

    private static final String UI_LANG = "ui";
    private static final String LEARN_LANG = "learn";

    @Mock
    FlashcardsModule module;
    @Captor
    ArgumentCaptor<List<FlashcardWord>> listCaptor;
    @Captor
    ArgumentCaptor<Observer<FlashcardTestData>> observerCaptor;

    private IFlashcardsView view = TestUtils.mockViewForClass(IFlashcardsView.class);
    private FlashcardsPresenter presenter;

    @Before
    public void before() {
        MockitoAnnotations.initMocks(this);
        presenter = new FlashcardsPresenter(module);
    }

    @Test
    public void onLoadFlashcardsWouldStartLoadingFlashcards() {
        when(module.getFlashcards()).thenReturn(Calls.empty());

        presenter.onLoadFlashcards();

        verify(module).getFlashcards();
    }

//    @Test
//    public void onDestroyWouldUnsubscribe() {
//        AtomicBoolean unsubscribed = new AtomicBoolean(false);
//        // Create empty observable to notify us when it's unsubscribed from
//        Observable<FlashcardTestData> observable = Observable.empty();
//        observable = observable.doOnUnsubscribe(() -> unsubscribed.set(true));
//        when(module.getFlashcards()).thenReturn(observable);
//
//        // Simulate list load
//        presenter.onLoadFlashcards();
//        presenter.onDestroy();
//
//        assertTrue(unsubscribed.get());
//    }

    @Test
    public void onCreateWouldStartLoadingFlashcards() {
        when(module.getFlashcards()).thenReturn(Calls.empty());

        presenter.onCreate();
        presenter.onBind(view);

        verify(module).getFlashcards();
        verify(view).showProgress();
    }

    @Test
    public void onCreateWouldShowList() {
        List<FlashcardWord> list = Collections.singletonList(mock(FlashcardWord.class));
        prepareTestData(list);
        presenter.onCreate();
        presenter.onBind(view);

        verify(view).showWords(list);
    }

    @Test
    public void onFlashcardsLoadErrorWouldShowError() {
        when(module.getFlashcards()).thenReturn(Calls.error(new ServerException()));

        presenter.onCreate();
        presenter.onBind(view);

        verify(view).showError();
    }

    @Test
    public void emptyWordsListIsTreatedAsError() {
        prepareTestData(Collections.emptyList());

        presenter.onCreate();
        presenter.onBind(view);

        verify(view).showError();
    }

    @Test
    public void resultsArePosted() {
        FlashcardWord rightWord = mock(FlashcardWord.class);
        FlashcardWord wrongWord = mock(FlashcardWord.class);
        prepareTestData(Arrays.asList(rightWord, wrongWord));

        presenter.onCreate();
        presenter.onBind(view);
        presenter.onWordRight(rightWord);
        presenter.onWordWrong(wrongWord);
        presenter.onCardsDepleted();

        ArgumentCaptor<FlashcardTestResult> resultCaptor = ArgumentCaptor.forClass(FlashcardTestResult.class);
        verify(module).postResult(resultCaptor.capture());
        FlashcardTestResult result = resultCaptor.getValue();
        assertThat(result.getLearningLanguage(), equalTo(LEARN_LANG));
        assertThat(result.getUiLanguage(), equalTo(UI_LANG));

        FlashcardWordResult rightResult = FlashcardWordResult.create(rightWord, true);
        FlashcardWordResult wrongResult = FlashcardWordResult.create(wrongWord, false);
        assertThat(result.getWordResults(), containsInAnyOrder(rightResult, wrongResult));
    }

    @Test
    public void resultWithErrors() {
        FlashcardWord wrongWord = mock(FlashcardWord.class);
        prepareTestData(Collections.singletonList(wrongWord));

        presenter.onCreate();
        presenter.onBind(view);
        presenter.onWordWrong(wrongWord);
        presenter.onCardsDepleted();

        verify(view).showResultErrors(listCaptor.capture());
        List<FlashcardWord> list = listCaptor.getValue();
        assertThat(list, containsInAnyOrder(wrongWord));
    }

    @Test
    public void resultWithNoErrors() {
        prepareTestData(Collections.singletonList(mock(FlashcardWord.class)));

        presenter.onCreate();
        presenter.onBind(view);
        presenter.onCardsDepleted();

        verify(view).showResultsNoErrors();
    }

    private void prepareTestData(List<FlashcardWord> words) {
        FlashcardTestData data = mock(FlashcardTestData.class);
        when(data.getUiLanguage()).thenReturn(UI_LANG);
        when(data.getLearningLanguage()).thenReturn(LEARN_LANG);
        when(data.getWords()).thenReturn(words);
        when(module.getFlashcards()).thenReturn(Calls.just(data));
    }
}
@@ -36,24 +36,65 @@
 .ui.cards;%0A%0A
+import com.triangleleft.flashcards.Call;%0A
 import com.t
@@ -123,16 +123,16 @@
 .Calls;%0A
-
 import c
@@ -954,16 +954,66 @@
 il.List;
+%0Aimport java.util.concurrent.atomic.AtomicBoolean;
 %0A%0Aimport
@@ -1407,16 +1407,64 @@
 module;%0A
+    @Mock%0A    Call%3CFlashcardTestData%3E mockCall;%0A
     @Cap
@@ -2094,18 +2094,16 @@
 %0A    %7D%0A%0A
-//
     @Tes
@@ -2104,18 +2104,16 @@
   @Test%0A
-//
     publ
@@ -2138,26 +2138,19 @@
 ould
-Unsubscribe
+Cancel
 () %7B%0A
-//
     
@@ -2208,26 +2208,24 @@
 false);%0A
-//
         
 // Creat
@@ -2220,232 +2220,130 @@
     
-// Create empty observable to notify us when it's unsubscribed from%0A//        Observable%3CFlashcardTestData%3E observable = Observable.empty();%0A//        observable = observable.doOnUnsubscribe(() -%3E unsubscribed.set(true)
+doAnswer(invocation -%3E %7B%0A            unsubscribed.set(true);%0A            return true;%0A        %7D).when(mockCall).cancel(
 );%0A
-//
     
@@ -2390,26 +2390,20 @@
 urn(
-observable);%0A//%0A//
+mockCall);%0A%0A
     
@@ -2428,18 +2428,16 @@
 st load%0A
-//
         
@@ -2466,18 +2466,16 @@
 ards();%0A
-//
         
@@ -2501,13 +2501,9 @@
 ();%0A
-//%0A//
+%0A
     
@@ -2513,19 +2513,19 @@
  assertT
-rue
+hat
 (unsubsc
@@ -2539,13 +2539,26 @@
 et()
+, equalTo(true)
 );%0A
-//
     

Update onDestroy test.