package com.google.step.coffee.data;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.google.api.services.calendar.Calendar;
import com.google.api.services.calendar.Calendar.Events;
import com.google.api.services.calendar.Calendar.Events.Insert;
import com.google.api.services.calendar.model.Event;
import com.google.appengine.api.users.User;
import com.google.step.coffee.TestHelper;
import com.google.step.coffee.entity.TimeSlot;
import java.io.IOException;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
import org.junit.Before;
import org.junit.Test;

public class CalendarUtilsTest extends TestHelper {

  private final TimeSlot timeslot = new TimeSlot(ZonedDateTime.now(), Duration.ofMinutes(30));
  private final List<String> participantIds = new ArrayList<>();
  private final List<String> commonTags = new ArrayList<>();
  private final String testUser1Id = "1111";
  private final String testUser2Id = "2222";
  private final String testUser3Id = "3333";

  @Before
  public void setParticipantIds() {
    participantIds.add(testUser1Id);
    participantIds.add(testUser2Id);
    participantIds.add(testUser3Id);
  }

  @Before
  public void setCommonTags() {
    commonTags.add("Football");
    commonTags.add("Photography");
  }

  @Test
  public void eventDescriptionContainsCommonTags() {
    addTestUsersInfo();

    Event event = CalendarUtils.createEvent(timeslot, participantIds, commonTags);

    assertThat(event.getDescription(), containsString("Football"));
    assertThat(event.getDescription(), containsString("Photography"));
  }

  @Test
  public void addingEventAddsOnceOnSuccess() throws IOException {
    Event event = mock(Event.class);
    Calendar service = mock(Calendar.class);
    Events events = mock(Events.class);
    Insert insertReturn = mock(Insert.class);

    when(service.events()).thenReturn(events);

    when(events.insert("primary", event)).thenReturn(insertReturn);

    when(insertReturn.setConferenceDataVersion(1)).thenReturn(insertReturn);

    CalendarUtils.addEvent(service, testUser1Id, event, true);

    verify(events, times(1)).insert("primary", event);
    verify(insertReturn, times(1)).execute();
  }

  @Test
  public void addingAnEventRetriesIfFails() throws IOException {
    Event event = mock(Event.class);
    Calendar service = mock(Calendar.class);
    Events events = mock(Events.class);
    Insert insertReturn = mock(Insert.class);

    when(service.events()).thenReturn(events);

    when(events.insert("primary", event))
        .thenThrow(new IOException("failed"))
        .thenReturn(insertReturn);

    when(insertReturn.setConferenceDataVersion(1)).thenReturn(insertReturn);

    CalendarUtils.addEvent(service, testUser1Id, event, true);

    verify(events, times(2)).insert("primary", event);
    verify(insertReturn, times(1)).execute();
  }

  private void addTestUsersInfo() {
    User testUser1 = mock(User.class);
    User testUser2 = mock(User.class);
    User testUser3 = mock(User.class);

    when(testUser1.getUserId()).thenReturn(testUser1Id);
    when(testUser1.getEmail()).thenReturn("testUser1@example.com");
    when(testUser2.getUserId()).thenReturn(testUser2Id);
    when(testUser2.getEmail()).thenReturn("testUser2@example.com");
    when(testUser3.getUserId()).thenReturn(testUser3Id);
    when(testUser3.getEmail()).thenReturn("testUser3@example.com");

    UserStore userStore = new UserStore();
    userStore.addNewUser(testUser1);
    userStore.addNewUser(testUser2);
    userStore.addNewUser(testUser3);
  }
}

@@ -799,33 +799,8 @@
 st;%0A
-import org.junit.Before;%0A
 impo
@@ -876,229 +876,8 @@
  %7B%0A%0A
-  private final TimeSlot timeslot = new TimeSlot(ZonedDateTime.now(), Duration.ofMinutes(30));%0A  private final List%3CString%3E participantIds = new ArrayList%3C%3E();%0A  private final List%3CString%3E commonTags = new ArrayList%3C%3E();%0A
   pr
@@ -1007,30 +1007,28 @@
 %223333%22;%0A%0A  @
-Before
+Test
 %0A  public vo
@@ -1034,307 +1034,287 @@
 oid 
-setParticipantIds() %7B%0A    participantIds.add(testUser1Id);%0A    participantIds.add(testUser2Id);%0A    participantIds.add(testUser3Id);%0A  %7D%0A%0A  @Before%0A  public void setCommonTags() %7B%0A    commonTags.add(%22Football%22);%0A    commonTags.add(%22Photography%22
+eventDescriptionContainsCommonTags() %7B%0A    final TimeSlot timeslot = new TimeSlot(ZonedDateTime.now(), Duration.ofMinutes(30));%0A    final List%3CString%3E participantIds = new ArrayList%3C%3E();%0A    final List%3CString%3E commonTags = new ArrayList%3C%3E(
 );%0A
+%0A
   
-%7D%0A%0A  @Test%0A  public void eventDescriptionContainsC
+  setUpEventInfo(participantIds, c
 ommo
@@ -1314,28 +1314,26 @@
 , commonTags
-() %7B
+);
 %0A    addTest
@@ -2844,16 +2844,286 @@
 );%0A  %7D%0A%0A
+  private void setUpEventInfo(List%3CString%3E participantIds, List%3CString%3E commonTags) %7B%0A    participantIds.add(testUser1Id);%0A    participantIds.add(testUser2Id);%0A    participantIds.add(testUser3Id);%0A%0A    commonTags.add(%22Football%22);%0A    commonTags.add(%22Photography%22);%0A  %7D%0A%0A
   privat

Move test setup variables to corresponding test