package com.google.sps.servlets;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceConfig;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.EmbeddedEntity;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Transaction;
import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseToken;
import com.google.firebase.auth.UserRecord;
import com.google.sps.workspace.Workspace;
import com.google.sps.workspace.WorkspaceFactory;
import java.time.Clock;
import java.time.Duration;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

@RunWith(MockitoJUnitRunner.class)
public class NotifyStudentTest {

  private final LocalServiceTestHelper helper =
      new LocalServiceTestHelper(
          // Use High Rep job policy to allow cross group transactions in tests.
          new LocalDatastoreServiceTestConfig().setApplyAllHighRepJobPolicy());

  private DatastoreService datastore;

  @Mock HttpServletRequest httpRequest;

  @Mock HttpServletResponse httpResponse;

  @Mock FirebaseAuth authInstance;

  @Mock WorkspaceFactory factory;

  @Mock Workspace workspace;

  @Mock Clock clock;

  @InjectMocks NotifyStudent alertStudent;

  private Clock fixedClock;
  private static final LocalDate LOCAL_DATE = LocalDate.of(2020, 07, 07);
  private static final Date START_DATE =
      Date.from(LocalDate.of(2020, 07, 06).atStartOfDay(ZoneId.systemDefault()).toInstant());

  @Before
  public void setUp() {
    helper.setUp();
    datastore = DatastoreServiceFactory.getDatastoreService();
    System.setProperty(
        DatastoreServiceConfig.DATASTORE_EMPTY_LIST_SUPPORT, Boolean.TRUE.toString());

    fixedClock =
        Clock.fixed(
            LOCAL_DATE.atStartOfDay(ZoneId.systemDefault()).toInstant(), ZoneId.systemDefault());
    doReturn(fixedClock.getZone()).when(clock).getZone();
  }

  @After
  public void tearDown() {
    // Clean up any dangling transactions.
    Transaction txn = datastore.getCurrentTransaction(null);
    if (txn != null && txn.isActive()) {
      txn.rollback();
    }

    helper.tearDown();
  }

  @Test
  public void takeOff() throws Exception {
    Entity init = new Entity("Class");

    init.setProperty("owner", "ownerID");
    init.setProperty("name", "testClass");

    EmbeddedEntity addQueue1 = new EmbeddedEntity();
    EmbeddedEntity studentInfo1 = new EmbeddedEntity();
    studentInfo1.setProperty("timeEntered", START_DATE);
    addQueue1.setProperty("studentID", studentInfo1);

    EmbeddedEntity addQueue2 = new EmbeddedEntity();
    EmbeddedEntity studentInfo2 = new EmbeddedEntity();
    studentInfo2.setProperty("timeEntered", START_DATE);
    addQueue2.setProperty("test2", studentInfo2);

    init.setProperty("studentQueue", Arrays.asList(addQueue1, addQueue2));

    EmbeddedEntity queueInfo = new EmbeddedEntity();
    queueInfo.setProperty("taID", "taID");
    queueInfo.setProperty("workspaceID", "workspaceID");

    EmbeddedEntity beingHelped = new EmbeddedEntity();
    init.setProperty("beingHelped", beingHelped);

    datastore.put(init);

    when(httpRequest.getParameter("taToken")).thenReturn("testID");
    FirebaseToken mockToken = mock(FirebaseToken.class);
    when(authInstance.verifyIdToken("testID")).thenReturn(mockToken);
    when(mockToken.getUid()).thenReturn("taID");

    when(httpRequest.getParameter("studentEmail")).thenReturn("test@google.com");

    when(httpRequest.getParameter("classCode")).thenReturn(KeyFactory.keyToString(init.getKey()));

    UserRecord mockUser = mock(UserRecord.class);
    when(authInstance.getUserByEmail("test@google.com")).thenReturn(mockUser);
    when(mockUser.getUid()).thenReturn("studentID");
    doReturn(fixedClock.instant()).when(clock).instant();

    when(factory.fromStudentAndTA(KeyFactory.keyToString(init.getKey()), "studentID", "taID"))
        .thenReturn(workspace);
    when(workspace.getWorkspaceID()).thenReturn("workspaceID");

    alertStudent.doPost(httpRequest, httpResponse);

    Entity testClassEntity = datastore.prepare(new Query("Class")).asSingleEntity();

    EmbeddedEntity got = (EmbeddedEntity) testClassEntity.getProperty("beingHelped");
    EmbeddedEntity gotQueue = (EmbeddedEntity) got.getProperty("studentID");

    assertThat((String) gotQueue.getProperty("taID")).named("got.taID").isEqualTo("taID");
    assertThat((String) gotQueue.getProperty("workspaceID"))
        .named("got.workspaceID")
        .isEqualTo("workspaceID");

    ArrayList<EmbeddedEntity> testQueue =
        (ArrayList<EmbeddedEntity>) testClassEntity.getProperty("studentQueue");
    assertEquals(
        KeyFactory.keyToString(init.getKey()), KeyFactory.keyToString(testClassEntity.getKey()));
    assertEquals(1, testQueue.size());
    assertTrue(testQueue.get(0).hasProperty("test2"));

    Entity testWaitEntity = datastore.prepare(new Query("Wait")).asSingleEntity();
    ArrayList<Long> waitDurations = (ArrayList<Long>) testWaitEntity.getProperty("waitDurations");
    assertEquals((long) Duration.ofHours(24).getSeconds(), (long) waitDurations.get(0));
  }
}

@@ -3189,16 +3189,59 @@
 ption %7B%0A
+    String WORKSPACE_ID = %22WORKSPACE_ID%22;%0A%0A
     Enti
@@ -3267,24 +3267,24 @@
 y(%22Class%22);%0A
-
 %0A    init.se
@@ -3516,32 +3516,91 @@
 %22, START_DATE);%0A
+    studentInfo1.setProperty(%22workspaceID%22, WORKSPACE_ID);%0A
     addQueue1.se
@@ -3800,24 +3800,83 @@
 TART_DATE);%0A
+    studentInfo2.setProperty(%22workspaceID%22, WORKSPACE_ID);%0A
     addQueue
@@ -3994,162 +3994,8 @@
 );%0A%0A
-    EmbeddedEntity queueInfo = new EmbeddedEntity();%0A    queueInfo.setProperty(%22taID%22, %22taID%22);%0A    queueInfo.setProperty(%22workspaceID%22, %22workspaceID%22);%0A%0A
     
@@ -4816,90 +4816,34 @@
 from
-StudentAndTA(KeyFactory.keyToString(init.getKey()), %22studentID%22, %22ta
+WorkspaceID(WORKSPACE_
 ID
-%22
 ))
-%0A        
 .the
@@ -4910,29 +4910,28 @@
 nReturn(
-%22workspace
+WORKSPACE_
 ID
-%22
 );%0A%0A    
@@ -5434,29 +5434,28 @@
 EqualTo(
-%22workspace
+WORKSPACE_
 ID
-%22
 );%0A%0A    

Fix NotifyStudentTest