package com.atlassian.bitbucket.test;

import com.atlassian.bitbucket.test.rest.comment.RestCommentUtils;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.jayway.restassured.RestAssured;
import com.jayway.restassured.response.Response;
import com.jayway.restassured.specification.ResponseSpecification;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.lang3.mutable.MutableLong;

import java.util.Arrays;

import static javax.ws.rs.core.Response.Status.CREATED;
import static javax.ws.rs.core.Response.Status.NO_CONTENT;

@SuppressWarnings("unused") //This class is "public", so plugin developers may be using its methods
public class PullRequestRestTestHelper {

    public static long createComment(String projectKey, String repoSlug, long pullRequestId, String username,
                                     String password, JSONObject body, String commentText) {
        MutableLong commentId = new MutableLong();
        RestAssured.expect()
                .spec(RestCommentUtils.createSpecBuilderWithNoReplies(CREATED, commentId, commentText, username)
                        .build())
                .log().ifError()
                .given()
                .auth().preemptive().basic(username, password)
                .contentType("application/json")
                .body(body)
                .post(getPullRequestCommentsURL(projectKey, repoSlug, pullRequestId));
        return commentId.longValue();
    }

    public static void addWatcher(String projectKey, String repoSlug, long pullRequestId,
                                  String username, String password) {
        RestAssured.expect()
                .statusCode(NO_CONTENT.getStatusCode())
                .log().ifError()
                .given()
                .auth().preemptive().basic(username, password)
                .contentType("application/json")
                .when()
                .post(getPullRequestWatchURL(projectKey, repoSlug, pullRequestId));
    }

    public static void removeWatcher(String projectKey, String repoSlug, long pullRequestId,
                                     String username, String password) {
        RestAssured.expect()
                .statusCode(NO_CONTENT.getStatusCode())
                .log().ifError()
                .given()
                .auth().preemptive().basic(username, password)
                .contentType("application/json")
                .when()
                .delete(getPullRequestWatchURL(projectKey, repoSlug, pullRequestId));
    }

    public static JSONObject createCreateBody(String title, String description, String fromProjectKey,
                                              String fromRepoSlug, String fromBranchId, String toProjectKey,
                                              String toRepoSlug, String toBranchId) {
        JSONObject bodyObject = new JSONObject();
        bodyObject.put("title", title);
        bodyObject.put("description", description);

        bodyObject.put("fromRef", createRefBody(fromProjectKey, fromRepoSlug, fromBranchId));
        bodyObject.put("toRef", createRefBody(toProjectKey, toRepoSlug, toBranchId));
        return bodyObject;
    }

    public static Response createPullRequest(MutableLong id, String username, String password, String title,
                                             String description, String fromProjectKey, String fromRepoSlug,
                                             String fromBranchId, String toProjectKey, String toRepoSlug,
                                             String toBranchId, ResponseSpecification spec, String... reviewers) {
        JSONObject body = PullRequestRestTestHelper.createCreateBody(title, description,
                fromProjectKey, fromRepoSlug, fromBranchId,
                toProjectKey, toRepoSlug, toBranchId);
        body.put("reviewers", Lists.transform(Arrays.asList(reviewers), new Function<String, JSONObject>() {
            @Override
            public JSONObject apply(String reviewer) {
                return new JSONObject().accumulate("user", new JSONObject().accumulate("name", reviewer));
            }
        }));
        return RestAssured.expect().spec(spec)
                .log().ifError()
                .body("id", RestTestHelper.captureLongId(id))
                .given()
                .auth().preemptive().basic(username, password)
                .body(body.toString())
                .contentType("application/json")
                .when()
                .post(DefaultFuncTestData.getRestURL() + "/projects/" + toProjectKey + "/repos/" + toRepoSlug + "/pull-requests");
    }

    public static JSONObject createUpdateBody(String title, String description, Integer version) {
        JSONObject bodyObject = new JSONObject();
        bodyObject.put("title", title);
        bodyObject.put("description", description);
        if (version != null) {
            bodyObject.put("version", version);
        }
        return bodyObject;
    }

    public static JSONObject createUpdateReviewersBody(Integer version, String... usernames) {
        JSONObject bodyObject = new JSONObject();
        JSONArray reviewers = new JSONArray();
        for (String username : usernames) {
            JSONObject reviewer = new JSONObject();
            JSONObject user = new JSONObject();
            user.put("name", username);
            reviewer.put("user", user);
            reviewers.add(reviewer);
        }
        bodyObject.put("reviewers", reviewers);
        if (version != null) {
            bodyObject.put("version", version);
        }
        return bodyObject;
    }

    public static JSONObject createUpdateBody(String newState, Integer version) {
        JSONObject bodyObject = new JSONObject();
        if (version != null) {
            bodyObject.put("version", version);
        }
        bodyObject.put("state", newState);
        return bodyObject;
    }

    public static JSONObject createUpdateBody(String newTitle, String newDescription, String newState, Integer version) {
        JSONObject bodyObject = new JSONObject();
        bodyObject.put("title", newTitle);
        bodyObject.put("description", newDescription);
        if (version != null) {
            bodyObject.put("version", version);
        }
        bodyObject.put("state", newState);
        return bodyObject;
    }

    public static String getPullRequestCommentURL(String projectKey, String repoSlug, long pullRequestId,
                                                  long commentId, int version) {
        return getPullRequestCommentsURL(projectKey, repoSlug, pullRequestId) + "/" + commentId + "?version=" + version;
    }

    public static String getPullRequestCommentsURL(String projKey, String repoSlug, long pullRequestId) {
        return RestCommentUtils.getPullRequestCommentsUrl(projKey, repoSlug, pullRequestId);
    }

    private static JSONObject createRefBody(String projectKey, String repoSlug, String branchId) {
        JSONObject ref = new JSONObject();
        ref.put("id", branchId);

        if (projectKey != null || repoSlug != null) {
            if (projectKey == null || repoSlug == null) { //One is null, but not the other
                throw new IllegalArgumentException("When specifying a repository, both project key and repository slug are required");
            }

            JSONObject proj = new JSONObject();
            proj.put("key", projectKey);

            JSONObject repo = new JSONObject();
            repo.put("slug", repoSlug);
            repo.put("project", proj);

            ref.put("repository", repo);
        }

        return ref;
    }

    public static String getPullRequestWatchURL(String projectKey, String repoSlug, long pullRequestId) {
        return getPullRequestURL(projectKey, repoSlug, pullRequestId) + "/watch";
    }

    public static String getPullRequestURL(String projectKey, String repoSlug, long id) {
        return getPullRequestsURL(projectKey, repoSlug) + "/" + id;
    }

    public static String getPullRequestsURL(String projectKey, String repoSlug) {
        return DefaultFuncTestData.getRestURL() + "/projects/" + projectKey +
                "/repos/" + repoSlug + "/pull-requests";
    }

    public static String getPullRequestViewURL(String projectKey, String repoSlug, long id) {
        return DefaultFuncTestData.getBaseURL() + "/projects/" + projectKey + "/repos/" + repoSlug + "/pull-requests/" + id;
    }
}
