/**
 * Ozwillo Kernel
 * Copyright (C) 2015  The Ozwillo Kernel Authors
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package oasis.web.applications;

import java.net.URI;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Set;

import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;

import com.google.common.base.Strings;
import com.ibm.icu.util.ULocale;

import oasis.model.accounts.AccountRepository;
import oasis.model.accounts.UserAccount;
import oasis.model.applications.v2.CatalogEntry;
import oasis.model.applications.v2.CatalogEntryRepository;
import oasis.model.applications.v2.ImmutableCatalogEntryRepository;
import oasis.model.applications.v2.SimpleCatalogEntry;
import oasis.web.authn.OAuth;
import oasis.web.authn.OAuthPrincipal;

@Path("/m/search")
@OAuth
@Produces(MediaType.APPLICATION_JSON)
public class MarketSearchEndpoint {
  @Inject CatalogEntryRepository catalogEntryRepository;
  @Inject AccountRepository accountRepository;

  @Context SecurityContext context;

  @GET
  public Response get(
      @Nullable @QueryParam("hl") ULocale locale,
      @DefaultValue("0") @QueryParam("start") int start,
      @DefaultValue("25") @QueryParam("limit") int limit,
      @Nullable @QueryParam("q") String query,
      @Nullable @QueryParam("supported_locale") List<ULocale> supported_locale,
      @Nullable @QueryParam("geographical_areas") Set<URI> geographical_areas,
      @Nullable @QueryParam("restricted_areas") Set<URI> restricted_areas,
      @Nullable @QueryParam("target_audience") Set<CatalogEntry.TargetAudience> target_audience,
      @Nullable @QueryParam("payment_option") Set<CatalogEntry.PaymentOption> payment_option,
      @Nullable @QueryParam("category_id") Set<String> category_id
  ) {
    return post(locale, start, limit, query, supported_locale, geographical_areas, restricted_areas, target_audience, payment_option, category_id);
  }

  @POST
  @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
  public Response post(
      @Nullable @FormParam("hl") ULocale locale,
      @DefaultValue("0") @FormParam("start") int start,
      @DefaultValue("25") @FormParam("limit") int limit,
      @Nullable @FormParam("q") String query,
      @Nullable @QueryParam("supported_locale") List<ULocale> supported_locale,
      @Nullable @QueryParam("geographical_area") Set<URI> geographical_area,
      @Nullable @QueryParam("restricted_area") Set<URI> restricted_area,
      @Nullable @FormParam("target_audience") Set<CatalogEntry.TargetAudience> target_audience,
      @Nullable @FormParam("payment_option") Set<CatalogEntry.PaymentOption> payment_option,
      @Nullable @FormParam("category_id") Set<String> category_id
  ) {
    if (locale == null && context.getUserPrincipal() != null) {
      String accountId = ((OAuthPrincipal) context.getUserPrincipal()).getAccessToken().getAccountId();
      UserAccount account = accountRepository.getUserAccountById(accountId);
      locale = account.getLocale();
    }

    String trimmedQuery = query;
    if (query != null) {
      trimmedQuery = Strings.emptyToNull(query.trim());
    }

    // TODO: add information about apps the user has already "bought" (XXX: limit to client_id=portal! to avoid leaking data)
    CatalogEntryRepository.SearchRequest request = ImmutableCatalogEntryRepository.SearchRequest.builder()
        .displayLocale(locale)
        .start(start)
        .limit(limit)
        .query(trimmedQuery)
        .addAllSupported_locale(preProcess(supported_locale))
        .addAllGeographical_area(preProcess(geographical_area))
        .addAllRestricted_area(preProcess(restricted_area))
        .addAllTarget_audience(preProcess(target_audience))
        .addAllPayment_option(preProcess(payment_option))
        .addAllCategory_id(preProcessStr(category_id))
        .build();
    return Response.ok()
        .entity(new GenericEntity<Iterable<SimpleCatalogEntry>>(
            catalogEntryRepository.search(request)
        ) {})
        .build();
  }

  private Collection<String> preProcessStr(@Nullable Collection<String> strings) {
    strings = preProcess(strings);
    strings.removeIf(String::isEmpty);
    return strings;
  }

  private <T> Collection<T> preProcess(@Nullable Collection<T> objects) {
    if (objects == null) {
      return Collections.emptyList();
    }
    objects.removeIf(Objects::isNull);
    return objects;
  }
}

@@ -922,16 +922,84 @@
 til.Set;
+%0Aimport java.util.stream.Collectors;%0Aimport java.util.stream.Stream;
 %0A%0Aimport
@@ -5153,26 +5153,24 @@
 private 
-Collection
+Iterable
 %3CString%3E
@@ -5232,17 +5232,14 @@
     
-strings =
+return
  pre
@@ -5245,16 +5245,24 @@
 eProcess
+ToStream
 (strings
@@ -5266,67 +5266,189 @@
 ngs)
-;
 %0A    
-strings.removeIf(String::isEmpty);%0A    return strings
+    .filter(s -%3E !s.isEmpty())%0A        ::iterator;%0A  %7D%0A%0A  private %3CT%3E Iterable%3CT%3E preProcess(@Nullable Collection%3CT%3E objects) %7B%0A    return preProcessToStream(objects)::iterator
 ;%0A  
@@ -5464,26 +5464,22 @@
 ate %3CT%3E 
-Collection
+Stream
 %3CT%3E preP
@@ -5476,32 +5476,40 @@
 am%3CT%3E preProcess
+ToStream
 (@Nullable Colle
@@ -5572,29 +5572,20 @@
 urn 
-Collections
+Stream
 .empty
-List
 ();%0A
@@ -5598,16 +5598,23 @@
     
+return 
 objects.
 remo
@@ -5613,16 +5613,23 @@
 cts.
-removeIf
+stream().filter
 (Obj
@@ -5638,37 +5638,18 @@
 ts::
-is
+non
 Null);%0A
-    return objects;%0A
   %7D%0A

Fix regression/behavioral change in Resteasy 3.5.1 (compared to 3.1.4)