/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.solr.search.grouping.distributed.requestfactory;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.grouping.GroupDocs;
import org.apache.lucene.search.grouping.TopGroups;
import org.apache.lucene.util.BytesRef;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.GroupParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.params.ShardParams;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.handler.component.ResponseBuilder;
import org.apache.solr.handler.component.ShardDoc;
import org.apache.solr.handler.component.ShardRequest;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.search.grouping.distributed.ShardRequestFactory;
import org.apache.solr.search.grouping.distributed.command.QueryCommandResult;

/** */
public class StoredFieldsShardRequestFactory implements ShardRequestFactory {

  @Override
  public ShardRequest[] constructRequest(ResponseBuilder rb) {
    HashMap<String, Set<ShardDoc>> shardMap = new HashMap<>();
    for (TopGroups<BytesRef> topGroups : rb.mergedTopGroups.values()) {
      for (GroupDocs<BytesRef> group : topGroups.groups) {
        mapShardToDocs(shardMap, group.scoreDocs);
      }
    }

    for (QueryCommandResult queryCommandResult : rb.mergedQueryCommandResults.values()) {
      mapShardToDocs(shardMap, queryCommandResult.getTopDocs().scoreDocs);
    }

    ShardRequest[] shardRequests = new ShardRequest[shardMap.size()];
    SchemaField uniqueField = rb.req.getSchema().getUniqueKeyField();
    int i = 0;
    for (Collection<ShardDoc> shardDocs : shardMap.values()) {
      ShardRequest sreq = new ShardRequest();
      sreq.purpose = ShardRequest.PURPOSE_GET_FIELDS;
      sreq.shards = new String[] {shardDocs.iterator().next().shard};
      sreq.params = new ModifiableSolrParams();
      sreq.params.add(rb.req.getParams());
      sreq.params.remove(GroupParams.GROUP);
      sreq.params.remove(CommonParams.SORT);
      sreq.params.remove(ResponseBuilder.FIELD_SORT_VALUES);

      // we need to ensure the uniqueField is included for collating docs with their return fields
      if (!rb.rsp.getReturnFields().wantsField(uniqueField.getName())) {
        // the user didn't ask for it, so we have to...
        sreq.params.add(CommonParams.FL, uniqueField.getName());
      }

      List<String> ids = new ArrayList<>(shardDocs.size());
      for (ShardDoc shardDoc : shardDocs) {
        ids.add(shardDoc.id.toString());
      }
      sreq.params.add(ShardParams.IDS, StrUtils.join(ids, ','));
      shardRequests[i++] = sreq;
    }

    return shardRequests;
  }

  private void mapShardToDocs(HashMap<String, Set<ShardDoc>> shardMap, ScoreDoc[] scoreDocs) {
    for (ScoreDoc scoreDoc : scoreDocs) {
      ShardDoc solrDoc = (ShardDoc) scoreDoc;
      Set<ShardDoc> shardDocs = shardMap.get(solrDoc.shard);
      if (shardDocs == null) {
        shardMap.put(solrDoc.shard, shardDocs = new HashSet<>());
      }
      shardDocs.add(solrDoc);
    }
  }
}
