// Path: src/main/java/shopJava/util/Constants.java
// public interface Constants {
// 
//     int MAX_ORDERS_PER_USER = 10;
// 
//     String MONGODB_URI = "mongodb://localhost:27017";
//     String SHOP_DB_NAME = "shop";
//     String USERS_COLLECTION_NAME = "users";
//     String ORDERS_COLLECTION_NAME = "orders";
// 
//     String HOMER = "homer";
//     String MARGE = "marge";
//     String BART = "bart";
//     String LISA = "lisa";
//     String MAGGIE = "maggie";
// 
//     String[] SIMPSONS = {HOMER, MARGE, BART, LISA, MAGGIE};
// }
// 
// Path: src/main/java/shopJava/util/Util.java
// public static int average(final int totalAmount, final int orderCount) {
//     return orderCount == 0 ? 0 : Math.round((100.0f * totalAmount / orderCount) / 100);
// }
// 
// Path: src/main/java/shopJava/util/Util.java
// public static User checkUserLoggedIn(final Optional<User> optUser, final Credentials credentials) {
// 
//     if (!optUser.isPresent()) {     // replaces if (user != null)
//         throw new RuntimeException(new IllegalAccessException("User unknown: " + credentials.username));
//     }
//     final User user = optUser.get();
//     return checkUserLoggedIn(user, credentials);
// }


import com.mongodb.async.SingleResultCallback;
import com.mongodb.async.client.MongoClient;
import com.mongodb.async.client.MongoClients;
import com.mongodb.async.client.MongoCollection;
import com.mongodb.async.client.MongoDatabase;
import org.bson.Document;
import shopJava.model.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.*;
import static com.mongodb.client.model.Filters.eq;
import static java.lang.Thread.sleep;
import static shopJava.util.Constants.*;
import static shopJava.util.Util.average;
import static shopJava.util.Util.checkUserLoggedIn;
    }   // end DAO


    private CompletionStage<String> logIn(final Credentials credentials) {
        return dao.findUserByName(credentials.username)
                .thenApply(optUser -> checkUserLoggedIn(optUser, credentials))
                .thenApply(user -> user.name);
    }

    private CompletionStage<Result> processOrdersOf(final String username) {
        return dao.findOrdersByUsername(username)
                .thenApply(orders -> {
                    final IntPair pair =
                            orders.stream()
                                    .map(order -> new IntPair(order.amount, 1))
                                    .reduce(new IntPair(0, 0), (p1, p2) -> new IntPair(p1.first + p2.first, p1.second + p2.second));
                    return new  Result(username, pair.second, pair.first);
                });
    }

    private void eCommerceStatistics(final Credentials credentials) throws Exception {

        System.out.println("--- Calculating eCommerce statistics for user \"" + credentials.username + "\" ...");

        final CountDownLatch latch = new CountDownLatch(1);

        logIn(credentials)
                .thenCompose(username -> processOrdersOf(username))     // flatMap of CompletionStage
                .whenComplete((result, t) -> {
                    if (t == null) {
// Path: src/main/java/shopJava/util/Constants.java
// public interface Constants {
// 
//     int MAX_ORDERS_PER_USER = 10;
// 
//     String MONGODB_URI = "mongodb://localhost:27017";
//     String SHOP_DB_NAME = "shop";
//     String USERS_COLLECTION_NAME = "users";
//     String ORDERS_COLLECTION_NAME = "orders";
// 
//     String HOMER = "homer";
//     String MARGE = "marge";
//     String BART = "bart";
//     String LISA = "lisa";
//     String MAGGIE = "maggie";
// 
//     String[] SIMPSONS = {HOMER, MARGE, BART, LISA, MAGGIE};
// }
// 
// Path: src/main/java/shopJava/util/Util.java
// public static int average(final int totalAmount, final int orderCount) {
//     return orderCount == 0 ? 0 : Math.round((100.0f * totalAmount / orderCount) / 100);
// }
// 
// Path: src/main/java/shopJava/util/Util.java
// public static User checkUserLoggedIn(final Optional<User> optUser, final Credentials credentials) {
// 
//     if (!optUser.isPresent()) {     // replaces if (user != null)
//         throw new RuntimeException(new IllegalAccessException("User unknown: " + credentials.username));
//     }
//     final User user = optUser.get();
//     return checkUserLoggedIn(user, credentials);
// }


// Path: src/main/java/shopJava/queries/QueryJ06bCompletionStageCompleteCallbackRefactored.java
import com.mongodb.async.SingleResultCallback;
import com.mongodb.async.client.MongoClient;
import com.mongodb.async.client.MongoClients;
import com.mongodb.async.client.MongoCollection;
import com.mongodb.async.client.MongoDatabase;
import org.bson.Document;
import shopJava.model.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.*;
import static com.mongodb.client.model.Filters.eq;
import static java.lang.Thread.sleep;
import static shopJava.util.Constants.*;
import static shopJava.util.Util.average;
import static shopJava.util.Util.checkUserLoggedIn;

    }   // end DAO


    private CompletionStage<String> logIn(final Credentials credentials) {
        return dao.findUserByName(credentials.username)
                .thenApply(optUser -> checkUserLoggedIn(optUser, credentials))
                .thenApply(user -> user.name);
    }

    private CompletionStage<Result> processOrdersOf(final String username) {
        return dao.findOrdersByUsername(username)
                .thenApply(orders -> {
                    final IntPair pair =
                            orders.stream()
                                    .map(order -> new IntPair(order.amount, 1))
                                    .reduce(new IntPair(0, 0), (p1, p2) -> new IntPair(p1.first + p2.first, p1.second + p2.second));
                    return new  Result(username, pair.second, pair.first);
                });
    }

    private void eCommerceStatistics(final Credentials credentials) throws Exception {

        System.out.println("--- Calculating eCommerce statistics for user \"" + credentials.username + "\" ...");

        final CountDownLatch latch = new CountDownLatch(1);

        logIn(credentials)
                .thenCompose(username -> processOrdersOf(username))     // flatMap of CompletionStage
                .whenComplete((result, t) -> {
                    if (t == null) {
                        result.display();