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

import com.atlassian.bitbucket.rest.task.RestTask;
import com.atlassian.bitbucket.task.TaskAnchorType;
import com.atlassian.bitbucket.task.TaskState;
import com.atlassian.bitbucket.test.PullRequestTestHelper;
import com.atlassian.bitbucket.test.TaskTestHelper;
import com.jayway.restassured.RestAssured;
import com.jayway.restassured.http.ContentType;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import static com.atlassian.bitbucket.hamcrest.MoreMatchers.longEqual;
import static javax.ws.rs.core.Response.Status.*;
import static org.hamcrest.Matchers.*;

public class TaskCreateResourceTest extends AbstractTaskResourceTest {

    private static PullRequestTestHelper pullRequest;

    @BeforeClass
    public static void setupRepository() throws Exception {
        defaultSetupRepository(getClassTestContext());
        pullRequest = getClassTestContext().pullRequest(
                new PullRequestTestHelper.Builder(
                        USER_PR_AUTHOR, USER_PR_AUTHOR,
                        PR_TITLE, PR_TITLE,
                        PROJECT_NAME, PROJECT_NAME, SOURCE_BRANCH,
                        PROJECT_NAME, PROJECT_NAME, TARGET_BRANCH));
    }


    @Test
    public void testPullRequestAuthorCanCreateTask() throws Exception {
        PullRequestComment comment = createComment(pullRequest);

        RestAssured
            .given()
                .auth().preemptive().basic(USER_PR_AUTHOR, USER_PR_AUTHOR)
                .body(createTaskBody(comment.commentId, TASK_TEXT))
                .contentType(ContentType.JSON)
            .when()
                .post(TaskTestHelper.getCreateUrl())
            .then()
                .log().ifValidationFails()
                .statusCode(CREATED.getStatusCode())
                .body(ID, greaterThanOrEqualTo(0))
                .body(STATE, equalTo(TaskState.OPEN.name()))
                .body(TEXT, equalTo(TASK_TEXT))
                .body(ANCHOR_ID, longEqual(comment.commentId))
                .body(ANCHOR_TYPE, equalTo(TaskAnchorType.COMMENT.name()))
                .body(CREATED_BY, equalTo(USER_PR_AUTHOR))
                .body(CREATED_DATE, notNullValue());
    }

    @Test
    public void testReaderCanCreateTask() throws Exception {
        PullRequestComment comment = createComment(pullRequest);

        RestAssured
            .given()
                .auth().preemptive().basic(USER_PARTICIPANT, USER_PARTICIPANT)
                .body(createTaskBody(comment.commentId, TASK_TEXT))
                .contentType(ContentType.JSON)
            .when()
                .post(TaskTestHelper.getCreateUrl())
            .then()
                .log().ifValidationFails()
                .statusCode(CREATED.getStatusCode())
                .body(ID, notNullValue())
                .body(STATE, equalTo(TaskState.OPEN.name()))
                .body(TEXT, equalTo(TASK_TEXT))
                .body(ANCHOR_ID, longEqual(comment.commentId))
                .body(CREATED_BY, equalTo(USER_PARTICIPANT))
                .body(CREATED_DATE, notNullValue());
    }

    @Test
    public void testCannotCreateTaskWithoutText() throws Exception {
        PullRequestComment comment = createComment(pullRequest);

        RestAssured
                .given()
                .auth().preemptive().basic(USER_PARTICIPANT, USER_PARTICIPANT)
                .body(createTaskBody(comment.commentId, null))
                .contentType(ContentType.JSON)
                .when()
                .post(TaskTestHelper.getCreateUrl())
                .then()
                .log().ifValidationFails()
                .statusCode(BAD_REQUEST.getStatusCode())
                .body(FIRST_ERROR_MESSAGE, equalTo("A text must be provided when adding a task."));
    }

    @Test
    public void testCannotCreateTaskWithWhiteSpaceOnlyText() throws Exception {
        PullRequestComment comment = createComment(pullRequest);

        RestAssured
            .given()
                .auth().preemptive().basic(USER_PARTICIPANT, USER_PARTICIPANT)
                .body(createTaskBody(comment.commentId, " \n"))
                .contentType(ContentType.JSON)
            .when()
                .post(TaskTestHelper.getCreateUrl())
            .then()
                .log().ifValidationFails()
                .statusCode(BAD_REQUEST.getStatusCode())
                .body(FIRST_ERROR_MESSAGE, equalTo("A text must be provided when adding a task."));
    }

    @Test
    public void testCannotCreateTaskWithEmptyText() throws Exception {
        PullRequestComment comment = createComment(pullRequest);

        RestAssured
            .given()
                .auth().preemptive().basic(USER_PARTICIPANT, USER_PARTICIPANT)
                .body(createTaskBody(comment.commentId, ""))
                .contentType(ContentType.JSON)
            .when()
                .post(TaskTestHelper.getCreateUrl())
            .then()
                .log().ifValidationFails()
                .statusCode(BAD_REQUEST.getStatusCode())
                .body(FIRST_ERROR_MESSAGE, equalTo("A text must be provided when adding a task."));
    }

    @Test
    public void testCannotCreateTaskWithoutAnchor() throws Exception {
        RestAssured
            .given()
                .auth().preemptive().basic(USER_PARTICIPANT, USER_PARTICIPANT)
                .body(createTaskBody(null, TASK_TEXT))
                .contentType(ContentType.JSON)
            .when()
                .post(TaskTestHelper.getCreateUrl())
            .then()
                .log().ifValidationFails()
                .statusCode(BAD_REQUEST.getStatusCode())
                .body(FIRST_ERROR_MESSAGE, equalTo("A task anchor must be provided when adding a task."));
    }

    @Test
    public void testCannotCreateTaskWithInvalidAnchor() throws Exception {
        RestAssured
            .given()
                .auth().preemptive().basic(USER_PARTICIPANT, USER_PARTICIPANT)
                .body(createTaskBody(Long.MAX_VALUE, TASK_TEXT))
                .contentType(ContentType.JSON)
            .when()
                .post(TaskTestHelper.getCreateUrl())
            .then()
                .log().ifValidationFails()
                .statusCode(NOT_FOUND.getStatusCode())
                .body(FIRST_ERROR_MESSAGE, equalTo("The requested comment does not exist."));
    }

    @Test
    public void testCannotCreateTaskWithTaskId() throws Exception {
        PullRequestComment comment = createComment(pullRequest);

        Map<String, Serializable> taskBody = createTaskBody(comment.commentId, TASK_TEXT);
        taskBody.put(RestTask.ID, Long.MAX_VALUE);

        RestAssured
            .given()
                .auth().preemptive().basic(USER_PARTICIPANT, USER_PARTICIPANT)
                .body(taskBody)
                .contentType(ContentType.JSON)
            .when()
                .post(TaskTestHelper.getCreateUrl())
            .then()
                .log().ifValidationFails()
                .statusCode(BAD_REQUEST.getStatusCode())
                .body(FIRST_ERROR_MESSAGE, equalTo("Task ID must not be provided while adding a task."));
    }

    @Test
    public void testAnonymousUserCannotCreateTask() throws Exception {
        PullRequestComment comment = createComment(pullRequest);

        RestAssured
            .given()
                .body(createTaskBody(comment.commentId, TASK_TEXT))
                .contentType(ContentType.JSON)
            .when()
                .post(TaskTestHelper.getCreateUrl())
            .then()
                .log().ifValidationFails()
                .statusCode(UNAUTHORIZED.getStatusCode())
                .body("message", equalTo("You are not permitted to access this resource"));
    }

    @Test
    public void testCannotCreateTaskWhenUnableToSeePullRequest() throws Exception {
        PullRequestComment comment = createComment(pullRequest);

        RestAssured
            .given()
                .auth().preemptive().basic(USER_CANNOT_READ, USER_CANNOT_READ)
                .body(createTaskBody(comment.commentId, TASK_TEXT))
                .contentType(ContentType.JSON)
            .when()
                .post(TaskTestHelper.getCreateUrl())
            .then()
                .log().ifValidationFails()
                .statusCode(NOT_FOUND.getStatusCode())
                .body(FIRST_ERROR_MESSAGE, equalTo("There is no task context with ID " + pullRequest.getId()));
    }

    @Test
    public void testCreatingPreservesTextAsPlainButStripsWhiteSpace() throws Exception {
        String wikiText = " this is some\n" +
                "\n" +
                "``` \n" +
                "text in markdown\n" +
                "```\n";
        String plainText = "this is some ``` text in markdown ```";
        PullRequestComment comment = createComment(pullRequest);

        RestAssured
            .given()
                .auth().preemptive().basic(USER_PARTICIPANT, USER_PARTICIPANT)
                .body(createTaskBody(comment.commentId, wikiText))
                .contentType(ContentType.JSON)
            .when()
                .post(TaskTestHelper.getCreateUrl())
            .then()
                .log().ifValidationFails()
                .statusCode(CREATED.getStatusCode())
                .body(TEXT, equalTo(plainText));
    }

    @Test
    @Ignore("Stash's PullRequestService does not check that a comment is within the provided pull request")
    public void testCannotCreateTaskOnCommentNotInPullRequest() throws Exception {
        PullRequestComment comment = createComment(pullRequest);
        PullRequestTestHelper pullRequest2 = testContext.pullRequest(
                new PullRequestTestHelper.Builder(
                        USER_PR_AUTHOR, USER_PR_AUTHOR,
                        PR_TITLE + '2', PR_TITLE + '2',
                        PROJECT_NAME, PROJECT_NAME, TARGET_BRANCH,
                        PROJECT_NAME, PROJECT_NAME, SOURCE_BRANCH));

        try {
            RestAssured
                .given()
                    .auth().preemptive().basic(USER_PARTICIPANT, USER_PARTICIPANT)
                    .body(createTaskBody(comment.commentId, TASK_TEXT))
                    .contentType(ContentType.JSON)
                .when()
                    .post(TaskTestHelper.getCreateUrl())
                .then()
                    .log().ifValidationFails()
                    .statusCode(BAD_REQUEST.getStatusCode())
                    .body(FIRST_ERROR_MESSAGE, equalTo("TODO")); // TODO error message
        } finally {
            pullRequest2.declineSafe();
        }
    }

    @Test
    public void testCanCreateMultipleTasksPerComment() throws Exception {
        PullRequestComment comment = createComment(pullRequest);

        for (int i = 0; i < 3; i++) {
            String text = TASK_TEXT + i;
            RestAssured
                .given()
                    .auth().preemptive().basic(USER_PARTICIPANT, USER_PARTICIPANT)
                    .body(createTaskBody(comment.commentId, text))
                    .contentType(ContentType.JSON)
                .when()
                    .post(TaskTestHelper.getCreateUrl())
                .then()
                    .log().ifValidationFails()
                    .statusCode(CREATED.getStatusCode())
                    .body(TEXT, equalTo(text))
                    .body(ANCHOR_ID, longEqual(comment.commentId))
                    .body(CREATED_BY, equalTo(USER_PARTICIPANT))
                    .body(CREATED_DATE, notNullValue());
        }
    }

    private Map<String, Serializable> createTaskBody(Long commentId, String taskText) {
        Map<String, Serializable> task = new HashMap<>();
        if (taskText != null) {
            task.put(RestTask.TEXT, taskText);
        }
        if (commentId != null) {
            Map<String, Serializable> anchor = new HashMap<>();
            anchor.put(RestTask.ID, commentId);
            anchor.put(RestTask.ANCHOR_TYPE, TaskAnchorType.COMMENT);
            task.put(RestTask.ANCHOR, (Serializable) anchor);
        }
        return task;
    }


}
