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

import com.atlassian.pageobjects.elements.query.Poller;
import com.atlassian.webdriver.bitbucket.element.*;
import com.atlassian.webdriver.bitbucket.element.activity.ActivityItem;
import com.atlassian.webdriver.bitbucket.element.activity.DiffLineCommentActivity;
import com.atlassian.webdriver.bitbucket.element.activity.FileCommentActivity;
import com.atlassian.webdriver.bitbucket.page.PullRequestDiffPage;
import com.atlassian.webdriver.bitbucket.page.PullRequestOverviewPage;
import it.com.atlassian.stash.pull.BasePullRequestFuncTest;

import java.util.List;

import static com.atlassian.bitbucket.test.DefaultFuncTestData.getRepositoryURL;
import static com.atlassian.bitbucket.util.Chainable.chain;
import static it.com.atlassian.stash.BranchCompareTest.treeNode;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.*;

public abstract class BasePullRequestDiffPageTest extends BasePullRequestFuncTest {

    private static final int DRAFT_DEBOUNCE_WAIT = 300;

    protected void testPullRequestDiff(PullRequestDiffPage page) {
        List<FileTree.Node> nodes = page.getFileTree().getNodes();
        assertThat(nodes, hasSize(1));
        assertThat(nodes, hasItem(treeNode("Monthly_416.bak")));

        UnifiedDiffFileContent fileContent = page.getFileContent();

        UnifiedDiffFileContent.DiffToolbar toolbar = fileContent.getToolbar();
        assertThat(toolbar.getBreadCrumbs(), equalTo("Monthly_416.bak"));
        assertThat(toolbar.getChangeType(), equalToIgnoringCase("added"));
        assertThat(toolbar.getSourceLinkUrl(), equalTo(getRepositoryURL(PROJECT_KEY, REPO_SLUG) +
                "/browse/Monthly_416.bak?at=7549846524f8aed2bd1c0249993ae1bf9d3c9998"));

        List<UnifiedDiffFileContent.Line> diffLines = fileContent.getDiffLines();
        assertThat(diffLines, hasSize(1));

        UnifiedDiffFileContent.Line line = diffLines.get(0);
        assertThat(line.getSourceText(), startsWith("Autem placeat ducimus consequatur ipsum"));

        String message = "This is an inline comment";
        page.addComment(line, message);
        assertEquals(message, page.getFileContent().getComments().get(0).getCommentText());

        page.clickFileAndWaitUntilLoaded(nodes.get(0).getPath());

        List<FileTree.Node> nodes2 = page.getFileTree().getNodes();
        assertThat(nodes2, hasSize(1));
        assertThat(nodes2, hasItem(treeNode("Monthly_416.bak")));
    }

    protected void testPullRequestDiffFileComment(PullRequestDiffPage page) {
        List<FileTree.Node> nodes = page.getFileTree().getNodes();
        assertEquals(1, nodes.size());
        assertEquals(1, page.getFileContent().getDiffLines().size());

        String commentText = "This is a file comment";
        String commentReplyText = "Reply for " + commentText;

        UnifiedDiffFileContent diffFileContent = page.getFileContent();
        Comment comment = diffFileContent.openNewFileCommentForm().setText(commentText).submit();
        assertEquals(commentText, diffFileContent.getComments().get(0).getCommentText());

        comment.reply().setText(commentReplyText).submit();

        assertEquals(1, comment.getReplies().size());
        assertEquals(commentReplyText, comment.getReplies().get(0).getCommentText());
    }

    protected void testPullRequestDiffFileCommentActivityReply(PullRequestDiffPage page) {
        DiffCommentForm form = page.getFileContent().openNewFileCommentForm();
        form.setText("This is a file comment").submit();

        PullRequestOverviewPage overviewPage = page.switchToOverview();
        List<? extends ActivityItem> activityItems = overviewPage.getActivityItems();
        Comment comment = ((FileCommentActivity) activityItems.get(0)).getComment();
        comment.reply().setText("Reply to file comment").submit();
        comment = comment.edit().setText("Edited text").submit();
        assertEquals("Comment has edited text", "Edited text", comment.getCommentText());
        assertEquals("File comment has replies", 1, comment.getReplies().size());
        assertEquals("Reply to file comment", comment.getReplies().get(0).getCommentText());

        comment.getReplies().get(0).delete().confirmDelete();
        comment.delete().confirmDelete();
        ActivityItem firstItem = overviewPage.getActivityItems().get(0);
        assertFalse(firstItem instanceof FileCommentActivity);
    }

    protected void testPullRequestDiffFileCommentEditDraft(PullRequestDiffPage page) {
        // edit a comment without submitting
        DiffCommentForm form = page.getFileContent().openNewFileCommentForm();
        Comment newComment = form.setText("This is a file comment").submit();
        newComment.reply().setText("Reply to file comment").submit();
        editAndWaitForDraftToSave(newComment.edit(), "Edited text");

        // switch to overview and draft comment should be restored
        // reply comment should show up in activity
        PullRequestOverviewPage overviewPage = page.switchToOverview();
        List<? extends ActivityItem> activityItems = overviewPage.getActivityItems();
        Comment comment = ((FileCommentActivity) activityItems.get(0)).getComment();
        Comment.EditCommentForm editForm = comment.getEditForm();
        assertEquals("Edited draft is restored correctly", "Edited text", editForm.getText());
        editForm.cancel();

        assertEquals("Comment has original text", "This is a file comment", comment.getCommentText());
        assertEquals("File comment has replies", 1, comment.getReplies().size());
        assertEquals("Reply to file comment", comment.getReplies().get(0).getCommentText());
    }

    protected void testPullRequestDiffFileCommentNewDraft(PullRequestDiffPage page) {
        DiffCommentForm form = page.getFileContent().openNewFileCommentForm();
        editAndWaitForDraftToSave(form, "This is a file comment");

        // switch out of diff and back and draft is restored
        // TODO: when commit selector is ready, use that instead
        page = page.switchToOverview().switchToEffectiveDiff();
        form = page.getFileContent().getNewFileCommentForm();
        assertEquals("Draft should be restored correctly", "This is a file comment", form.getText());
        form.submit();

        Poller.waitUntilTrue("File node in file-tree should be marked as commented", page.getFileTree().getNodes()
                .get(1).isCommented());

        // switch to overview, file comment should be also there
        PullRequestOverviewPage overviewPage = page.switchToOverview();
        FileCommentActivity fileCommentActivity = chain(
                overviewPage.getActivitiesOfType(FileCommentActivity.class).now()).getFirst();
        Comment comment = fileCommentActivity.getComment();
        assertEquals("This is a file comment", comment.getCommentText());
        comment.delete().confirmDelete();
        Poller.waitUntil("File comment activity should be removed",
                overviewPage.getActivitiesOfType(FileCommentActivity.class), emptyIterable());
    }

    protected void testPullRequestDiffFileCommentPreview(PullRequestDiffPage page) {
        // Test preview for file comment
        DiffCommentForm fileCommentForm = page.getFileContent().openNewFileCommentForm();

        // try to preview it
        fileCommentForm.setText("File comment").togglePreview();
        assertTrue("Comment preview is not shown", fileCommentForm.isPreviewVisible());
        assertEquals("<p>File comment</p>\n", fileCommentForm.getPreviewContent());
        fileCommentForm.togglePreview();
        assertFalse("Comment preview should not be visible", fileCommentForm.isPreviewVisible());
    }

    protected void testPullRequestDiffFileCommentReplyEditAndDelete(PullRequestDiffPage page) {
        DiffCommentForm commentForm = page.getFileContent().openNewFileCommentForm();
        String fileComment = "File comment";

        Comment comment = commentForm.setText(fileComment).submit();
        assertEquals(fileComment, comment.getCommentText());

        Comment.ReplyCommentForm replyForm = comment.reply();
        String reply = " reply";
        replyForm.setText(fileComment + reply);
        Comment replyComment = replyForm.submit();
        assertEquals("The file commnent should have a reply",
                fileComment + reply, replyComment.getCommentText());

        String editText = " edit";
        Comment.EditCommentForm edit = replyComment.edit();
        assertEquals("The file comment should have been edited",
                fileComment + editText, edit.setText(fileComment + editText).submit().getCommentText());

        replyComment.delete().confirmDelete();
        assertFalse("The file comment should have been deleted",
                page.getFileContent().getDiffLines().get(0).getComment(0).hasReplies());
    }

    protected void testPullRequestDiffInlineCommentDrafts(PullRequestDiffPage page) {
        UnifiedDiffFileContent.Line line = page.getFileContent().getDiffLines().get(0);
        page.openNewComment(line, "Inline comment");
        waitForDraftToSave();

        // switch out of diff and back and check draft
        // TODO: when commit selector is ready, use that instead
        page = page.switchToCommits().switchToEffectiveDiff();
        DiffCommentForm commentForm = page.getFileContent().getDiffLines().get(0).getNewCommentForm();
        assertEquals("Draft should be restored", "Inline comment", commentForm.getText());
        Comment comment = commentForm.submit();
        assertEquals("Inline comment", comment.getCommentText());

        // edit the comment and check the edit draft is restored on activity page
        editAndWaitForDraftToSave(comment.edit(), "Edited comment");
        PullRequestOverviewPage overviewPage = page.switchToOverview();
        DiffLineCommentActivity commentActivity = (DiffLineCommentActivity) overviewPage.getActivityItems().get(0);
        comment = commentActivity.getFileContent().getComments().get(0);

        // submit edited comment draft on activity
        Comment.EditCommentForm editForm = comment.getEditForm();
        assertEquals("Draft edited comment should be restored", "Edited comment", editForm.getText());
        comment = editForm.submit();
        assertEquals("Edited comment", comment.getCommentText());

        // add a reply as draft and check it gets restored on diff
        editAndWaitForDraftToSave(comment.reply(), "Draft reply comment");
        page = overviewPage.switchToEffectiveDiff();
        Comment.ReplyCommentForm replyForm = page.getFileContent().getComments().get(0).getReplyCommentForm();
        assertEquals("Draft reply comment should be restored", "Draft reply comment", replyForm.getText());
    }

    protected void testPullRequestDiffInlineCommentPreview(PullRequestDiffPage page) {
        // open a new diff comment
        UnifiedDiffFileContent.Line line = page.getFileContent().getDiffLines().get(0);
        String inlineComment = "Inline comment";
        DiffCommentForm diffCommentForm = page.openNewComment(line, inlineComment);

        // try to preview it
        diffCommentForm.togglePreview();
        assertTrue("comment preview is not shown", diffCommentForm.isPreviewVisible());
        assertEquals("<p>Inline comment</p>\n", diffCommentForm.getPreviewContent());
        diffCommentForm.togglePreview();
        assertFalse("comment preview should not be visible", diffCommentForm.isPreviewVisible());

        // check the inline comment can be correctly submitted after toggling on and off the preview
        Comment comment = diffCommentForm.submit();
        assertEquals("Comment text should be same as entered", inlineComment, comment.getCommentText());
        assertEquals("Comment author should be user who submitted form", AUTHOR_1_DISPLAY_NAME, comment.getAuthor());
    }

    protected void testPullRequestDiffInlineCommentReplyEditAndDelete(PullRequestDiffPage page) {
        UnifiedDiffFileContent.Line line = page.getFileContent().getDiffLines().get(0);
        String inlineComment = "Inline comment";

        Comment comment = page.openNewComment(line, inlineComment).submit();
        assertEquals(inlineComment, comment.getCommentText());

        Comment.ReplyCommentForm replyForm = comment.reply();
        String reply = " reply";
        replyForm.setText(inlineComment + reply);
        Comment replyComment = replyForm.submit();
        assertEquals("The inline commnent should have a reply",
                inlineComment + reply, replyComment.getCommentText());

        String editText = " edit";
        Comment.EditCommentForm edit = replyComment.edit();
        assertEquals("The inline comment should have been edited",
                inlineComment + editText, edit.setText(inlineComment + editText).submit().getCommentText());

        replyComment.delete().confirmDelete();
        assertFalse("The inline comment should have been deleted",
                page.getFileContent().getDiffLines().get(0).getComment(0).hasReplies());
    }

    protected void testPullRequestDiffShowsBinaryFile(PullRequestDiffPage page, boolean conflicted) {
        List<FileTree.Node> nodes = page.getFileTree().getNodes();

        assertEquals(1, nodes.size());
        assertThat(nodes.get(0).isConflicted(), is(conflicted));
    }

    private static void editAndWaitForDraftToSave(CommentForm<?> comment, String text) {
        comment.setText(text);
        waitForDraftToSave();
    }

    private static void waitForDraftToSave() {
        // TODO: restore hope (BSERVDEV-12299)
        // yes I am doing this. All hope is lost
        try {
            Thread.sleep(DRAFT_DEBOUNCE_WAIT + 5); // wait just a little bit longer than the delay in the draft saving code
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}
