// Copyright 2014 Palantir Technologies
//
// 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.palantir.stash.stashbot.admin;

import java.io.IOException;
import java.io.Writer;
import java.util.Arrays;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.slf4j.Logger;

import com.atlassian.stash.nav.NavBuilder;
import com.atlassian.stash.project.Project;
import com.atlassian.stash.project.ProjectService;
import com.atlassian.stash.pull.PullRequest;
import com.atlassian.stash.pull.PullRequestMergeVeto;
import com.atlassian.stash.pull.PullRequestMergeability;
import com.atlassian.stash.pull.PullRequestService;
import com.atlassian.stash.repository.Repository;
import com.atlassian.stash.repository.RepositoryService;
import com.palantir.stash.stashbot.logger.PluginLoggerFactory;

public class BuildStatusReportingServlet extends HttpServlet {

    /**
     * Surface information about build success / failure for a given pull request
     * 
     * URL is of the form BASE_URL/REPO_ID_OR_SLUG/PULLREQUEST_ID] <br/>
     * 
     * <br/>
     * REPO_ID is the stash internal ID of the repository<br/>
     * PULLREQUEST_ID is the pull request ID<br/>
     * 
     */
    private static final long serialVersionUID = 1L;
    private final Logger log;

    private final RepositoryService rs;
    private final ProjectService ps;
    private final PullRequestService prs;
    private final NavBuilder nb;

    public BuildStatusReportingServlet(RepositoryService rs, ProjectService ps, PullRequestService prs,
        NavBuilder nb, PluginLoggerFactory lf) {
        this.rs = rs;
        this.ps = ps;
        this.prs = prs;
        this.nb = nb;
        this.log = lf.getLoggerForThis(this);
    }

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {
        try {
            // Look at JenkinsManager class if you change this:
            // final two arguments could be empty...
            final String URL_FORMAT =
                "BASE_URL/REPO_ID_OR_SLUG/PULLREQUEST_ID]";
            final String pathInfo = req.getPathInfo();
            final String[] parts = pathInfo.split("/");

            // need at *least* 3 parts to be correct
            if (parts.length < 3) {
                throw new IllegalArgumentException("The format of the URL is "
                    + URL_FORMAT);
            }

            // Last part is always the PR
            String pullRequestPart = parts[parts.length - 1];

            // First part is always empty because string starts with '/', last is pr, the rest is the slug
            String slugOrId = StringUtils.join(Arrays.copyOfRange(parts, 1, parts.length - 1), "/");

            Repository repo;
            try {
                int repoId = Integer.valueOf(slugOrId);
                repo = rs.getById(repoId);
                if (repo == null) {
                    throw new IllegalArgumentException("Unable to find repository for repo id " + repoId);
                }
            } catch (NumberFormatException e) {
                // we have a slug, try to get a repo ID from that
                // slug should look like this: projects/PROJECT_KEY/repos/REPO_SLUG/pull-requests
                String[] newParts = slugOrId.split("/");

                if (newParts.length != 5) {
                    throw new IllegalArgumentException(
                        "The format of the REPO_ID_OR_SLUG is an ID, or projects/PROJECT_KEY/repos/REPO_SLUG/pull-requests");
                }
                Project p = ps.getByKey(newParts[1]);
                if (p == null) {
                    throw new IllegalArgumentException("Unable to find project for project key" + newParts[1]);
                }
                repo = rs.getBySlug(p.getKey(), newParts[3]);
                if (repo == null) {
                    throw new IllegalArgumentException("Unable to find repository for project key" + newParts[1]
                        + " and repo slug " + newParts[3]);
                }
            }

            final long pullRequestId;
            final PullRequest pullRequest;

            try {
                pullRequestId = Long.parseLong(pullRequestPart);
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException(
                    "Unable to parse pull request id " + parts[7], e);
            }
            pullRequest = prs.getById(repo.getId(), pullRequestId);
            if (pullRequest == null) {
                throw new IllegalArgumentException(
                    "Unable to find pull request for repo id "
                        + repo.getId().toString() + " pr id "
                        + pullRequestId);
            }

            PullRequestMergeability canMerge = prs.canMerge(repo.getId(), pullRequestId);

            JSONObject output = new JSONObject();
            output.put("repoId", repo.getId());
            output.put("prId", pullRequestId);
            output.put("url", nb.repo(repo).pullRequest(pullRequest.getId()).buildAbsolute());
            output.put("canMerge", canMerge.canMerge());
            if (!canMerge.canMerge()) {
                JSONArray vetoes = new JSONArray();
                for (PullRequestMergeVeto prmv : canMerge.getVetos()) {
                    JSONObject prmvjs = new JSONObject();
                    prmvjs.put("summary", prmv.getSummaryMessage());
                    prmvjs.put("details", prmv.getDetailedMessage());
                    vetoes.put(prmvjs);
                }
                output.put("vetoes", vetoes);
            }

            log.debug("Serving build status: " + output.toString());
            printOutput(output, req, res);
        } catch (Exception e) {
            res.reset();
            res.setStatus(500);
            res.setContentType("application/json");
            Writer w = res.getWriter();
            try {
                w.append(new JSONObject().put("error", e.getMessage()).toString());
            } catch (JSONException e1) {
                throw new RuntimeException("Errorception!", e1);
            }
            w.close();
        }
    }

    private void printOutput(JSONObject output, HttpServletRequest req, HttpServletResponse res)
        throws IOException {
        res.reset();
        res.setStatus(200);
        res.setContentType("application/json;charset=UTF-8");
        Writer w = res.getWriter();
        try {
            w.append(output.toString(4));
        } catch (JSONException e) {
            w.append(output.toString());
        }
        w.close();
    }

}

@@ -6308,32 +6308,510 @@
               %7D%0A
+                // You might expect a conflict would be included in the list of merge blockers.  You'd be mistaken.%0A                if (canMerge.isConflicted()) %7B%0A                    JSONObject prmvjs = new JSONObject();%0A                    prmvjs.put(%22summary%22, %22This pull request is unmergeable due to conflicts.%22);%0A                    prmvjs.put(%22details%22, %22You will need to resolve conflicts to be able to merge.%22);%0A                    vetoes.put(prmvjs);%0A                %7D%0A
                 

Include conflicted status in merge blockers