package it.com.atlassian.stash;

import com.atlassian.fugue.Pair;
import com.atlassian.bitbucket.test.CommitCommentTestHelper;
import com.atlassian.webdriver.bitbucket.element.Comment;
import com.atlassian.webdriver.bitbucket.element.DiffCommentForm;
import com.atlassian.webdriver.bitbucket.element.SideBySideDiffFileContent;
import com.atlassian.webdriver.bitbucket.element.UnifiedDiffFileContent;
import com.atlassian.webdriver.bitbucket.page.CommitSideBySideDiffPage;
import com.atlassian.webdriver.bitbucket.page.CommitUnifiedDiffPage;
import it.com.atlassian.stash.rules.WebStorageCleanUpRule;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.openqa.selenium.By;
import org.openqa.selenium.StaleElementReferenceException;

import java.util.ArrayList;
import java.util.List;

import static com.atlassian.bitbucket.test.TestRuleOrderingSyntax.outerRule;
import static org.junit.Assert.*;

public class DiffCommentHidingTest extends BaseFileContentTest {

    private static final String COMMIT_HASH = "b4d378388c2dc3840ff5dcf64b4ac0f1ffc17cb9";

    @Override
    protected List<TestRule> getRetryRuleChain() {
        return outerRule(new WebStorageCleanUpRule()).around(super.getRetryRuleChain());
    }

    private CommitCommentTestHelper commentTestHelper = new CommitCommentTestHelper(getProjectKey(), getRepoSlug(), COMMIT_HASH);
    private List<Pair<Long, Integer>> comments;

    private UnifiedDiffFileContent unifiedDiffContent;
    private CommitUnifiedDiffPage unifiedDiffPage;

    @Before
    public void setup() {
        comments = new ArrayList<>();

        // setup a page with one comment on line 1
        unifiedDiffPage = loginAsAdmin(CommitUnifiedDiffPage.class, getProjectKey(), getRepoSlug(), COMMIT_HASH);
        unifiedDiffContent = unifiedDiffPage.getFileContent();
    }

    @After
    public void tearDown() throws Exception {
        for (Pair<Long, Integer> comment : comments) {
            commentTestHelper.deleteCommitCommentAndWait(comment.left(), comment.right());
        }
        comments.clear();
    }

    @Test
    public void unifiedLineCommentsShownByDefault() {
        addLineComments(1);
        assertCommentsVisible(unifiedDiffContent.getComments());
    }

    @Test
    public void unifiedLineCommentsCanBeHidden() {
        addLineComments(1);
        unifiedDiffPage.openDropdownSelector().toggleHideComments();

        assertCommentsHidden(unifiedDiffContent.getComments());
    }

    @Test
    public void unifiedFileCommentShownByDefault() {
        addFileComment();

        assertCommentsVisible(unifiedDiffContent.getComments());
    }

    @Test
    public void unifiedFileCommentCanBeHidden() {
        addFileComment();
        unifiedDiffPage.openDropdownSelector().toggleHideComments();

        assertCommentsHidden(unifiedDiffContent.getComments());
    }

    @Test
    public void unifiedNewCommentShowsAllComments() {
        addLineComments(1);
        unifiedDiffPage.openDropdownSelector().toggleHideComments();
        unifiedDiffContent.getDiffLines().get(2).find(By.cssSelector(".add-comment-trigger")).click();

        assertCommentsVisible(unifiedDiffContent.getComments());
    }

    @Test
    public void sbsLineCommentsShownByDefault() {
        addLineComments(1);
        CommitSideBySideDiffPage sbsPage = unifiedDiffPage.switchToSideBySide();
        SideBySideDiffFileContent sbsDiff = sbsPage.getFileContent();

        assertCommentsVisible(sbsDiff.getComments());
    }

    @Test
    public void sbsLineCommentsCanBeHidden() {
        addLineComments(1);
        CommitSideBySideDiffPage sbsPage = unifiedDiffPage.switchToSideBySide();
        SideBySideDiffFileContent sbsDiff = sbsPage.getFileContent();
        sbsPage.openDropdownSelector().toggleHideComments();

        assertCommentsHidden(sbsDiff.getComments());
    }

    @Test
    public void sbsFileCommentShownByDefault() {
        addFileComment();
        CommitSideBySideDiffPage sbsPage = unifiedDiffPage.switchToSideBySide();
        SideBySideDiffFileContent sbsDiff = sbsPage.getFileContent();

        assertCommentsVisible(sbsDiff.getComments());
    }

    @Test
    public void sbsFileCommentCanBeHidden() {
        addFileComment();
        CommitSideBySideDiffPage sbsPage = unifiedDiffPage.switchToSideBySide();
        SideBySideDiffFileContent sbsDiff = sbsPage.getFileContent();
        sbsPage.openDropdownSelector().toggleHideComments();

        assertCommentsHidden(sbsDiff.getComments());
    }

    @Test
    public void sbsNewCommentShowsAllComments() {
        addLineComments(1);
        CommitSideBySideDiffPage sbsPage = unifiedDiffPage.switchToSideBySide();
        SideBySideDiffFileContent sbsDiff = sbsPage.getFileContent();
        sbsPage.openDropdownSelector().toggleHideComments();

        sbsDiff.getFromLines().get(2).find(By.cssSelector(".add-comment-trigger")).click();

        assertCommentsVisible(unifiedDiffContent.getComments());
    }


    private void assertCommentsVisible(List<Comment> comments) {
        assertTrue("Assume there are some comments", comments.size() > 0);

        assertCommentsVisible(comments, comments.size());
    }

    private void assertCommentsVisible(List<Comment> comments, int numComments) {
        assertEquals(numComments, comments.size());
        for (Comment c : comments) {
            assertTrue(c.isVisible() && !unifiedDiffContent.find(
                    By.cssSelector("div.diff-view.hide-comments .comment[data-id=\"" + c.getId() + "\"]")).isPresent());
        }
    }

    private void assertCommentsHidden(List<Comment> comments) {
        for (Comment c : comments) {
            assertFalse(c.isVisible() && !unifiedDiffContent.find(
                    By.cssSelector("div.diff-view.hide-comments .comment[data-id=\"" + c.getId() + "\"]")).isPresent());
        }
    }

    private Comment addFileComment() {
        DiffCommentForm form = unifiedDiffContent.openNewFileCommentForm();
        form.setText("Test File Comment");
        Comment comment = form.submit();
        comments.add(new Pair<>(comment.getCommentId(), comment.getCommentVersion()));
        return comment;
    }

    private void addLineComments(int... lineNumbers) {
        List<UnifiedDiffFileContent.Line> lines = unifiedDiffContent.getDiffLines();

        int errorCount = 0;
        for (int i = 0; i < lineNumbers.length; i++) {
            UnifiedDiffFileContent.Line line = lines.get(lineNumbers[i]);
            // Often the click() function was failing because the element was no longer bound to the DOM.
            // This causes it to re-try after requesting the lines again.
            // This needs to be fixed at a page objects level rather than patching the test.
            try {
                line.getAddComment().click();
            } catch (StaleElementReferenceException e) {
                if (errorCount++ > 5) {
                    throw e;
                }
                lines = unifiedDiffContent.getDiffLines();
                i--;
                continue;
            }
            DiffCommentForm commentForm = line.getNewCommentForm();
            Comment comment = commentForm.setText("Test Comment").submit();
            comments.add(new Pair<>(comment.getCommentId(), comment.getCommentVersion()));
        }
    }
}
