package it.com.atlassian.stash.pull;

import com.atlassian.bitbucket.test.PullRequestTestHelper;
import com.atlassian.bitbucket.test.RepositoryTestHelper;
import com.atlassian.pageobjects.elements.PageElement;
import com.atlassian.pageobjects.elements.query.Poller;
import com.atlassian.webdriver.bitbucket.element.PullRequestMergeConfirmDialog;
import com.atlassian.webdriver.bitbucket.page.PullRequestEffectiveDiffPage;
import com.atlassian.webdriver.bitbucket.page.PullRequestOverviewPage;
import it.com.atlassian.stash.rules.WebStorageCleanUpRule;
import org.junit.Test;
import org.junit.rules.TestRule;
import org.openqa.selenium.By;

import java.util.List;

import static com.atlassian.bitbucket.test.TestRuleOrderingSyntax.outerRule;
import static com.atlassian.pageobjects.elements.query.Conditions.forSupplier;
import static com.atlassian.pageobjects.elements.query.Poller.waitUntilFalse;
import static com.atlassian.pageobjects.elements.query.Poller.waitUntilTrue;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

/**
 * Any test that actually performs a merge must use a unique source branch.  If not, other tests will fail
 * when trying to set up a PR with a branch that has already been merged (because the PR has no changes).
 */
public class PullRequestMergeBranchDeletionTest extends BasePullRequestFuncTest {

    private static final By MERGE_OK_BUT_DELETE_FAILED_DIALOG = By.cssSelector("#branch-deletion-failure");

    @Test
    public void testShouldDeleteBranchWhenCheckboxIsTickedAndMergeCompletes() {
        PullRequestOverviewPage pullRequestPage = createPullRequestAndLogin(BRANCH_THAT_DIFFER_BY_ONE_FILE_SRC, MASTER);

        PullRequestMergeConfirmDialog<PullRequestOverviewPage> mergeConfirmDialog = pullRequestPage.getMergeButton()
                .clickButton();

        PageElement deleteBranchCheckbox = mergeConfirmDialog.getDeleteBranchCheckbox();
        waitUntilTrue("Delete branch checkbox should be visible", deleteBranchCheckbox.timed().isVisible());

        waitUntilTrue("Delete branch checkbox should be selected", forSupplier(5000, () -> {
            deleteBranchCheckbox.click();
            return deleteBranchCheckbox.isSelected();
        }));

        mergeConfirmDialog.confirmMerge();
        waitUntilTrue("Pull request is marked as merged", pullRequestPage.getMergedLozenge().timed().isPresent());

        Poller.waitUntilFalse("Pull request source branch should be deleted", forSupplier(5000,
                () -> RepositoryTestHelper.hasBranch(PROJECT_KEY, REPO_SLUG, BRANCH_THAT_DIFFER_BY_ONE_FILE_SRC)));
    }

    @Test
    public void testShouldDisableDeleteBranchCheckboxIfBranchCannotBeDeleted() {
        // Set up a pull request that can't delete the source branch because it's the default branch
        PullRequestOverviewPage pullRequestPage = createPullRequestAndLogin(
                MASTER, BRANCH_THAT_HAS_SAME_FILE_MODIFIED_AND_MOVED_SRC);

        PullRequestMergeConfirmDialog<PullRequestOverviewPage> mergeConfirmDialog = pullRequestPage.getMergeButton()
                .clickButton();

        waitUntilFalse("Delete branch checkbox should be disabled", mergeConfirmDialog.getDeleteBranchCheckbox().timed()
                .isEnabled());
    }

    @Test
    public void testShouldNotDeleteBranchWhenCheckboxIsUntickedAndMergeCompletes() {
        PullRequestOverviewPage pullRequestPage = createPullRequestAndLogin(
                BRANCH_THAT_HAS_SAME_BINARY_FILE_MODIFIED_SRC, MASTER);

        PullRequestMergeConfirmDialog<PullRequestOverviewPage> mergeConfirmDialog = pullRequestPage.getMergeButton()
                .clickButton();
        // Above test verifies that the checkbox is cleared by default, so we don't do anything here...
        mergeConfirmDialog.confirmMerge();
        waitUntilTrue("Pull request is marked as merged", pullRequestPage.getMergedLozenge().timed().isPresent());

        assertTrue("Pull request source branch should exist", RepositoryTestHelper.hasBranch(PROJECT_KEY, REPO_SLUG,
                BRANCH_THAT_HAS_SAME_BINARY_FILE_MODIFIED_SRC));
    }

    @Test
    public void testShouldShowErrorDialogIfBranchCouldNotBeDeleted() {
        PullRequestOverviewPage pullRequestPage = createPullRequestAndLogin(
                BRANCH_THAT_HAS_SAME_FILE_RENAMED_ON_BOTH_SRC, MASTER);

        PullRequestMergeConfirmDialog<PullRequestOverviewPage> mergeConfirmDialog = pullRequestPage.getMergeButton()
                .clickButton();

        PageElement deleteBranchCheckbox = mergeConfirmDialog.getDeleteBranchCheckbox();
        waitUntilTrue("Delete branch checkbox should be visible", deleteBranchCheckbox.timed().isVisible());

        waitUntilTrue("Delete branch checkbox should be selected", forSupplier(5000, () -> {
            deleteBranchCheckbox.click();
            return deleteBranchCheckbox.isSelected();
        }));

        // create another pull request with this branch as the target branch, after the branch deletion check passes
        PullRequestTestHelper pullRequest = createPullRequest(
                new PullRequestBuilder(MASTER, BRANCH_THAT_HAS_SAME_FILE_RENAMED_ON_BOTH_SRC).build());

        mergeConfirmDialog.confirmMergeExpectingWarning(MERGE_OK_BUT_DELETE_FAILED_DIALOG);

        // Merging the PR in the previous step triggered asynchronous rescoping. This makes the test flaky because
        // the PR version can change while we are trying to decline it in the tear down.
        // Visiting the PR diff page solves this issue by doing just-in-time rescoping/effective diff generation/drifting
        loginAs(AUTHOR_1, AUTHOR_1, PullRequestEffectiveDiffPage.class, pullRequest.getProjectKey(), pullRequest.getRepoSlug(),
                pullRequest.getId());
    }

    @Test
    public void testShouldShowUnselectedDeleteBranchCheckboxOnMergeConfirmDialog() {
        PullRequestOverviewPage pullRequestPage = createPullRequestAndLogin(
                BRANCH_THAT_HAS_SAME_FILE_MODIFIED_AND_MOVED_SRC, MASTER);

        PullRequestMergeConfirmDialog<PullRequestOverviewPage> mergeConfirmDialog = pullRequestPage.getMergeButton()
                .clickButton();

        waitUntilTrue("Delete branch checkbox should be visible", mergeConfirmDialog.getDeleteBranchCheckbox().timed()
                .isPresent());
        assertFalse("Delete branch checkbox should be unselected by default", mergeConfirmDialog
                .getDeleteBranchCheckbox().isSelected());
    }

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