package productcatalog.services;

import io.sphere.sdk.categories.Category;
import io.sphere.sdk.client.PlayJavaSphereClient;
import io.sphere.sdk.products.ProductProjection;
import io.sphere.sdk.products.ProductVariant;
import io.sphere.sdk.products.queries.ProductProjectionQuery;
import io.sphere.sdk.products.search.ProductProjectionSearch;
import io.sphere.sdk.queries.PagedQueryResult;
import io.sphere.sdk.queries.PagedResult;
import play.libs.F;

import javax.inject.Inject;
import java.util.*;

public class ProductProjectionService {

    private final PlayJavaSphereClient sphere;

    @Inject
    public ProductProjectionService(final PlayJavaSphereClient sphere) {
        this.sphere = sphere;
    }

    public F.Promise<List<ProductProjection>> searchProducts(final int page, final int pageSize) {
        final int offset = (page - 1) * pageSize;
        return sphere.execute(ProductProjectionSearch.ofCurrent()
                .withOffset(offset)
                .withLimit(pageSize))
                .map(PagedResult::getResults);
    }

    public F.Promise<Optional<ProductProjection>> searchProductBySlug(final Locale locale, final String slug) {
        return sphere.execute(ProductProjectionQuery.ofCurrent().bySlug(locale, slug))
                .map(PagedQueryResult::head);
    }

    public Optional<ProductVariant> findVariantBySku(final ProductProjection product, final String sku) {
        return product.getAllVariants().stream()
                .filter(variant -> variantHasSku(variant, sku))
                .findFirst();
    }

    public F.Promise<List<ProductProjection>> getSuggestions(final List<Category> categories, final int numSuggestions) {
        final ProductProjectionQuery productProjectionQuery = ProductProjectionQuery.ofCurrent()
                .withPredicate(p -> p.categories().isIn(categories));

        return sphere.execute(productProjectionQuery)
                .map(PagedQueryResult::getResults)
                .map(results -> pickNRandom(results, numSuggestions));
    }

    private boolean variantHasSku(final ProductVariant variant, final String sku) {
        return variant.getSku().map(variantSku -> variantSku.equals(sku)).orElse(false);
    }

    private <T> List<T> pickNRandom(final List<T> elements, final int n) {
        if(elements.size() < n) {
            return pickNRandom(elements, elements.size());
        }

        final List<T> picked = new ArrayList<>();
        final Random random = new Random();

        for(int i = 0; i < n; i++) {
            final int index = random.nextInt(elements.size());
            picked.add(elements.get(index));
            elements.remove(index);
        }

        return picked;
    }
}

@@ -584,16 +584,71 @@
  sphere;
+%0A    private static final Random RANDOM = new Random();
 %0A%0A    @I
@@ -2338,24 +2338,176 @@
 al int n) %7B%0A
+        return pickNRandom(RANDOM, elements, n);%0A    %7D%0A%0A    private %3CT%3E List%3CT%3E pickNRandom(final Random random, final List%3CT%3E elements, final int n) %7B%0A
         if(e
@@ -2649,56 +2649,12 @@
 t%3C%3E(
-);%0A        final Random random = new Random(
+n
 );%0A
-%0A
     

Split pickNRandom and put Random to class level