package it.com.atlassian.stash.rest.task;

import com.atlassian.bitbucket.content.DiffSegmentType;
import com.atlassian.bitbucket.rest.user.RestPerson;
import com.atlassian.bitbucket.rest.task.RestTask;
import com.atlassian.bitbucket.rest.task.RestTaskOperations;
import com.atlassian.bitbucket.test.*;
import com.atlassian.bitbucket.permission.Permission;
import it.com.atlassian.stash.rest.pull.BasePullRequestResourceTest;

import java.io.IOException;
import java.util.concurrent.TimeUnit;

abstract class AbstractTaskResourceTest extends BasePullRequestResourceTest {

    public static final String PROJECT_NAME = "tasks_test";
    public static final String REPOSITORY_NAME = "tasks_test";

    public static final String USER_PR_AUTHOR = PROJECT_NAME + "_pr_author";
    public static final String USER_TASK_CREATOR = PROJECT_NAME + "_task_creator";
    public static final String USER_PARTICIPANT = PROJECT_NAME + "_participant";
    public static final String USER_REPO_ADMIN = PROJECT_NAME + "_repo_admin";
    public static final String USER_CANNOT_READ = PROJECT_NAME + "_cannot_read";

    public static final String COMMENT_TEXT = "A comment";
    public static final String DIFF_COMMENT_TEXT = "A diff or file comment";
    public static final String TASK_TEXT = "A task";

    public static final String PR_TITLE = "task test title";

    protected static final String BASIC_BRANCHING_BRANCH = "branch_that_differ_by_one_file_src";
    protected static final String SOURCE_BRANCH = "refs/heads/" + BASIC_BRANCHING_BRANCH;
    protected static final String MASTER_BRANCH = "master";
    protected static final String TARGET_BRANCH = "refs/heads/" + MASTER_BRANCH;

    public static final String ID = RestTask.ID;
    public static final String STATE = RestTask.STATE;
    public static final String TEXT = RestTask.TEXT;
    public static final String ANCHOR = RestTask.ANCHOR;
    public static final String ANCHOR_ID = RestTask.ANCHOR + '.' + RestTask.ID;
    public static final String ANCHOR_TYPE = RestTask.ANCHOR + '.' + RestTask.ANCHOR_TYPE;
    public static final String AUTHOR = RestTask.AUTHOR;
    public static final String CREATED_BY = RestTask.AUTHOR + '.' + RestPerson.NAME;
    public static final String CREATED_DATE = RestTask.CREATED_DATE;
    public static final String PERMITTED_OPERATIONS = RestTask.PERMITTED_OPERATIONS;
    public static final String DELETABLE = RestTask.PERMITTED_OPERATIONS + '.' + RestTaskOperations.DELETABLE;
    public static final String EDITABLE = RestTask.PERMITTED_OPERATIONS + '.' + RestTaskOperations.EDITABLE;
    public static final String TRANSITIONABLE = RestTask.PERMITTED_OPERATIONS + '.' + RestTaskOperations.TRANSITIONABLE;

    public static final String FIRST_ERROR_MESSAGE = "errors[0].message";

    protected PullRequestComment createComment(PullRequestTestHelper pullRequest) {
        int repositoryId = RepositoryTestHelper.getRepository(pullRequest.getProjectKey(),
                pullRequest.getRepoSlug()).jsonPath().getInt("id");
        return new PullRequestComment(
            pullRequest.addGeneralComment(COMMENT_TEXT).jsonPath().getLong("id"),
            pullRequest.getId(),
            repositoryId);
    }

    protected PullRequestComment createDiffComment(PullRequestTestHelper pullRequest, String commitId, String path,
                                                   int line, DiffSegmentType lineType) {
        int repositoryId = RepositoryTestHelper.getRepository(pullRequest.getProjectKey(),
                pullRequest.getRepoSlug()).jsonPath().getInt("id");
        return new PullRequestComment(
                pullRequest.addCommitComment(commitId, DIFF_COMMENT_TEXT, path, line, lineType).jsonPath().getLong("id"),
                pullRequest.getId(),
                repositoryId);
    }

    protected PullRequestComment createDiffComment(PullRequestTestHelper pullRequest, String path, int line, DiffSegmentType lineType) {
        int repositoryId = RepositoryTestHelper.getRepository(pullRequest.getProjectKey(), pullRequest.getRepoSlug()).jsonPath().getInt("id");
        return new PullRequestComment(
                pullRequest.addDiffComment(DIFF_COMMENT_TEXT, path, line, lineType).jsonPath().getLong("id"),
                pullRequest.getId(),
                repositoryId);
    }

    protected PullRequestComment createFileComment(PullRequestTestHelper pullRequest, String path) {
        // Stash will create a file comment instead of a diff comment if either the line or line type is unspecified
        // see PullRequestCommentResource.createComment()
        return createDiffComment(pullRequest, path, -1, null);
    }

    protected PullRequestComment createReply(PullRequestTestHelper pullRequest, long commentId) {
        int repositoryId = RepositoryTestHelper.getRepository(pullRequest.getProjectKey(),
                pullRequest.getRepoSlug()).jsonPath().getInt("id");
        return new PullRequestComment(pullRequest.addCommentReply(DIFF_COMMENT_TEXT, commentId).jsonPath().getLong("id"),
                pullRequest.getId(), repositoryId);
    }

    protected TaskTestHelper createTask(TestContext testContext, PullRequestTestHelper pullRequest) {
        Long commentId = pullRequest.addGeneralComment(COMMENT_TEXT).jsonPath().getLong("id");
        return createTask(testContext, pullRequest.getId(), commentId);
    }

    protected TaskTestHelper createTask(TestContext testContext, long pullRequestId, long commentId) {
        return createTask(testContext, pullRequestId, commentId, TASK_TEXT);
    }

    protected TaskTestHelper createTask(TestContext testContext, long pullRequestId, long commentId, String text) {
        return createTask(testContext, PROJECT_NAME, PROJECT_NAME, pullRequestId, commentId, text);
    }

    protected TaskTestHelper createTask(TestContext testContext, String projectKey, String repoSlug,
                                        long pullRequestId, long commentId, String text) {
        //Wait at least one second between creating tasks to guarantee distinct timestamps across all databases
        //Yes, MySQL really does suck
        try {
            Thread.sleep(TimeUnit.SECONDS.toMillis(1));
        } catch (InterruptedException e) {
            //Ignore
        }

        return testContext.task(
                new TaskTestHelper.Builder(USER_TASK_CREATOR, USER_TASK_CREATOR, text)
                        .projectName(projectKey)
                        .repoSlug(repoSlug)
                        .pullRequestId(pullRequestId)
                        .commentId(commentId)
        );
    }

    protected static void defaultSetupProject(TestContext testContext) throws IOException {
        testContext
                .user(USER_PR_AUTHOR)
                .user(USER_REPO_ADMIN)
                .user(USER_PARTICIPANT)
                .user(USER_TASK_CREATOR)
                .user(USER_CANNOT_READ)
                .project(PROJECT_NAME);
    }

    protected static void defaultSetupRepository(TestContext testContext) throws IOException {
        defaultSetupProject(testContext);
        setupRepository(testContext, PROJECT_NAME, REPOSITORY_NAME);
    }

    protected static void setupRepository(TestContext testContext, String projectName, String repoName) throws IOException {
        testContext
                .repository(projectName, repoName, REPO_ZIP)
                .repositoryPermission(projectName, repoName, USER_PR_AUTHOR, Permission.REPO_WRITE)
                .repositoryPermission(projectName, repoName, USER_PARTICIPANT, Permission.REPO_READ)
                .repositoryPermission(projectName, repoName, USER_TASK_CREATOR, Permission.REPO_READ)
                .repositoryPermission(projectName, repoName, USER_REPO_ADMIN, Permission.REPO_ADMIN);
    }

    public static class PullRequestComment {

        public final Long commentId;
        public final Long pullRequestId;
        public final Integer repositoryId;

        public PullRequestComment(Long commentId, Long pullRequestId, Integer repositoryId) {
            this.commentId = commentId;
            this.pullRequestId = pullRequestId;
            this.repositoryId = repositoryId;
        }

        public String getGetUrl() {
            return DefaultFuncTestData.getRestURL() +
                    String.format("/projects/%s/repos/%s/pull-requests/%s/comments/%s",
                            PROJECT_NAME,
                            PROJECT_NAME,
                            pullRequestId,
                            commentId);
        }
    }
}
