package it.com.atlassian.stash.pull;

import com.atlassian.pageobjects.elements.query.Poller;
import com.atlassian.bitbucket.test.DefaultFuncTestData;
import com.atlassian.webdriver.bitbucket.element.PullRequestDetails;
import com.atlassian.webdriver.bitbucket.element.UserSelect;
import com.atlassian.webdriver.bitbucket.page.PullRequestCreatePage;
import com.atlassian.webdriver.bitbucket.page.PullRequestOverviewPage;
import com.atlassian.webdriver.bitbucket.page.BitbucketLoginPage;
import com.google.common.collect.Iterables;
import org.junit.Test;

import java.util.Collection;

import static com.atlassian.pageobjects.elements.query.Poller.*;
import static com.atlassian.webdriver.bitbucket.hamcrest.OptionMatchers.withUsername;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;

public class PullRequestCreateTest extends BasePullRequestFuncTest {

    private static final String MASTER_BRANCH_ID = "refs/heads/" + MASTER;
    private static final String SOURCE_BRANCH_ID = "refs/heads/" + BRANCH_THAT_HAS_SAME_FILE_MODIFIED_AND_MOVED_SRC;
    private static final String TARGET_BRANCH_ID = "refs/heads/" + BRANCH_THAT_HAS_SAME_FILE_MODIFIED_AND_MOVED_TRGT;

    @Test
    public void testCancelButton() {
        PullRequestCreatePage createPage = getCreatePage();
        createPage.getSourceBranchSelector().open().selectItemById(SOURCE_BRANCH_ID);

        createPage.clickContinue();

        assertTrue("Check we got the the right page", createPage.isShowingPRForm());
        assertEquals("check source branch lozenge", BRANCH_THAT_HAS_SAME_FILE_MODIFIED_AND_MOVED_SRC, createPage
                .getCollapsedSourceBranch().getBranch());
        assertEquals("check source branch lozenge", REPO_NAME, createPage.getCollapsedSourceBranch().getRepository());
        assertEquals("check target branch lozenge", "master", createPage.getCollapsedTargetBranch().getBranch());
        assertEquals("check target branch lozenge", REPO_NAME, createPage.getCollapsedTargetBranch().getRepository());

        createPage.clickChange();

        assertFalse("Check we got the the right page", createPage.isShowingPRForm());
        createPage.getSourceBranchSelector().open().selectItemById(TARGET_BRANCH_ID);
        createPage.getTargetBranchSelector().open().selectItemById(SOURCE_BRANCH_ID);

        createPage.clickContinue();

        assertTrue("Check we got the the right page", createPage.isShowingPRForm());
        assertEquals("check source branch lozenge", BRANCH_THAT_HAS_SAME_FILE_MODIFIED_AND_MOVED_TRGT, createPage
                .getCollapsedSourceBranch().getBranch());
        assertEquals("check source branch lozenge", REPO_NAME, createPage.getCollapsedSourceBranch().getRepository());
        assertEquals("check target branch lozenge", BRANCH_THAT_HAS_SAME_FILE_MODIFIED_AND_MOVED_SRC, createPage
                .getCollapsedTargetBranch().getBranch());
        assertEquals("check target branch lozenge", REPO_NAME, createPage.getCollapsedTargetBranch().getRepository());
    }

    @Test
    public void testCreateCancellation() {
        PullRequestCreatePage createPage = getCreatePage();
        createPage.getSourceBranchSelector().open().selectItemById(SOURCE_BRANCH_ID);
        createPage.getTargetBranchSelector().open().selectItemById(TARGET_BRANCH_ID);

        createPage.clickContinue();
        createPage.getDetails().setTitle("***CANCELME****").setDescription("***cancelme***");
        // Verify that creation is canceled and that the cancellation message from the plugin is shown
        PullRequestCreatePage errorPage = createPage.clickSubmitExpectingErrors();
        Collection<String> errors = errorPage.getAuiErrorMessages();
        assertFalse(errors.isEmpty());
        assertEquals(1, errors.size());
        assertTrue(Iterables.getOnlyElement(errors).endsWith("\nTesting pull request open cancellation."));
    }

    @Test
    public void testCreateSuccessful() {
        PullRequestCreatePage createPage = getCreatePage();
        String description = "# A pull request <script>description</script>";
        String descriptionHtml = "<h1>A pull request &lt;script&gt;description&lt;/script&gt;</h1>\n";

        assertEquals("Source repository selector should be set", PROJECT_NAME + "/" + REPO_NAME, createPage.getSourceRepositorySelector().getTriggerText());
        assertFalse("Source branch selector should not be set", createPage.getSourceBranchSelector().hasValue().now());
        assertEquals("Target repository selector should be set", PROJECT_NAME + "/" + REPO_NAME, createPage.getTargetRepositorySelector().getTriggerText());
        assertEquals("Target branch selector should default to default branch master", MASTER_BRANCH_ID, createPage.getTargetBranchSelector().getSelectedValue().now());

        createPage.getSourceBranchSelector().open().selectItemById(SOURCE_BRANCH_ID);
        createPage.getTargetBranchSelector().open().selectItemById(TARGET_BRANCH_ID);

        createPage.clickContinue();
        assertTrue(createPage.isShowingPRForm());

        PullRequestDetails details = createPage.getDetails().setDescription(description).togglePreview();
        assertEquals(descriptionHtml, details.getPreviewHtml());
        assertFalse(details.togglePreview().isPreviewVisible());

        PullRequestOverviewPage activityPage = createPage.clickSubmit();
        try {
            assertEquals("Modifying file on source branch", activityPage.getPullRequestTitle());
            assertEquals(descriptionHtml, activityPage.getPullRequestDescriptionAsHtml());
        } finally {
            activityPage.getDeclineButton().clickButton().decline();
        }
    }

    @Test
    public void testCreateWithFork() {
        testContext.fork(PROJECT_KEY, REPO_SLUG, AUTHOR_1, AUTHOR_1);

        PullRequestCreatePage createPage = getCreatePage();
        assertEquals("Should have upstream and fork repo for selection", 2, createPage.getSourceRepositorySelector()
                .open().numberOfResults());
        createPage.getSourceRepositorySelector().open().selectItemByName(AUTHOR_1_DISPLAY_NAME + "/" + REPO_NAME);
        waitUntilFalse("Source branch selector should unset", createPage.getSourceBranchSelector().hasValue());
        createPage.getSourceBranchSelector().open().selectItemById(SOURCE_BRANCH_ID);

        // We want to use all selectors
        assertEquals("Target branch selector should default to default branch master", MASTER_BRANCH_ID, createPage
                .getTargetBranchSelector().getSelectedValue().now());
        createPage.getTargetRepositorySelector().open().selectItemByName(AUTHOR_1_DISPLAY_NAME + "/" + REPO_NAME);
        waitUntilFalse("Target branch selector should unset", createPage.getTargetBranchSelector().hasValue());
        createPage.getTargetRepositorySelector().open().selectItemByName(PROJECT_NAME + "/" + REPO_NAME);
        createPage.getTargetBranchSelector().open().selectItemById(TARGET_BRANCH_ID);

        PullRequestOverviewPage activityPage = createPage.clickContinue().clickSubmit();
        activityPage.getDeclineButton().clickButton().decline();
    }

    @Test
    public void testReviewers() {
        PullRequestCreatePage createPage = getCreatePage();
        createPage.getSourceBranchSelector().open().selectItemById(SOURCE_BRANCH_ID);
        PullRequestDetails details = createPage.clickContinue().getDetails();

        // we can do this because we are creating the PR as admin
        details.addReviewer(AUTHOR_1);
        waitUntil(details.getReviewerUsernames(), hasItems(AUTHOR_1));

        details.addReviewer(REVIEWER_1);
        waitUntil(details.getReviewerUsernames(), hasItems(AUTHOR_1, REVIEWER_1));

        // make sure non-licensed and non-repo-read users cannot be selected
        UserSelect reviewerSelect = details.getReviewerSelect();
        reviewerSelect.type(REVIEWER_UNLICENSED);
        waitUntilTrue("Unexpected options: " + reviewerSelect.getOptions().now(), reviewerSelect.hasNoOptions());
        reviewerSelect.clearFilter();

        reviewerSelect.type(REVIEWER_NO_REPOSITORY_ACCESS);
        waitUntilTrue("Unexpected options: " + reviewerSelect.getOptions().now(), reviewerSelect.hasNoOptions());
        reviewerSelect.clearFilter();

        // remove pull request creator from reviewers and make sure the other reviewer is still there
        details.removeReviewer(AUTHOR_1);
        waitUntil(details.getReviewerSelect().getSelectedOptions(), contains(withUsername(REVIEWER_1)));
    }

    @Test
    public void testReviewerWithAt() {
        PullRequestCreatePage createPage = getCreatePage();
        createPage.getSourceBranchSelector().open().selectItemById(SOURCE_BRANCH_ID);
        PullRequestDetails details = createPage.clickContinue().getDetails();

        waitUntil(details.getReviewerUsernames(), not(hasItems(AUTHOR_1)));
        details.addReviewer("@" + AUTHOR_1, AUTHOR_1);
        waitUntil(details.getReviewerUsernames(), hasItems(AUTHOR_1));
    }

    @Test
    public void testTitleAndDescriptionGenerationForMultipleCommits() {
        PullRequestCreatePage createPage = getCreatePage();

        createPage.getSourceBranchSelector().open().selectItemById(SOURCE_BRANCH_ID);
        createPage.getTargetBranchSelector().open().selectItemById(MASTER_BRANCH_ID);

        createPage.clickContinue();
        assertTrue(createPage.isShowingPRForm());

        assertEquals("Expected title to be set from branch name",
                "Branch that has same file modified and moved src", createPage.getDetails().getTitle());
        assertEquals("Expected description to be set from commit messages",
                "* Commiting a file\n* Modifying file on source branch", createPage.getDetails().getDescription());
    }

    @Test
    public void testTitleAndDescriptionGenerationForSingleCommit() {
        PullRequestCreatePage createPage = getCreatePage();

        createPage.getSourceBranchSelector().open().selectItemById(SOURCE_BRANCH_ID);
        createPage.getTargetBranchSelector().open().selectItemById(TARGET_BRANCH_ID);

        createPage.clickContinue();
        assertTrue(createPage.isShowingPRForm());

        assertEquals("Expected title to be set from message of single commit",
                "Modifying file on source branch", createPage.getDetails().getTitle());
        assertEquals("", createPage.getDetails().getDescription());
    }

    @Test
    public void testTitleValidation() {
        PullRequestCreatePage createPage = getCreatePage();
        assertFalse(createPage.canCreate());
        createPage.getSourceBranchSelector().open().selectItemById(SOURCE_BRANCH_ID);
        createPage.getTargetBranchSelector().open().selectItemById(TARGET_BRANCH_ID);
        assertTrue(createPage.canCreate());

        createPage.clickContinue();
        Poller.waitUntilEquals("Modifying file on source branch", createPage.getDetails().getTitleTimed());

        createPage.getDetails().setTitle("");
        createPage.clickSubmitExpectingErrors();
        assertEquals(1, createPage.getTitleFieldErrors().size());
    }

    private PullRequestCreatePage getCreatePage() {
        return BITBUCKET.visit(BitbucketLoginPage.class).login(
                DefaultFuncTestData.getAdminUser(), DefaultFuncTestData.getAdminPassword(),
                PullRequestCreatePage.class, PROJECT_KEY, REPO_SLUG);
    }
}
