package it.com.atlassian.stash;

import com.atlassian.bitbucket.test.BuildStatusTestHelper;
import com.atlassian.webdriver.bitbucket.element.BuildStatusDialog;
import com.atlassian.webdriver.bitbucket.element.CommitList;
import com.atlassian.webdriver.bitbucket.page.CommitListPage;
import com.atlassian.webdriver.bitbucket.util.DateUtils;
import org.junit.Test;

import java.time.ZoneId;
import java.time.ZoneOffset;
import java.util.List;
import java.util.Optional;

import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.*;

public class CommitListTest extends QARepoFuncTest {

    @Test
    public void testDefaultBranch() {
        TimeZoneTestHelper.withServerTimeZone(Optional.empty(), this::checkDefaultBranchInTimeZone);
    }

    @Test
    public void testDefaultBranchInServerTimeZone() {
        TimeZoneTestHelper.withServerTimeZone(Optional.of(ZoneOffset.MIN), this::checkDefaultBranchInTimeZone);
    }

    @Test
    public void testDefaultBranchInUserTimeZone() {
        TimeZoneTestHelper.withServerAndUserTimeZone(Optional.of(ZoneOffset.MIN),
                Optional.of(ZoneOffset.MAX), this::checkDefaultBranchInTimeZone);
    }

    @Test
    public void testCommitListOnBranch() throws Exception {
        String branch = "!@#%";
        CommitListPage page = loginAsAdmin(CommitListPage.class, getProjectKey(), getRepoSlug(), branch);

        List<CommitList.CommitListEntry> commits = page.getCommitList().getRows();
        assertThat("Commit list should have 50 items", commits, hasSize(50));
        
        assertEquals(branch, page.getBranchSelector().getSelectedItemName());

        assertCorrectCommit(commits.get(0), "Federico Silva Armas", "GIT Commit 1", "5b947ec4925", false, "2012-01-03T17:04:06+1100");
        assertCorrectCommit(commits.get(1), "Federico Silva Armas", "GIT Commit 1", "fa53fd70deb", false, "2012-01-03T17:00:51+1100");
        assertCorrectCommit(commits.get(2), "Federico Silva Armas", "GIT Commit 1", "45e41440ff7", false, "2012-01-03T17:00:24+1100");
        assertCorrectCommit(commits.get(3), "Federico Silva Armas", "GIT Commit 1", "fa83c3191ed", false, "2012-01-03T16:59:06+1100");
        assertCorrectCommit(commits.get(4), "Federico Silva Armas", "GIT Commit 1", "17aecefaa3b", false, "2012-01-03T16:59:01+1100");
        assertCorrectCommit(commits.get(49), "Federico Silva Armas", "GIT Commit 1", "a2c429b658a", false, "2011-12-20T16:33:59+1100");

        page.getCommitList().loadNextPage();

        commits = page.getCommitList().getRows();
        assertThat("Commit list should have 100 items", commits, hasSize(100));

        assertCorrectCommit(commits.get(49), "Federico Silva Armas", "GIT Commit 1", "a2c429b658a", false, "2011-12-20T16:33:59+1100");
        assertCorrectCommit(commits.get(50), "Federico Silva Armas", "GIT Commit 1", "2abc8aea0d6", false, "2011-12-20T11:20:20+1100");
        assertCorrectCommit(commits.get(99), "Federico Silva Armas", "GIT Commit 2", "9da010a7b62", false, "2011-12-20T08:54:40+1100");
    }

    @Test
    public void testCommitListBranchSwitch() throws Exception {
        CommitListPage page = loginAsAdmin(CommitListPage.class, getProjectKey(), getRepoSlug());

        assertEquals("master", page.getBranchSelector().getSelectedItemName());

        List<CommitList.CommitListEntry> commits = page.getCommitList().getRows();
        assertThat("Commit list should have 50 items", commits, hasSize(50));

        assertCorrectCommit(commits.get(0), "Michael McGlynn", "Add file with CRCRLF line endings to qa-resources", "6acc46ee4f9", false, "2015-01-09T15:23:21+1100");
        assertCorrectCommit(commits.get(1), "Federico Silva Armas", "Modified file with GB18030 encoding", "d456b884414", false, "2012-03-11T18:56:50-0500");
        assertCorrectCommit(commits.get(49), "Federico Silva Armas", "a commit to file1.txt #46", "b967c311a4b", false, "2012-02-06T09:39:07+1100");

        page.getBranchSelector().open().filterBy("%").selectItemById("refs/heads/%");
        assertEquals("%", page.getBranchSelector().getSelectedItemName());

        commits = page.waitUntilCommitsTableLoaded().getCommitList().getRows();
        assertThat("Commit list should have 50 items", commits, hasSize(50));

        assertCorrectCommit(commits.get(0), "Federico Silva Armas", "GIT Commit 1", "fa53fd70deb", false, "2012-01-03T00:00:51-0600");
        assertCorrectCommit(commits.get(49), "Federico Silva Armas", "GIT Commit 1", "2abc8aea0d6", false, "2011-12-19T18:20:20-0600");
    }

    @Test
    public void testCommitListBuildStatus() throws Exception {
        BuildStatusTestHelper.addBuildStatus("d456b884414cc5e1939a3e5ba1e7f7e5d1b6b943", "TEST-1", BuildStatusTestHelper.State.SUCCESSFUL);

        CommitListPage page = loginAsAdmin(CommitListPage.class, getProjectKey(), getRepoSlug());
        CommitList.CommitListEntry commit = page.getCommitList().getRows().get(1);

        //double check we got the commit we expected.
        assertCorrectCommit(commit, "Federico Silva Armas", "Modified file with GB18030 encoding", "d456b884414", false, "2012-03-11T18:56:50-0500");

        assertTrue("Commit has a build status", commit.hasBuildStatus());

        BuildStatusDialog buildStatus = commit.clickBuildStatus();
        assertEquals("Build status dialog has a successful build",
                BuildStatusTestHelper.State.SUCCESSFUL, buildStatus.getBuildStatusLines().get(0).getStatus());
    }

    @Test
    public void testCommitListAllowsUntilCommitId() throws Exception {
        CommitListPage page = loginAsAdmin(CommitListPage.class, getProjectKey(), getRepoSlug(), "b4d378388c2");
        List<CommitList.CommitListEntry> commits = page.getCommitList().getRows();
        assertThat("Commit list should have 50 items", commits, hasSize(50));

        assertCorrectCommit(commits.get(0), "Federico Silva Armas", "new commit", "b4d378388c2", false, "2012-02-22T17:07:17+1100");
        assertCorrectCommit(commits.get(1), "Federico Silva Armas", "<script>alert(666)</script>", "c4a3c85d620", false, "2012-02-22T17:06:08+1100");
        assertCorrectCommit(commits.get(49), "Federico Silva Armas", "a commit to file1.txt #41", "0dabc9ccadb", false, "2012-02-06T09:39:04+1100");

        page.getCommitList().loadNextPage();

        commits = page.getCommitList().getRows();
        assertThat("Commit list should have 100 items", commits, hasSize(100));

        assertCorrectCommit(commits.get(50), "Federico Silva Armas", "a commit to file1.txt #40", "b7db979a508", false, "2012-02-06T09:39:04+1100");
        assertCorrectCommit(commits.get(51), "Federico Silva Armas", "a commit to file1.txt #39", "182ba4c374c", false, "2012-02-06T09:39:03+1100");
    }

    private static void assertCorrectCommit(CommitList.CommitListEntry commit, String author, String message,
                                            String commitId, Boolean isMerge, String date) throws Exception {
        assertCorrectCommit(commit, author, message, commitId, isMerge, date, Optional.empty());
    }

    private static void assertCorrectCommit(CommitList.CommitListEntry commit, String author, String message,
                                            String commitId, Boolean isMerge, String date,
                                            Optional<? extends ZoneId> timeZone) {
        assertEquals("Commit author is incorrect", author, commit.getAuthor());
        assertEquals("Commit message is incorrect", message, commit.getMessage());
        assertEquals("Commit ID is incorrect", commitId, commit.getCommitId());
        assertEquals("Commit was not flagged as a merge", isMerge, commit.isMerge());

        assertThat(commit.getMachineDate(), DateUtils.TIME_STAMP.withTimeZone(timeZone).matchesTimestamp(date));
        assertThat(commit.getCommitDate(), DateUtils.FULL.withTimeZone(timeZone).matchesTimestamp(date));
    }

    private Void checkDefaultBranchInTimeZone(Optional<? extends ZoneId> zoneId) {
        CommitListPage page = loginAsAdmin(CommitListPage.class, getProjectKey(), getRepoSlug());
        List<CommitList.CommitListEntry> commits = page.getCommitList().getRows();
        assertThat("Commit list should have 50 items", commits, hasSize(50));

        assertCorrectCommit(commits.get(0), "Michael McGlynn", "Add file with CRCRLF line endings to qa-resources", "6acc46ee4f9", false, "2015-01-09T15:23:21+1100", zoneId);
        assertCorrectCommit(commits.get(1), "Federico Silva Armas", "Modified file with GB18030 encoding", "d456b884414", false, "2012-03-11T18:56:50-0500", zoneId);
        assertCorrectCommit(commits.get(2), "\u6768\u8d35\u5983", "\u52a0\u83f2\u732b", "18cbc665632", false, "2012-02-28T15:02:19+1100", zoneId);
        assertCorrectCommit(commits.get(3), "Federico Silva Armas", "merge", "f0a3d73385c", true, "2012-02-22T17:15:18+1100", zoneId);
        assertCorrectCommit(commits.get(4), "Federico Silva Armas", "new commit", "b4d378388c2", false, "2012-02-22T17:07:17+1100", zoneId);
        assertCorrectCommit(commits.get(5), "Federico Silva Armas", "<script>alert(666)</script>", "c4a3c85d620", false, "2012-02-22T17:06:08+1100", zoneId);
        assertCorrectCommit(commits.get(6), "Federico Silva Armas", "goo", "c7c7497cb7e", false, "2012-02-22T17:05:11+1100", zoneId);
        assertCorrectCommit(commits.get(49), "Federico Silva Armas", "a commit to file1.txt #46", "b967c311a4b", false, "2012-02-06T09:39:07+1100", zoneId);

        page.getCommitList().loadNextPage();

        commits = page.getCommitList().getRows();
        assertThat("Commit list should have 100 items", commits, hasSize(100));

        assertCorrectCommit(commits.get(0), "Michael McGlynn", "Add file with CRCRLF line endings to qa-resources", "6acc46ee4f9", false, "2015-01-09T15:23:21+1100", zoneId);
        assertCorrectCommit(commits.get(1), "Federico Silva Armas", "Modified file with GB18030 encoding", "d456b884414", false, "2012-03-11T18:56:50-0500", zoneId);
        assertCorrectCommit(commits.get(2), "\u6768\u8d35\u5983", "\u52a0\u83f2\u732b", "18cbc665632", false, "2012-02-28T15:02:19+1100", zoneId);
        assertCorrectCommit(commits.get(49), "Federico Silva Armas", "a commit to file1.txt #46", "b967c311a4b", false, "2012-02-06T09:39:07+1100", zoneId);
        assertCorrectCommit(commits.get(50), "Federico Silva Armas", "a commit to file1.txt #45", "48d50ee5828", false, "2012-02-05T16:39:06-0600", zoneId);
        assertCorrectCommit(commits.get(51), "Federico Silva Armas", "a commit to file1.txt #44", "3aa949f90df", false, "2012-02-06T09:39:06+1100", zoneId);
        assertCorrectCommit(commits.get(99), "Federico Silva Armas", "JPN files", "d86c2faf589", false, "2012-01-31T16:27:33+1100", zoneId);

        return null;
    }
}
