package it.com.atlassian.stash;

import com.atlassian.bitbucket.hamcrest.SimpleTypeSafeMatcher;
import com.atlassian.webdriver.bitbucket.element.*;
import com.atlassian.webdriver.bitbucket.page.BranchListPage;
import com.atlassian.webdriver.bitbucket.page.PullRequestCreatePage;
import com.atlassian.webdriver.bitbucket.page.compare.CommitsComparePage;
import com.atlassian.webdriver.bitbucket.page.compare.DiffComparePage;
import org.hamcrest.Matcher;
import org.junit.Test;

import java.util.List;
import java.util.regex.Pattern;

import static com.atlassian.bitbucket.test.DefaultFuncTestData.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;

public class BranchCompareTest extends BaseBrowserFuncTest {

    @Test
    public void testHappyPath() {
        BranchListPage branchListPage = loginAsAdmin(BranchListPage.class, getProject1(), getProject1Repository1());

        CommitsComparePage commitsTab = branchListPage.getBranchList().getDefaultBranchRow().click();

        assertEquals("master", commitsTab.getSourceTargetSelector().getSourceBranchSelector().getSelectedItemName());
        assertEquals("master", commitsTab.getSourceTargetSelector().getTargetBranchSelector().getSelectedItemName());
        assertThat(commitsTab.isPullRequestButtonEnabled(), equalTo(false));

        commitsTab.getSourceTargetSelector().getSourceBranchSelector().open().selectTags().selectItemByName("backdated_annotated_tag");
        assertThat(commitsTab.isPullRequestButtonEnabled(), equalTo(false));

        commitsTab.getSourceTargetSelector().getSourceBranchSelector().open().selectBranches().selectItemByName("basic_branching");
        assertThat(commitsTab.isPullRequestButtonEnabled(), equalTo(true));

        assertThat(commitsTab.getSourceBranchFromUrl(), equalTo("refs/heads/basic_branching"));
        assertThat(commitsTab.getTargetBranchFromUrl(), equalTo("refs/heads/master"));

        // commit tab
        List<CommitList.CommitListEntry> commits = commitsTab.getCommits().getRows();
        assertThat(commits, hasSize(1));
        assertThat(commits.get(0), is(commit("Michael Heemskerk", "d6edcbf9246", "a modification on branch basic_branching")));

        // diff tab
        DiffComparePage diffTab = commitsTab.switchTab();
        assertThat(diffTab.hasCommentsSwitch(), equalTo(false));
        FileTree tree = diffTab.getTree();
        assertThat(tree.getNodes(), hasSize(2));
        assertThat(tree.getNodes(), hasItem(/* dir node */treeNode("")));
        assertThat(tree.getNodes(), hasItem(treeNode("basic_branching/file.txt")));
        UnifiedDiffFileContent diff = diffTab.getDiff();
        assertThat(diff.getDiffLines(), hasSize(2));
        assertThat(diff.getDiffLines(), hasItem(diffLine(1, "basic branching scenario", false)));
        assertThat(diff.getDiffLines(), hasItem(diffLine(1, "a modification on branch basic_branching", true)));
        UnifiedDiffFileContent.DiffToolbar toolbar = diff.getToolbar();
        assertThat(toolbar.getBreadCrumbs(), equalTo("basic_branching/file.txt"));
        assertThat(toolbar.getChangeType(), equalToIgnoringCase("modified"));
        assertThat(toolbar.getSourceLinkUrl(), equalTo(getRepositoryURL(getProject1(), getProject1Repository1()) +
                "/browse/basic_branching/file.txt?at=d6edcbf924697ab811a867421dab60d954ccad99"));

        PullRequestCreatePage pullRequestCreatePage = commitsTab.createPullRequest();
        assertTrue(pullRequestCreatePage.isShowingPRForm());
        assertEquals("basic_branching", pullRequestCreatePage.getCollapsedSourceBranch().getBranch());
        assertEquals("master", pullRequestCreatePage.getCollapsedTargetBranch().getBranch());
    }

    @Test
    public void testBranchSwap() throws Exception {
        BranchListPage branchListPage = loginAsAdmin(BranchListPage.class, getProject1(), getProject1Repository1());

        CommitsComparePage commitsTab = branchListPage.getBranchList().filter("basic_branching").getFocusedRow().click();
        commitsTab.getSourceTargetSelector().getTargetBranchSelector().open().selectItemByName("branch_mod_merge");

        commitsTab = commitsTab.clickSwapBranches();

        assertThat(commitsTab.getSourceBranchFromUrl(), equalTo("refs/heads/branch_mod_merge"));
        assertThat(commitsTab.getTargetBranchFromUrl(), equalTo("refs/heads/basic_branching"));

        List<CommitList.CommitListEntry> commits = commitsTab.getCommits().getRows();
        assertThat(commits, hasSize(2));
        assertThat(commits.get(0), is(commit("Michael Heemskerk", "6053a1eaa1c", "modification on branch_mod_merge")));
        assertThat(commits.get(1), is(commit("Michael Heemskerk", "042fb0cc198", "branch, mod and merge scenario")));

        DiffComparePage diffTab = commitsTab.switchTab();
        FileTree tree = diffTab.getTree();
        assertThat(tree.getNodes(), hasSize(2));
        assertThat(tree.getNodes(), hasItem(/* dir node */treeNode("")));
        assertThat(tree.getNodes(), hasItem(treeNode("branch_mod_merge/file.txt")));
        UnifiedDiffFileContent diff = diffTab.getDiff();
        assertThat(diff.getDiffLines(), hasSize(2));
        assertThat(diff.getDiffLines(), hasItem(diffLine(1, "branch, mod and merge scenario", true)));
        assertThat(diff.getDiffLines(), hasItem(diffLine(2, "a modification", true)));
        UnifiedDiffFileContent.DiffToolbar toolbar = diff.getToolbar();
        assertThat(toolbar.getBreadCrumbs(), equalTo("branch_mod_merge/file.txt"));
        assertThat(toolbar.getChangeType(), equalToIgnoringCase("added"));
    }

    @Test
    public void testKeyboardShortcuts() throws Exception {
        BranchListPage branchListPage = loginAsAdmin(BranchListPage.class, getProject1(), getProject1Repository1());

        CommitsComparePage commitsTab = branchListPage.getBranchList().filter("basic_branching").getFocusedRow().click();

        // commit tab
        List<CommitList.CommitListEntry> commits = commitsTab.getCommits().getRows();
        assertThat(commits, hasSize(1));
        assertThat(commits.get(0), is(commit("Michael Heemskerk", "d6edcbf9246", "a modification on branch basic_branching")));

        // diff tab
        DiffComparePage diffTab = commitsTab.switchTabUsingKeyboard();
        FileTree tree = diffTab.getTree();
        assertThat(tree.getNodes(), hasSize(2));
        assertThat(tree.getNodes(), hasItem(treeNode("basic_branching/file.txt")));
        UnifiedDiffFileContent diff = diffTab.getDiff();
        assertThat(diff.getDiffLines(), hasSize(2));
        assertThat(diff.getDiffLines(), hasItem(diffLine(1, "basic branching scenario", false)));
        assertThat(diff.getDiffLines(), hasItem(diffLine(1, "a modification on branch basic_branching", true)));

        // make sure we can switch back (and get the same results after the switch)
        commitsTab = diffTab.switchTabUsingKeyboard();
        commits = commitsTab.getCommits().getRows();
        assertThat(commits, hasSize(1));
        assertThat(commits.get(0), is(commit("Michael Heemskerk", "d6edcbf9246", "a modification on branch basic_branching")));
    }

    @Test
    public void testCommitBadgeUpdates() {
        BranchListPage branchListPage = loginAsAdmin(BranchListPage.class, getProject1(), getProject1Repository1());

        CommitsComparePage comparePage = branchListPage.getBranchList().filter("basic_branching").getFocusedRow().click();
        SourceTargetSelector selector = comparePage.getSourceTargetSelector();

        assertThat(selector.getSourceChangsetBadge(), is(commitBadge("Michael Heemskerk", "d6edcbf9246")));
        selector.getSourceBranchSelector().open().selectBranches().selectItemByName("out_of_order_branch");
        assertThat(selector.getSourceChangsetBadge(), is(commitBadge("Michael Heemskerk", "2e10dd2d1d5")));

        assertThat(selector.getTargetChangsetBadge(), is(commitBadge("Anna Buttfield", "0a943a29376")));
        selector.getTargetBranchSelector().open().selectBranches().selectItemByName("branch_mod_merge");
        assertThat(selector.getTargetChangsetBadge(), is(commitBadge("Michael Heemskerk", "6053a1eaa1c")));
    }

    public static Matcher<CommitList.CommitListEntry> commit(final String author, final String hash, final String message) {
        return new SimpleTypeSafeMatcher<CommitList.CommitListEntry>() {
            @Override
            protected boolean matchesSafely(CommitList.CommitListEntry commit) {
                return commit.getAuthor().equals(author)
                        && commit.getCommitId().equals(hash)
                        && commit.getMessage().equals(message);
            }
        };
    }

    public static Matcher<UnifiedDiffFileContent.Line> diffLine(final int line, final String sourceText, final boolean addition) {
        return new SimpleTypeSafeMatcher<UnifiedDiffFileContent.Line>() {
            @Override
            protected boolean matchesSafely(UnifiedDiffFileContent.Line diffLine) {
                return diffLine.getLine() == line
                        && diffLine.getSourceText().equals(sourceText)
                        && diffLine.isAdd() == addition;
            }
        };
    }

    public static Matcher<FileTree.Node> treeNode(final String path) {
        return new SimpleTypeSafeMatcher<FileTree.Node>() {
            @Override
            protected boolean matchesSafely(FileTree.Node node) {
                return path.equals(node.getPath());
            }
        };
    }

    public static Matcher<SourceTargetSelector.CommitBadge> commitBadge(final String author, final String hash) {
        return new SimpleTypeSafeMatcher<SourceTargetSelector.CommitBadge>() {
            @Override
            protected boolean matchesSafely(SourceTargetSelector.CommitBadge badge) {
                return badge.getAuthor().equals(author)
                        && badge.getId().equals(hash)
                        && Pattern.matches("\\d{2} \\w{3} \\d{4}", badge.getDate());
            }
        };
    }

}
