package io.corbel.resources.rem.dao;


import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import io.corbel.lib.queries.request.Pagination;
import io.corbel.lib.queries.request.ResourceQuery;
import io.corbel.lib.queries.request.Sort;
import io.corbel.resources.rem.dao.builder.MongoAggregationBuilder;
import io.corbel.resources.rem.model.ResourceUri;
import io.corbel.resources.rem.resmi.exception.InvalidApiParamException;
import io.corbel.resources.rem.utils.JsonUtils;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.index.IndexDefinition;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author Alexander De Leon (alex.deleon@devialab.com)
 */
public interface MongoResmiDao extends ResmiDao {

    default JsonObject findResource(ResourceUri uri) {
        return findResource(uri, JsonObject.class);
    }

    default JsonArray findCollection(ResourceUri uri, Optional<List<ResourceQuery>> resourceQueries, Optional<String> textSearch, Optional<Pagination> pagination, Optional<Sort> sort) throws InvalidApiParamException {
        return JsonUtils.convertToArray(findCollection(uri, resourceQueries, textSearch, pagination, sort, JsonObject.class));
    }


    default JsonElement findRelation(ResourceUri uri, Optional<List<ResourceQuery>> resourceQueries, Optional<String> textSearch, Optional<Pagination> pagination, Optional<Sort> sort) throws InvalidApiParamException {
        JsonArray result = ResmiDaoHelper.renameIds(JsonUtils.convertToArray(findRelation(uri, resourceQueries, textSearch, pagination, sort, JsonObject.class)), uri.isTypeWildcard());

        if (uri.getRelationId() != null) {
            if (result.size() == 1) {
                return result.get(0);
            } else if (result.size() == 0) {
                return null;
            }
        }

        return result;
    }

    default JsonArray findCollectionWithGroup(ResourceUri uri, Optional<List<ResourceQuery>> resourceQueries, Optional<String> textSearch, Optional<Pagination> pagination, Optional<Sort> sort, List<String> groups, boolean first) throws InvalidApiParamException {
        if (sort.isPresent() && first) {
            sort = Optional.of(new Sort(sort.get().getDirection().name(), "first."+sort.get().getField()));
        }
        List<JsonObject> result = aggregate(uri, resourceQueries, textSearch, pagination, sort, JsonObject.class);
        return JsonUtils.convertToArray(first ? extractDocuments(result) : result);
    }

    default JsonArray findRelationWithGroup(ResourceUri uri, Optional<List<ResourceQuery>> resourceQueries, Optional<String> textSearch, Optional<Pagination> pagination, Optional<Sort> sort, List<String> groups, boolean first) throws InvalidApiParamException {
        if (sort.isPresent() && first) {
            sort = Optional.of(new Sort(sort.get().getDirection().name(), "first."+sort.get().getField()));
        }
        List<JsonObject> result = aggregate(uri, resourceQueries, textSearch, pagination, sort, JsonObject.class);
        return ResmiDaoHelper.renameIds(JsonUtils.convertToArray(first ? extractDocuments(result) : result), uri.isTypeWildcard());
    }


    <T> T findResource(ResourceUri uri, Class<T> entityClass);

    <T> List<T> findCollection(ResourceUri uri, Optional<List<ResourceQuery>> resourceQueries, Optional<String> textSearch, Optional<Pagination> pagination, Optional<Sort> sort, Class<T> entityClass) throws InvalidApiParamException;

    <T> List<T> findRelation(ResourceUri uri, Optional<List<ResourceQuery>> resourceQueries, Optional<String> textSearch, Optional<Pagination> pagination, Optional<Sort> sort, Class<T> entityClass)throws InvalidApiParamException;

    <T> List<T> aggregate(ResourceUri resourceUri, Optional<List<ResourceQuery>> resourceQueries, Optional<String> textSearch, Optional<Pagination> pagination, Optional<Sort> sort, Class<T> entityClass, AggregationOperation... operations) throws InvalidApiParamException;

    <T> List<T> findAll(ResourceUri uri, Class<T> entityClass);

    void ensureExpireIndex(ResourceUri uri);

    void ensureIndex(ResourceUri uri, IndexDefinition indexDefinition);

    static List<JsonObject> extractDocuments(List<JsonObject> results) {
        return results.stream().map(result -> result.get(MongoAggregationBuilder.REFERENCE).getAsJsonObject()).collect(Collectors.toList());
    }
}

@@ -580,32 +580,175 @@
 tion.Aggregation
+;%0Aimport org.springframework.data.mongodb.core.aggregation.AggregationOperation;%0Aimport org.springframework.data.mongodb.core.aggregation.Group
 Operation;%0Aimpor
@@ -898,16 +898,102 @@
 ctors;%0A%0A
+import static io.corbel.resources.rem.dao.builder.MongoAggregationBuilder.REFERENCE;%0A%0A
 /**%0A * @
@@ -2545,106 +2545,115 @@
 t = 
-Optional.of(new Sort(sort.get().getDirection().name()
+getSortWithFirst(sort);%0A        %7D%0A        GroupOperation group = getGroupOperationWithFirst(groups
 , 
-%22
 first
-.%22+sort.get().getField()));%0A        %7D
+);
 %0A   
@@ -2753,32 +2753,39 @@
 JsonObject.class
+, group
 );%0A        retur
@@ -3187,107 +3187,116 @@
 t = 
-Optional.of(new Sort(sort.get().getDirection().name()
+getSortWithFirst(sort);%0A        %7D%0A        GroupOperation group = getGroupOperationWithFirst(groups
 , 
-%22
 first
-.%22+sort.get().getField())
 );%0A
-        %7D%0A
     
@@ -3399,24 +3399,31 @@
 Object.class
+, group
 );%0A        r
@@ -3548,24 +3548,512 @@
 ());%0A    %7D%0A%0A
+    default GroupOperation getGroupOperationWithFirst(List%3CString%3E groups, boolean first) %7B%0A        GroupOperation group = Aggregation.group(groups.toArray(new String%5Bgroups.size()%5D));%0A        if (first) %7B%0A            group = group.first(Aggregation.ROOT).as(REFERENCE);%0A        %7D%0A        return group;%0A    %7D%0A%0A    default Optional%3CSort%3E getSortWithFirst(Optional%3CSort%3E sort) %7B%0A        return Optional.of(new Sort(sort.get().getDirection().name(), %22first.%22+sort.get().getField()));%0A    %7D%0A%0A
 %0A    %3CT%3E T f
@@ -5161,32 +5161,8 @@
 get(
-MongoAggregationBuilder.
 REFE

add group operation to fix disctinct