package it.com.atlassian.stash.pull.notification;

import com.atlassian.bitbucket.content.DiffSegmentType;
import com.atlassian.bitbucket.permission.Permission;
import com.atlassian.bitbucket.smtp.MailTestUtils;
import com.atlassian.bitbucket.smtp.SmtpServer;
import com.atlassian.bitbucket.test.DefaultFuncTestData;
import com.atlassian.bitbucket.test.PullRequestRestTestHelper;
import com.atlassian.bitbucket.test.PullRequestTestHelper;
import com.atlassian.bitbucket.test.rest.comment.RestCommentUtils;
import com.jayway.restassured.RestAssured;
import it.com.atlassian.stash.pull.BasePullRequestFuncTest;
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.regex.Pattern;

import static com.atlassian.bitbucket.hamcrest.RegexMatcher.matchesRegex;
import static com.atlassian.bitbucket.test.PullRequestRestTestHelper.*;
import static java.lang.String.format;
import static javax.ws.rs.core.Response.Status.*;
import static org.apache.commons.lang3.StringEscapeUtils.escapeHtml4;

public abstract class BasePullRequestNotificationTest extends BasePullRequestFuncTest {

    protected static final String COMMENT_AUTHOR_USERNAME = "commentauthor";
    protected static final String COMMENT_AUTHOR_DISPLAY_NAME = "Comment Author Display Name";
    protected static final String COMMENT_AUTHOR_EMAIL_ADDRESS = COMMENT_AUTHOR_USERNAME + "@testdata.atlassian.com";
    protected static final String COMMENT_AUTHOR_EMAIL_MD5 = "9d26ebd220f86f6b47b4931f25cd9a9e";
    protected static final String IN_REPLY_TO_HEADER = "in-reply-to";
    protected static final String MENTION_USERNAME = "pr_mentionuser";
    protected static final String MENTION_USER_EMAIL = MENTION_USERNAME + "@testdata.atlassian.com";
    protected static final String MENTION_DISPLAY_NAME = "Mention Display Name";
    protected static final String PR_BRANCH = BRANCH_THAT_HAS_SAME_FILE_MODIFIED_AND_MOVED_SRC;
    protected static final String REFERENCES_HEADER = "references";

    protected long pullRequestId;
    protected SmtpServer smtpServer;

    @BeforeClass
    public static void configureMailServer() {
        getClassTestContext()
                .mailConfig(MailTestUtils.getSmtpConfiguration(), "testprnotifications@example.com");
    }

    @Before
    public void createTestData() {
        testContext
                .user(COMMENT_AUTHOR_USERNAME, COMMENT_AUTHOR_DISPLAY_NAME, COMMENT_AUTHOR_USERNAME)
                .user(MENTION_USERNAME, MENTION_DISPLAY_NAME, MENTION_USERNAME)
                .projectPermission(PROJECT_KEY, COMMENT_AUTHOR_USERNAME, Permission.PROJECT_READ)
                .projectPermission(PROJECT_KEY, MENTION_USERNAME, Permission.PROJECT_READ);

        pullRequestId = createPullRequest(new PullRequestBuilder(PR_BRANCH, MASTER).build()).getId();
    }

    @Before
    public void startSmtpServer() {
        smtpServer = MailTestUtils.createNewSMTPServer();
        smtpServer.start();
    }

    @After
    public void stopSmtpServer() throws Exception {
            smtpServer.stop();
    }

    protected static void addWatcher(long pullRequestId, String watcherUserName) {
        // Abusing the fact that the username/password are the same for the watcher user
        PullRequestRestTestHelper.addWatcher(PROJECT_KEY, REPO_SLUG, pullRequestId, watcherUserName, watcherUserName);
    }

    protected static void commentOnFileInPullRequest(long pullRequestId, String commentingUserName, String fileName, int lineNumber, String commentText) {
        // Right now, you can comment on an arbitrary line-number for a non-existent file...
        // Abusing the fact that the username/password are the same for the comment user
        RestAssured.expect()
                .spec(RestCommentUtils.createSpecBuilderWithNoReplies(CREATED, null, commentText, commentingUserName).build())
                .log().ifError()
                .given()
                .auth().preemptive().basic(commentingUserName, commentingUserName)
                .contentType("application/json")
                .body(RestCommentUtils.createLineBody(commentText, fileName, null, lineNumber, DiffSegmentType.ADDED))
                .post(getPullRequestCommentsURL(PROJECT_KEY, REPO_SLUG, pullRequestId));
    }

    protected static long commentOnPullRequest(long pullRequestId, String commentingUserName, String commentText) {
        // Abusing the fact that the username/password are the same for the comment user
        return createComment(PROJECT_KEY, REPO_SLUG, pullRequestId, commentingUserName, commentingUserName,
                RestCommentUtils.createBody(commentText), commentText);
    }

    protected static Matcher<String> containsMention(String prefix, String displayName) {
        return matchesRegex(prefix.replace("@%s", format("<[^>]+>%s</[^>]+>", Pattern.quote(escapeHtml4(displayName)))));
    }

    protected static void deleteCommentOnPullRequest(long pullRequestId, long commentId, String commentingUserName) {
        RestAssured
                .expect()
                .statusCode(NO_CONTENT.getStatusCode())
                .log().ifError()
                .given()
                .auth().preemptive().basic(commentingUserName, commentingUserName)
                .delete(getPullRequestCommentURL(PROJECT_KEY, REPO_SLUG, pullRequestId, commentId, 0));
    }

    protected static void editCommentOnPullRequest(long pullRequestId, long commentId, String commentingUserName, String newCommentText) {
        RestAssured
                .expect()
                .statusCode(OK.getStatusCode())
                .log().ifError()
                .given()
                .auth().preemptive().basic(commentingUserName, commentingUserName)
                .contentType("application/json")
                .body(RestCommentUtils.createBodyWithVersion(newCommentText, 0)) // we assume only one edit so hardcode version 0
                .put(getPullRequestCommentURL(PROJECT_KEY, REPO_SLUG, pullRequestId, commentId, 0));
    }

    protected static String formatDate(String pattern, long date) {
        SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
        dateFormat.setTimeZone(TimeZone.getDefault());
        return dateFormat.format(new Date(date));
    }

    protected static String getNotificationSettingsUrl() {
        return DefaultFuncTestData.getBaseURL() + "/plugins/servlet/notification/settings";
    }

    protected static PullRequestTestHelper getPullRequestTestHelper(String username, String password, long pullRequestId) {
        return PullRequestTestHelper.get(username, password, PROJECT_KEY, REPO_SLUG, pullRequestId);
    }

    protected static String getUnwatchUrl(String projectKey, String repoSlug, long pullRequestId) {
        return format("%s/projects/%s/repos/%s/pull-requests/%d?unwatch", DefaultFuncTestData.getBaseURL(), projectKey,
                repoSlug, pullRequestId);
    }

    protected static void mergePullRequest(long pullRequestId) {
        PullRequestTestHelper.mergePullRequest(AUTHOR_1, AUTHOR_1, PROJECT_KEY, REPO_SLUG, pullRequestId, 0);
    }

    protected static void reopenPullRequest(long pullRequestId) {
        PullRequestTestHelper.reopenPullRequest(AUTHOR_1, AUTHOR_1, PROJECT_KEY, REPO_SLUG, pullRequestId, 1);
    }

    protected static void removeWatcher(long pullRequestId, String watcherUserName) {
        PullRequestTestHelper pullRequest = getPullRequestTestHelper(watcherUserName, watcherUserName, pullRequestId);
        pullRequest.request()
                .log().ifValidationFails()
                .expect().statusCode(NO_CONTENT.getStatusCode())
                .delete(pullRequest.getWatchUrl());
    }

    protected static void replyToPullRequestComment(long pullRequestId, String commentingUserName, long commentId, String replyText) {
        // Abusing the fact that the username/password are the same for the comment user
        createComment(PROJECT_KEY, REPO_SLUG, pullRequestId, commentingUserName, commentingUserName,
                RestCommentUtils.createReplyBody(replyText, commentId), replyText);
    }
}
