package com.google.sps.data;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.Filter;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Query.FilterPredicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSet.Builder;
import com.google.sps.data.Book;
import com.google.sps.data.Review;
import com.google.sps.data.User;
import java.util.Set;

public class ReviewDaoDatastore implements ReviewDao {
  private DatastoreService datastore;

  public ReviewDaoDatastore() {
    datastore = DatastoreServiceFactory.getDatastoreService();
  }

  /**
   * Takes in a book and uploads all associated reviews
   * to Datastore with user set to default values
   *
   * @param book: Book object whose reviews are to be uploaded
   */
  @Override
  public void loadAll(Book book) {
    User defaultUser = User.create("unknown email", "GoodReads User");
    // loop through all the reviews of a book
    for (String review : book.reviews()) {
      Entity reviewEntity = new Entity("Review");
      reviewEntity.setProperty("Book", book);
      reviewEntity.setProperty("Content", review);
      reviewEntity.setProperty("User", defaultUser);
      datastore.put(reviewEntity);
    }
  }

  /**
   * Takes in a review and uploads to Datastore
   *
   * @param review: Review object to be uploaded
   */
  @Override
  public void loadNew(Review review) {
    Entity reviewEntity = new Entity("Review");
    reviewEntity.setProperty("Book", review.book());
    reviewEntity.setProperty("Content", review.fullText());
    reviewEntity.setProperty("User", review.user());
    datastore.put(reviewEntity);
  }

  /**
   * Takes in a book and returns a set of all Reviews associated
   * with that book in Datastore
   *
   * @param book: Book object whose reviews are to be returned
   * @return ImmutableSet of reviews associated with the book
   */
  @Override
  public ImmutableSet<Review> getAll(Book book) {
    ImmutableSet.Builder<Review> reviews = new ImmutableSet.Builder<Review>();

    Filter bookFilter = new FilterPredicate("Book", FilterOperator.EQUAL, book);
    Query query = new Query("Review").setFilter(bookFilter);
    PreparedQuery results = datastore.prepare(query);
    if (results == null) {
      return reviews.build();
    }
    for (Entity entity : results.asIterable()) {
      Review review = entityToReview(entity);
      reviews.add(review);
    }
    return reviews.build();
  }

  private static Review entityToReview(Entity reviewEntity) {
    return Review.builder()
        .fullText((String) reviewEntity.getProperty("Content"))
        .book((Book) reviewEntity.getProperty("Book"))
        .user((User) reviewEntity.getProperty("User"))
        .build();
  }
}

@@ -809,16 +809,238 @@
 tastore;
+%0A  private static final String ENTITY_KIND = %22Review%22;%0A  private static final String BOOK_PROPERTY = %22Book%22;%0A  private static final String CONTENT_PROPERTY = %22Content%22;%0A  private static final String USER_PROPERTY = %22User%22;
 %0A%0A  publ
@@ -1561,32 +1561,35 @@
  new Entity(
-%22Review%22
+ENTITY_KIND
 );%0A      rev
@@ -1610,22 +1610,29 @@
 roperty(
-%22Book%22
+BOOK_PROPERTY
 , book);
@@ -1659,33 +1659,40 @@
 setProperty(
-%22Content%22
+CONTENT_PROPERTY
 , review);%0A 
@@ -1721,22 +1721,29 @@
 roperty(
-%22User%22
+USER_PROPERTY
 , defaul
@@ -1998,24 +1998,27 @@
  Entity(
-%22Review%22
+ENTITY_KIND
 );%0A    r
@@ -2041,22 +2041,29 @@
 roperty(
-%22Book%22
+BOOK_PROPERTY
 , review
@@ -2101,25 +2101,32 @@
 roperty(
-%22Content%22
+CONTENT_PROPERTY
 , review
@@ -2168,22 +2168,29 @@
 roperty(
-%22User%22
+USER_PROPERTY
 , review
@@ -2663,22 +2663,29 @@
 edicate(
-%22Book%22
+BOOK_PROPERTY
 , Filter
@@ -2735,24 +2735,27 @@
 w Query(
-%22Review%22
+ENTITY_KIND
 ).setFil
@@ -3191,25 +3191,32 @@
 roperty(
-%22Content%22
+CONTENT_PROPERTY
 ))%0A     
@@ -3256,22 +3256,29 @@
 roperty(
-%22Book%22
+BOOK_PROPERTY
 ))%0A     
@@ -3318,22 +3318,29 @@
 roperty(
-%22User%22
+USER_PROPERTY
 ))%0A     

use private static final for string literals