// Copyright (C) 2013 The Android Open Source Project
//
// Licensed 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 com.googlesource.gerrit.plugins.github.wizard;

import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gerrit.entities.Project.NameKey;
import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.project.ProjectCache;
import com.google.gerrit.server.query.change.ChangeQueryBuilder;
import com.google.gerrit.server.query.change.ChangeQueryProcessor;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.googlesource.gerrit.plugins.github.GitHubConfig;
import com.googlesource.gerrit.plugins.github.oauth.GitHubLogin;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.lib.Repository;
import org.kohsuke.github.GHIssueState;
import org.kohsuke.github.GHPullRequest;
import org.kohsuke.github.GHPullRequestCommitDetail;
import org.kohsuke.github.GHRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Singleton
public class PullRequestListController implements VelocityController {
  private static final Logger LOG = LoggerFactory.getLogger(PullRequestListController.class);
  private static final String DATE_FMT = "yyyy-MM-dd HH:mm z";

  private final GitHubConfig config;
  private final ProjectCache projectsCache;
  private final GitRepositoryManager repoMgr;
  private final Provider<ChangeQueryProcessor> qp;
  private final ChangeQueryBuilder changeQuery;

  @Inject
  public PullRequestListController(
      ProjectCache projectsCache,
      GitRepositoryManager repoMgr,
      GitHubConfig config,
      Provider<ChangeQueryProcessor> qp,
      ChangeQueryBuilder changeQuery) {
    this.projectsCache = projectsCache;
    this.repoMgr = repoMgr;
    this.config = config;
    this.qp = qp;
    this.changeQuery = changeQuery;
  }

  @Override
  public void doAction(
      IdentifiedUser user,
      GitHubLogin hubLogin,
      HttpServletRequest req,
      HttpServletResponse resp,
      ControllerErrors errors)
      throws ServletException, IOException {
    try (PrintWriter out = resp.getWriter()) {
      SimpleDateFormat dateFmt = new SimpleDateFormat(DATE_FMT);
      String organisation = req.getParameter("organisation");
      String repository = req.getParameter("repository");
      Map<String, List<GHPullRequest>> pullRequests =
          getPullRequests(hubLogin, organisation, repository);

      JsonArray reposPullRequests = new JsonArray();
      for (Entry<String, List<GHPullRequest>> repoEntry : pullRequests.entrySet()) {
        JsonObject repoPullRequests = new JsonObject();

        repoPullRequests.add("repository", new JsonPrimitive(repoEntry.getKey()));

        if (repoEntry.getValue() != null) {
          JsonArray prArray = new JsonArray();
          for (GHPullRequest pr : repoEntry.getValue()) {
            JsonObject prObj = new JsonObject();
            prObj.add("id", new JsonPrimitive(new Integer(pr.getNumber())));
            prObj.add("title", new JsonPrimitive(pr.getTitle()));
            prObj.add("body", new JsonPrimitive(pr.getBody()));
            prObj.add(
                "author", new JsonPrimitive(pr.getUser() == null ? "" : pr.getUser().getLogin()));
            prObj.add("status", new JsonPrimitive(pr.getState().name()));
            prObj.add("date", new JsonPrimitive(dateFmt.format(pr.getUpdatedAt())));

            prArray.add(prObj);
          }
          repoPullRequests.add("pullrequests", prArray);
        }

        reposPullRequests.add(repoPullRequests);
      }
      out.println(reposPullRequests.toString());
    }
  }

  private Map<String, List<GHPullRequest>> getPullRequests(
      GitHubLogin hubLogin, String organisation, String repository) throws IOException {
    return getPullRequests(
        hubLogin, projectsCache.byName(organisation + "/" + Strings.nullToEmpty(repository)));
  }

  private Map<String, List<GHPullRequest>> getPullRequests(
      GitHubLogin login, Iterable<NameKey> repos) throws IOException {
    int numPullRequests = 0;
    Map<String, List<GHPullRequest>> allPullRequests = Maps.newHashMap();
    for (NameKey gerritRepoName : repos) {
      try (Repository gitRepo = repoMgr.openRepository(gerritRepoName)) {
        String ghRepoName = gerritRepoName.get().split("/")[1];
        Optional<GHRepository> githubRepo = getGHRepository(login, gerritRepoName);
        if (githubRepo.isPresent()) {
          numPullRequests =
              collectPullRequestsFromGitHubRepository(
                  numPullRequests, allPullRequests, gitRepo, ghRepoName, githubRepo);
        }
      }
    }
    return allPullRequests;
  }

  private int collectPullRequestsFromGitHubRepository(
      int numPullRequests,
      Map<String, List<GHPullRequest>> allPullRequests,
      Repository gitRepo,
      String ghRepoName,
      Optional<GHRepository> githubRepo)
      throws IncorrectObjectTypeException, IOException {
    List<GHPullRequest> repoPullRequests = Lists.newArrayList();

    int count = numPullRequests;
    if (count < config.pullRequestListLimit) {
      for (GHPullRequest ghPullRequest : githubRepo.get().listPullRequests(GHIssueState.OPEN)) {

        if (isAnyCommitOfPullRequestToBeImported(gitRepo, ghPullRequest)) {
          repoPullRequests.add(ghPullRequest);
          count++;
        }
      }
      if (repoPullRequests.size() > 0) {
        allPullRequests.put(ghRepoName, repoPullRequests);
      }
    } else {
      allPullRequests.put(ghRepoName, null);
    }
    return count;
  }

  private Optional<GHRepository> getGHRepository(GitHubLogin login, NameKey gerritRepoName)
      throws IOException {
    try {
      return Optional.of(login.getHub().getRepository(gerritRepoName.get()));
    } catch (FileNotFoundException e) {
      LOG.debug("GitHub repository {} cannot be found", gerritRepoName.get());
      return Optional.absent();
    }
  }

  private boolean isAnyCommitOfPullRequestToBeImported(
      Repository gitRepo, GHPullRequest ghPullRequest)
      throws IncorrectObjectTypeException, IOException {
    boolean pullRequestToImport = false;
    try {
      for (GHPullRequestCommitDetail pullRequestCommit : ghPullRequest.listCommits()) {
        pullRequestToImport |=
            qp.get().query(changeQuery.commit(pullRequestCommit.getSha())).entities().isEmpty();
      }
      return pullRequestToImport;
    } catch (QueryParseException e) {
      LOG.error("Unable to query Gerrit changes for pull-request " + ghPullRequest.getNumber(), e);
      return false;
    }
  }
}

@@ -4241,24 +4241,44 @@
 onPrimitive(
+Strings.nullToEmpty(
 pr.getTitle(
@@ -4276,24 +4276,25 @@
 getTitle()))
+)
 ;%0A          
@@ -4327,24 +4327,44 @@
 onPrimitive(
+Strings.nullToEmpty(
 pr.getBody()
@@ -4361,24 +4361,25 @@
 .getBody()))
+)
 ;%0A          

Fix issue with importing PR without description