package it.com.atlassian.stash.commit;

import com.atlassian.bitbucket.commit.SimpleMinimalCommit;
import com.atlassian.bitbucket.hamcrest.EmailMatcher;
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.rest.comment.RestCommentUtils;
import com.jayway.restassured.RestAssured;
import org.apache.commons.lang3.mutable.MutableLong;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import static javax.ws.rs.core.Response.Status.CREATED;
import static org.apache.commons.lang3.StringEscapeUtils.escapeHtml4;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.containsString;

public class CommitDiscussionNotificationTest extends BaseCommitDiscussionFuncTest {

    private static final String COMMENT_AUTHOR_USERNAME = "commentAuthor";
    private static final String COMMENT_AUTHOR_EMAIL = COMMENT_AUTHOR_USERNAME + "@testdata.atlassian.com";
    private static final String COMMENT_AUTHOR_DISPLAY_NAME = "Comment Author Display Name";
    private static final String COMMENT_AUTHOR_EMAIL_MD5 = "9d26ebd220f86f6b47b4931f25cd9a9e";

    private SmtpServer smtpServer;

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

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

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

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

    @Test
    public void testCommentAdded() throws Exception {
        String commitId = "6053a1eaa1c009dd11092d09a72f3c41af1b59ad";
        String comment = "This file should be the other way around";

        comment(COMMENT_AUTHOR_USERNAME, commitId, comment);

        checkEmail(commitId, COMMIT_EMAIL, comment);
    }

    @Test
    public void testCommentAddedMention() throws Exception {
        String commitId = "0a943a29376f2336b78312d99e65da17048951db";
        String comment = "This file should be the other way around";

        comment(COMMENT_AUTHOR_USERNAME, commitId, comment + " @" + COMMIT_USER);

        checkEmail(commitId, COMMIT_EMAIL, comment + " " + COMMIT_NAME);
    }

    @Test
    public void testCommentReplied() throws Exception {
        String commitId = "0a943a29376f2336b78312d99e65da17048951db";
        String comment = "This file should be the other way around";

        long commentId = comment(COMMENT_AUTHOR_USERNAME, commitId, comment);

        String replyComment = "Yeah maybe you are right";
        commentReply(COMMIT_USER, commitId, replyComment, commentId);

        checkEmail(commitId, COMMENT_AUTHOR_EMAIL, replyComment);
    }

    @Test
    public void testCommentRepliedMention() throws Exception {
        String commitId = "0a943a29376f2336b78312d99e65da17048951db";
        String comment = "This file should be the other way around";

        long commentId = comment(COMMENT_AUTHOR_USERNAME, commitId, comment);

        String replyComment = "Yeah maybe you are right";
        commentReply(COMMENT_AUTHOR_USERNAME, commitId, replyComment + " @" + COMMIT_USER, commentId);

        checkEmail(commitId, COMMIT_EMAIL, replyComment + " " + COMMIT_NAME);
    }

    private Long comment(String user, String commitId, String comment) {
        MutableLong commentId = new MutableLong();
        RestAssured.expect()
                .spec(RestCommentUtils.createSpecBuilderWithNoReplies(CREATED, commentId, comment, user).build())
                .log().ifError()
                .given()
                .auth().preemptive().basic(user, user)
                .contentType("application/json")
                .body(RestCommentUtils.createFileBody(comment, "add_file/add_file.txt", null))
                .post(RestCommentUtils.getCommitDiscussionCommentsUrl(PROJECT_KEY, REPO_SLUG, commitId));
        return commentId.longValue();
    }

    private Long commentReply(String user, String commitId, String comment, long parentId) {
        MutableLong commentId = new MutableLong();
        RestAssured.expect()
                .spec(RestCommentUtils.createSpecBuilderWithNoReplies(CREATED, commentId, comment, user).build())
                .log().ifError()
                .given()
                .auth().preemptive().basic(user, user)
                .contentType("application/json")
                .body(RestCommentUtils.createReplyBody(comment, parentId))
                .post(RestCommentUtils.getCommitDiscussionCommentsUrl(PROJECT_KEY, REPO_SLUG, commitId));
        return commentId.longValue();
    }

    private void checkEmail(String commitId, String email, String comment) {
        String shortCommitId = commitId.substring(0, SimpleMinimalCommit.DEFAULT_DISPLAY_ID_LENGTH);

        smtpServer.waitForEmail(new EmailMatcher()
                .addressedTo(email)
                .withSubject(allOf(
                        containsString(PROJECT_KEY),
                        containsString(REPO_SLUG),
                        containsString(shortCommitId)))
                .withBodyAsText(containsString(comment))
                .withBody(allOf(
                        containsString(getCommitViewURL(PROJECT_KEY, REPO_SLUG, commitId)),
                        containsString(getCommitUnwatchURL(PROJECT_KEY, REPO_SLUG, commitId)),
                        containsString(escapeHtml4(COMMENT_AUTHOR_DISPLAY_NAME)),
                        containsString("gravatar.com/avatar/" + COMMENT_AUTHOR_EMAIL_MD5 + ".jpg"))));
    }

    public static String getCommitViewURL(String projectKey, String repoSlug, String id) {
        return DefaultFuncTestData.getBaseURL() + "/projects/" + projectKey + "/repos/" + repoSlug + "/commits/" + id;
    }

    public static String getCommitUnwatchURL(String projectKey, String repoSlug, String id) {
        return getCommitViewURL(projectKey, repoSlug, id) + "?unwatch";
    }
}
