package com.criteo.gerrit.plugins.automerge;

import com.google.gerrit.common.data.SubmitRecord;
import com.google.gerrit.extensions.api.changes.SubmitInput;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.restapi.RestApiException;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.reviewdb.server.ReviewDb;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.account.AccountByEmailCache;
import com.google.gerrit.server.change.ChangesCollection;
import com.google.gerrit.server.change.GetRelated;
import com.google.gerrit.server.change.GetRelated.RelatedInfo;
import com.google.gerrit.server.change.PostReview;
import com.google.gerrit.server.change.RevisionResource;
import com.google.gerrit.server.change.Submit;
import com.google.gerrit.server.data.ChangeAttribute;
import com.google.gerrit.server.git.MergeUtil;
import com.google.gerrit.server.project.ChangeControl;
import com.google.gerrit.server.project.NoSuchChangeException;
import com.google.gerrit.server.project.SubmitRuleEvaluator;
import com.google.gerrit.server.query.change.ChangeData;
import com.google.gwtorm.server.OrmException;
import com.google.inject.Inject;
import com.google.inject.Provider;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.List;
import java.util.Set;

public class AtomicityHelper {

  private final static Logger log = LoggerFactory.getLogger(AtomicityHelper.class);

  @Inject
  private AccountByEmailCache byEmailCache;

  @Inject
  ChangeData.Factory changeDataFactory;

  @Inject
  private ChangeControl.GenericFactory changeFactory;

  @Inject
  private ChangesCollection collection;

  @Inject
  AutomergeConfig config;

  @Inject
  Provider<ReviewDb> db;

  @Inject
  private IdentifiedUser.GenericFactory factory;

  @Inject
  GetRelated getRelated;

  @Inject
  MergeUtil.Factory mergeUtilFactory;

  @Inject
  Provider<PostReview> reviewer;

  @Inject
  Submit submitter;

  /**
   * Check if the current patchset of the specified change has dependent
   * unmerged changes.
   *
   * @param project
   * @param number
   * @return true or false
   * @throws IOException
   * @throws NoSuchChangeException
   * @throws OrmException
   */
  public boolean hasDependentReview(String project, int number) throws IOException, NoSuchChangeException, OrmException {
      RevisionResource r = getRevisionResource(project, number);
    RelatedInfo related = getRelated.apply(r);
    log.debug(String.format("Checking for related changes on review %d", number));
    return related.changes.size() > 0;
  }

  /**
   * Check if a change is an atomic change or not. A change is atomic if it has
   * the atomic topic prefix.
   *
   * @param change a ChangeAttribute instance
   * @return true or false
   */
  public boolean isAtomicReview(final ChangeAttribute change) {
    final boolean atomic = change.topic != null && change.topic.startsWith(config.getTopicPrefix());
    log.debug(String.format("Checking if change %s is an atomic change: %b", change.number, atomic));
    return atomic;
  }

  /**
   * Check if a change is submitable.
   *
   * @param project a change project
   * @param change a change number
   * @return true or false
   * @throws OrmException
   */
  public boolean isSubmittable(String project, int change) throws OrmException {
    ChangeData changeData = changeDataFactory.create(db.get(), new Project.NameKey(project), new Change.Id(change));
    List<SubmitRecord> cansubmit = new SubmitRuleEvaluator(changeData).evaluate();
    log.debug(String.format("Checking if change %d is submitable.", change));
    for (SubmitRecord submit : cansubmit) {
      if (submit.status != SubmitRecord.Status.OK) {
        log.debug(String.format("Change %d is not submitable", change));
        return false;
      }
    }
    log.debug(String.format("Change %d is submitable", change));
    return true;
  }

  /**
   * Merge a review.
   *
   * @param info
   * @throws RestApiException
   * @throws NoSuchChangeException
   * @throws OrmException
   * @throws IOException
   */
  public void mergeReview(ChangeInfo info) throws RestApiException, NoSuchChangeException, OrmException, IOException {
    final SubmitInput input = new SubmitInput();
    input.waitForMerge = true;
    final RevisionResource r = getRevisionResource(info.project, info._number);
    submitter.apply(r, input);
  }

  public RevisionResource getRevisionResource(String project, int changeNumber) throws NoSuchChangeException, OrmException {
    ChangeControl ctl = changeFactory.validateFor(db.get(), new Change.Id(changeNumber), getBotUser());
    ChangeData changeData = changeDataFactory.create(db.get(), new Project.NameKey(project), new Change.Id(changeNumber));
    RevisionResource r = new RevisionResource(collection.parse(ctl), changeData.currentPatchSet());
    return r;
  }

  private IdentifiedUser getBotUser() {
    final Set<Account.Id> ids = byEmailCache.get(config.getBotEmail());
    if (ids.isEmpty()) {
      throw new RuntimeException("No user found with email: " + config.getBotEmail());
    }
    final IdentifiedUser bot = factory.create(ids.iterator().next());
    return bot;
  }
}

@@ -3686,16 +3686,58 @@
 geData).
+setPatchSet(changeData.currentPatchSet()).
 evaluate

Fix NullPointerException when evaluating draft reviews