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

import com.atlassian.bitbucket.permission.Permission;
import com.atlassian.bitbucket.test.AnalyticsTestHelper;
import com.atlassian.bitbucket.test.PullRequestCommentLikeHelper;
import com.atlassian.bitbucket.test.PullRequestTestHelper;
import com.atlassian.pageobjects.elements.query.Poller;
import com.atlassian.webdriver.bitbucket.element.Comment;
import com.atlassian.webdriver.bitbucket.element.LikesInfoDialog;
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.util.Map;
import java.util.function.Function;

import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasKey;
import static org.junit.Assert.*;

public abstract class BasePullRequestCommentLikeTest extends BasePullRequestFuncTest {

    protected static final String USER_1 = "pr_user1";
    protected static final String USER_2 = "pr_user2";

    protected long commentId;
    protected long pullRequestId;

    private PullRequestCommentLikeHelper likeHelper;

    @BeforeClass
    public static void setupClass() {
        getClassTestContext()
                .user(USER_1).repositoryPermission(PROJECT_KEY, REPO_SLUG, USER_1, Permission.REPO_WRITE)
                .user(USER_2).repositoryPermission(PROJECT_KEY, REPO_SLUG, USER_2, Permission.REPO_WRITE);
    }

    @Before
    public void enableAnalytics() {
        AnalyticsTestHelper.enableEventLogging();
    }

    protected void setUp(Function<PullRequestTestHelper, Long> addCommentAndGetId) {
        PullRequestTestHelper pullRequest = createPullRequest(new PullRequestBuilder(
                BRANCH_THAT_HAS_SAME_FILE_MODIFIED_AND_MOVED_SRC, BRANCH_THAT_HAS_SAME_FILE_MODIFIED_AND_MOVED_TRGT)
                .build());

        commentId = addCommentAndGetId.apply(pullRequest);
        pullRequestId = pullRequest.getId();
        likeHelper = new PullRequestCommentLikeHelper(PROJECT_KEY, REPO_SLUG, pullRequestId);

        AnalyticsTestHelper.enableEventLogging();
    }

    @After
    public void tearDown() {
        AnalyticsTestHelper.disableEventLogging();
    }

    protected void testAuthorCannotSeeLikeButton(Function<String, Comment> loginAndGetComment) {
        Comment comment = loginAndGetComment.apply(AUTHOR_1);

        Poller.waitUntilFalse("The author should not be able to see the like button", comment.canLike());
    }

    protected void testLikingFromDeepLink(Function<String, Comment> loginAndGetComment) {
        Comment comment = loginAndGetComment.apply(USER_1);
        assertFalse("Ensure that the button is a unlike button", comment.hasLikeButton());

        // Check analytics events are fired
        Map<String, Object> event = AnalyticsTestHelper.findWhitelistedEvent("stash.client.comment.actions.like");
        assertThat(event, hasKey("properties"));
        assertThat(event, hasEntry(equalTo("properties"), (Matcher) hasKey("pullRequest.id")));
        assertThat(event, hasEntry(equalTo("properties"), (Matcher) hasKey("repository.id")));
        assertThat(event, hasEntry(equalTo("properties"), (Matcher) hasKey("comment.id")));

        comment = loginAndGetComment.apply(USER_1);
        assertFalse("Comment should not be unliked", comment.hasLikeButton());

        // Check the user can unlike the comment
        comment.toggleLike();
        assertTrue("Comment should be unliked", comment.hasLikeButton());
    }

    protected void testInfoDialogThreeLikers(Function<String, Comment> loginAndGetComment) {
        likeCommentAs(USER_1);
        likeCommentAs(USER_2);
        likeCommentAs(REVIEWER_1);

        Comment comment = loginAndGetComment.apply(REVIEWER_1);
        LikesInfoDialog likesDialog = comment.openLikeDialog();

        assertEquals("There should be two users in the like dialog", 2, likesDialog.getNumberOfUsers());
        assertEquals("The first user should be " + USER_1, USER_1, likesDialog.getUserName(0));
        assertEquals("The second user should be " + USER_2, USER_2, likesDialog.getUserName(1));
    }

    protected void testInfoDialogTwoLikers(Function<String, Comment> loginAndGetComment) {
        likeCommentAs(USER_1);
        likeCommentAs(USER_2);

        Comment comment = loginAndGetComment.apply(REVIEWER_1);
        LikesInfoDialog likesDialog = comment.openLikeDialog();

        assertEquals("There should be two users in the like dialog", 2, likesDialog.getNumberOfUsers());
        assertEquals("The first user should be " + USER_1, USER_1, likesDialog.getUserName(0));
        assertEquals("The second user should be " + USER_2, USER_2, likesDialog.getUserName(1));
    }

    protected void testOnePersonLiking(Function<String, Comment> loginAndGetComment) {
        Comment comment = loginAndGetComment.apply(USER_1);
        assertTrue("The like button should be visible", comment.hasLikeButton());

        comment.toggleLike();
        assertFalse("The like button should not be visible", comment.hasLikeButton());
        assertEquals("The like info should change for the current user after liking", "You like this", comment.getLikeInfo());

        comment = loginAndGetComment.apply(AUTHOR_1);
        assertEquals("The comment author should see different like info", USER_1 + " likes this", comment.getLikeInfo());
    }

    protected void testOnePersonUnliking(Function<String, Comment> loginAndGetComment) {
        likeCommentAs(USER_1);

        Comment comment = loginAndGetComment.apply(USER_1);
        assertFalse("The like button should not be visible", comment.hasLikeButton());
        assertEquals("The like info should state the current user has liked this comment", "You like this", comment.getLikeInfo());

        comment.toggleLike();
        assertTrue("The like button should now be visible", comment.hasLikeButton());
    }

    protected void testTwoPeopleLiking(Function<String, Comment> loginAndGetComment) {
        likeCommentAs(USER_1);
        likeCommentAs(USER_2);

        Comment comment = loginAndGetComment.apply(USER_1);
        assertFalse("The like button should not be visible", comment.hasLikeButton());
        assertEquals("Ensure that info changes for liker", "You and " + USER_2 + " like this", comment.getLikeInfo());

        comment = loginAndGetComment.apply(AUTHOR_1);
        assertEquals("The comment author should see different like info", "2 people like this", comment.getLikeInfo());
    }

    protected void likeCommentAs(String user) {
        likeHelper.likeComment(commentId, user);
    }
}
