package com.airlock.iam.common.application.query;

import ch.ergon.restal.jsonapi.parameter.FilterParameter;
import ch.ergon.restal.jsonapi.parameter.PageParameter;
import ch.ergon.restal.jsonapi.parameter.SortParameter;
import com.airlock.iam.base.api.domain.model.store.query.Orderable;
import com.airlock.iam.base.api.domain.model.store.query.Restriction;
import com.airlock.iam.common.api.domain.model.store.user.UserStore;
import com.airlock.iam.common.api.domain.model.store.user.schema.UserSchema;
import com.airlock.iam.common.api.domain.model.user.PersistentUser;
import com.airlock.iam.common.application.query.restriction.RestrictionProviders;
import com.airlock.iam.common.application.query.restriction.StringRestrictionProvider;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;

public final class UserStoreQuery {
   private final UserStore store;
   private final Optional<UserStore> userListDetailLoader;
   private final QueryPager queryPager;
   private final QueryFilter queryFilter;
   private final QuerySorter querySorter;

   public UserStoreQuery withPager(PageParameter page) {
      this.queryPager.setPageParameter(page);
      return this;
   }

   public UserStoreQuery withSorter(SortParameter sort) {
      this.querySorter.setSortParameter(sort);
      return this;
   }

   public UserStoreQuery withFilter(FilterParameter filter, boolean caseInsensitiveUsername, boolean caseInsensitiveContextData) {
      this.queryFilter.setCaseInsensitiveUsername(caseInsensitiveUsername);
      this.queryFilter.setCaseInsensitiveContextData(caseInsensitiveContextData);
      this.queryFilter.setFilterParameter(filter);
      return this;
   }

   public UserStoreQuery withContextDataFiltering(List<String> contextDataColumns) {
      Set<String> reserved = QueryParameters.getReservedParameters();

      for (String column : contextDataColumns) {
         if (!reserved.contains(column)) {
            this.queryFilter.addDisjunctionProvider(new StringRestrictionProvider(column));
         }
      }

      return this;
   }

   public UserStoreQuery withAdditionalFilterRestrictionProviders(RestrictionProviders restrictionProviders) {
      restrictionProviders.getProviders().forEach(this.queryFilter::addConjunctionProvider);
      return this;
   }

   public UserStoreQuery withStaticConjunctions(List<Restriction> staticConjunctions) {
      this.queryFilter.addStaticConjunctions(staticConjunctions);
      return this;
   }

   public static UserStoreQuery on(UserStore store, Optional<UserStore> userListDetailLoader) {
      UserSchema schema = store.schema();
      return builder()
         .store(store)
         .userListDetailLoader(userListDetailLoader)
         .queryPager(new QueryPager(schema))
         .queryFilter(new QueryFilter(schema))
         .querySorter(new QuerySorter(schema))
         .build();
   }

   public List<String> getUserIds() {
      Orderable<PersistentUser> query = this.queryFilter.filter(this.store.find());
      query = this.querySorter.sort(query);
      return this.queryPager.selectUserIdsInRange(query).list();
   }

   public Stream<PersistentUser> getUsers() {
      if (this.userListDetailLoader.isPresent()) {
         Orderable<PersistentUser> query = this.queryFilter.filter(this.store.find());
         query = this.querySorter.sort(query);
         Stream<String> userIds = this.queryPager.selectUserIdsInRange(query).stream();
         return userIds.map(this.userListDetailLoader.get()::getByIdentity);
      } else {
         Orderable<PersistentUser> query = this.queryFilter.filter(this.store.find());
         query = this.querySorter.sort(query);
         return this.queryPager.selectUsersInRange(query).stream();
      }
   }

   public int getTotalCount() {
      Orderable<PersistentUser> query = this.queryFilter.filter(this.store.find());
      return query.aggregate(this.store.schema().count());
   }

   @SuppressFBWarnings(justification = "generated code")
   UserStoreQuery(UserStore store, Optional<UserStore> userListDetailLoader, QueryPager queryPager, QueryFilter queryFilter, QuerySorter querySorter) {
      this.store = store;
      this.userListDetailLoader = userListDetailLoader;
      this.queryPager = queryPager;
      this.queryFilter = queryFilter;
      this.querySorter = querySorter;
   }

   @SuppressFBWarnings(justification = "generated code")
   public static UserStoreQuery.UserStoreQueryBuilder builder() {
      return new UserStoreQuery.UserStoreQueryBuilder();
   }

   @SuppressFBWarnings(justification = "generated code")
   public static class UserStoreQueryBuilder {
      @SuppressFBWarnings(justification = "generated code")
      private UserStore store;
      @SuppressFBWarnings(justification = "generated code")
      private Optional<UserStore> userListDetailLoader;
      @SuppressFBWarnings(justification = "generated code")
      private QueryPager queryPager;
      @SuppressFBWarnings(justification = "generated code")
      private QueryFilter queryFilter;
      @SuppressFBWarnings(justification = "generated code")
      private QuerySorter querySorter;

      @SuppressFBWarnings(justification = "generated code")
      UserStoreQueryBuilder() {
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserStoreQuery.UserStoreQueryBuilder store(UserStore store) {
         this.store = store;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserStoreQuery.UserStoreQueryBuilder userListDetailLoader(Optional<UserStore> userListDetailLoader) {
         this.userListDetailLoader = userListDetailLoader;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserStoreQuery.UserStoreQueryBuilder queryPager(QueryPager queryPager) {
         this.queryPager = queryPager;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserStoreQuery.UserStoreQueryBuilder queryFilter(QueryFilter queryFilter) {
         this.queryFilter = queryFilter;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserStoreQuery.UserStoreQueryBuilder querySorter(QuerySorter querySorter) {
         this.querySorter = querySorter;
         return this;
      }

      @SuppressFBWarnings(justification = "generated code")
      public UserStoreQuery build() {
         return new UserStoreQuery(this.store, this.userListDetailLoader, this.queryPager, this.queryFilter, this.querySorter);
      }

      @SuppressFBWarnings(justification = "generated code")
      @Override
      public String toString() {
         return "UserStoreQuery.UserStoreQueryBuilder(store="
            + this.store
            + ", userListDetailLoader="
            + this.userListDetailLoader
            + ", queryPager="
            + this.queryPager
            + ", queryFilter="
            + this.queryFilter
            + ", querySorter="
            + this.querySorter
            + ")";
      }
   }
}
