package com.google.account.business.data;

import com.google.api.core.ApiFuture;
import com.google.cloud.firestore.*;
import com.google.common.collect.ImmutableList;
import com.google.utils.FireStoreUtils;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import static org.junit.Assert.assertEquals;

/** Tests for {@link BusinessDatabase} class. */
public final class BusinessDatabaseTest {
    // TODO(issue/15): Add failure test case

    private static final String TEST_BUSINESS_COLLECTION = "BusinessAccounts";
    private static final int BATCH_SIZE = 10;

    static BusinessDatabase businessDatabase;
    static Firestore firestore;

    @BeforeClass
    public static void setUp() throws IOException {
        businessDatabase = new BusinessDatabase();
        firestore = FireStoreUtils.getFireStore();
    }

    @Before
    public void clearCollection() {
        try {
            deleteCollection(firestore.collection(TEST_BUSINESS_COLLECTION), BATCH_SIZE);
        } catch (ExecutionException | InterruptedException e) {
            System.err.println("Error deleting collection : " + e.getMessage());
        }
    }

    @AfterClass
    public static void tearDownCollection() {
        try {
            deleteCollection(firestore.collection(TEST_BUSINESS_COLLECTION), BATCH_SIZE);
        } catch (ExecutionException | InterruptedException e) {
            System.err.println("Error tearing down collection : " + e.getMessage());
        }
    }

    @Test
    public void updateBusinessAccount_normalInput_success()
            throws IOException, ExecutionException, InterruptedException {
        // Arrange.
        String uid = "dummyBusinessUid";

        String businessName = "testBusinessAccount";
        List<String> jobs = ImmutableList.of("jobId1", "jobId2");
        Business expectedBusiness = Business.newBuilder().setName(businessName).setJobs(jobs).build();

        // Act.
        Future<WriteResult> future = this.businessDatabase.updateBusinessAccount(uid, expectedBusiness);

        // Assert.

        // future.get() blocks on response.
        future.get();

        DocumentReference documentReference = FireStoreUtils.getFireStore()
                .collection(TEST_BUSINESS_COLLECTION).document(uid);
        DocumentSnapshot documentSnapshot = documentReference.get().get();

        Business actualBusiness = documentSnapshot.toObject(Business.class);

        assertEquals(expectedBusiness, actualBusiness);
    }

    /**
     * Delete a collection in batches to avoid out-of-memory errors.
     *
     * Batch size may be tuned based on document size (atmost 1MB) and application requirements.
     */
    private static void deleteCollection(CollectionReference collection, int batchSize)
            throws ExecutionException, InterruptedException {
        // retrieve a small batch of documents to avoid out-of-memory errors
        ApiFuture<QuerySnapshot> future = collection.limit(batchSize).get();
        int deleted = 0;
        // future.get() blocks on document retrieval
        List<QueryDocumentSnapshot> documents = future.get().getDocuments();
        for (QueryDocumentSnapshot document : documents) {
            document.getReference().delete();
            ++deleted;
        }
        if (deleted >= batchSize) {
            // retrieve and delete another batch
            deleteCollection(collection, batchSize);
        }
    }
}

@@ -307,16 +307,53 @@
 .Test;%0A%0A
+import javax.swing.text.html.Option;%0A
 import j
@@ -391,24 +391,51 @@
 .util.List;%0A
+import java.util.Optional;%0A
 import java.
@@ -551,16 +551,59 @@
 tEquals;
+%0Aimport static org.junit.Assert.assertTrue;
 %0A%0A/** Te
@@ -1756,24 +1756,1210 @@
      %7D%0A    %7D
+%0A    %0A    @Test%0A    public void getBusinessAccount_normalInput_success() throws IOException, ExecutionException, InterruptedException %7B%0A        // Arrange.%0A        String uid = %22dummyBusinessUid%22;%0A%0A        String businessName = %22testBusinessAccount%22;%0A        List%3CString%3E jobs = ImmutableList.of(%22jobId1%22, %22jobId2%22);%0A        Business expectedBusiness = Business.newBuilder().setName(businessName).setJobs(jobs).build();%0A%0A        Future%3CDocumentReference%3E addedJobFuture = FireStoreUtils.getFireStore()%0A                .collection(TEST_BUSINESS_COLLECTION)%0A                .add(expectedBusiness);%0A%0A        DocumentReference documentReference = addedJobFuture.get();%0A        // Asynchronously retrieve the document.%0A        ApiFuture%3CDocumentSnapshot%3E future = documentReference.get();%0A%0A        // future.get() blocks on response.%0A        DocumentSnapshot document = future.get();%0A%0A        // Act.%0A        Optional%3CBusiness%3E businessOptional = this.businessDatabase.getBusinessAccount(uid).get();%0A%0A        // Assert.%0A        assertTrue(businessOptional.isPresent());%0A%0A        Business actualBusiness = businessOptional.get();%0A%0A        assertEquals(expectedBusiness, actualBusiness);%0A    %7D
 %0A%0A    @Test%0A

Add test for getBusinessAccount