package it.com.atlassian.stash.pull;

import com.atlassian.bitbucket.test.DefaultFuncTestData;
import com.atlassian.pageobjects.elements.PageElement;
import com.atlassian.pageobjects.elements.query.Poller;
import com.atlassian.pageobjects.elements.timeout.TimeoutType;
import com.atlassian.webdriver.bitbucket.element.CommitMetadata;
import com.atlassian.webdriver.bitbucket.element.PullRequestMergeConfirmDialog;
import com.atlassian.webdriver.bitbucket.element.activity.MergeActivityItem;
import com.atlassian.webdriver.bitbucket.page.ChangesetUnifiedDiffPage;
import com.atlassian.webdriver.bitbucket.page.PullRequestOverviewPage;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
import org.hamcrest.Matchers;
import org.joda.time.DateTime;
import org.junit.Test;
import org.openqa.selenium.By;

import java.util.List;
import java.util.Map;

import static com.atlassian.bitbucket.test.MergeStrategiesTestHelper.*;
import static com.atlassian.pageobjects.elements.query.Poller.waitUntilTrue;
import static org.junit.Assert.*;

public class PullRequestMergeStrategyTest extends BasePullRequestFuncTest {

    private static final String AUI_ITEM_LINK = "aui-item-link";
    private static final String DATA_STRATEGY = "data-strategy";
    private static final String FF_NAME = "Fast-forward";
    private static final By MERGE_STRATEGY_NOT_ALLOWED = By.cssSelector("#branch-deletion-failure");
    private static final String SQUASH_FF_ONLY_ID = "squash-ff-only";
    private static final Map<String, Object> SQUASH_FF_ONLY = ImmutableMap.of("id", SQUASH_FF_ONLY_ID);
    private static final String SQUASH_ID = "squash";
    private static final Map<String, Object> SQUASH = ImmutableMap.of("id", SQUASH_ID);
    private static final String SQUASH_NAME = "Squash";
    private static final String URL = DefaultFuncTestData.getRepositoryRestURL(BasePullRequestFuncTest.PROJECT_KEY,
            BasePullRequestFuncTest.REPO_SLUG) + "/settings/pull-requests";

    @Test
    public void testNoDropDownIfOnlyOneMergeStrategy() throws Exception {
        setMergeStrategies(URL, FF, FF);

        PullRequestOverviewPage pullRequestPage = createPullRequestAndLogin(BRANCH_THAT_HAS_SAME_FILE_MODIFIED_AND_MOVED_SRC,
                MASTER);

        PullRequestMergeConfirmDialog pullRequestMergeDialog = pullRequestPage.getMergeButton().clickButton();
        waitUntilTrue("All elements in the dialog should be loaded", pullRequestMergeDialog.getDeleteBranchCheckbox().timed().isVisible());
        assertFalse(pullRequestMergeDialog.getMergeStrategyDropDown().isPresent());
    }

    @Test
    public void testDefaultStrategySelected() throws Exception {
        setMergeStrategies(URL, FF, FF, NO_FF);

        PullRequestOverviewPage pullRequestPage = createPullRequestAndLogin(BRANCH_THAT_HAS_SAME_FILE_MODIFIED_AND_MOVED_SRC,
                MASTER);

        PullRequestMergeConfirmDialog pullRequestMergeDialog = pullRequestPage.getMergeButton().clickButton();
        PageElement dropDown = pullRequestMergeDialog.getMergeStrategyDropDown();
        waitUntilTrue("Merge strategy drop-down should be visible", dropDown.timed().isVisible());
        assertEquals(FF_NAME, dropDown.getText());
    }

    @Test
    public void testAllStrategiesListedInOrder() throws Exception {
        setMergeStrategies(URL, FF, FF, NO_FF, SQUASH);

        PullRequestOverviewPage pullRequestPage = createPullRequestAndLogin(BRANCH_THAT_HAS_SAME_FILE_MODIFIED_AND_MOVED_SRC,
                MASTER);

        PullRequestMergeConfirmDialog pullRequestMergeDialog = pullRequestPage.getMergeButton().clickButton();
        PageElement dropDown = pullRequestMergeDialog.getMergeStrategyDropDown();
        waitUntilTrue("Merge strategy drop-down should be visible", dropDown.timed().isVisible());
        dropDown.click();
        PageElement mergeStrategiesList = pullRequestPage.getMergeStrategiesList();

        List<PageElement> links = mergeStrategiesList.findAll(By.tagName(AUI_ITEM_LINK));
        assertEquals(FF_ID, links.get(0).getAttribute(DATA_STRATEGY));
        assertEquals(NO_FF_ID, links.get(1).getAttribute(DATA_STRATEGY));
        assertEquals(SQUASH_ID, links.get(2).getAttribute(DATA_STRATEGY));
    }

    //Not flakey, but not repeatable, needs PR merge cleanup
    @Test
    public void testSelectStrategyThenMerge() throws Exception {
        DateTime now = DateTime.now();
        setMergeStrategies(URL, FF, FF, NO_FF, SQUASH, SQUASH_FF_ONLY);

        PullRequestOverviewPage pullRequestPage = createPullRequestAndLogin(BRANCH_THAT_HAS_SAME_FILE_MODIFIED_AND_MOVED_SRC,
                MASTER);

        PullRequestMergeConfirmDialog pullRequestMergeDialog = pullRequestPage.getMergeButton().clickButton();
        PageElement dropDown = pullRequestMergeDialog.getMergeStrategyDropDown();
        waitUntilTrue("Merge strategy drop-down should be visible", dropDown.timed().isVisible());
        dropDown.click();

        PageElement mergeStrategiesList = pullRequestPage.getMergeStrategiesList();
        List<PageElement> links = mergeStrategiesList.findAll(By.tagName(AUI_ITEM_LINK));
        links.get(2).click();

        assertEquals(SQUASH_NAME, pullRequestMergeDialog.getMergeStrategyDropDown().getText());

        pullRequestMergeDialog.confirmMerge();
        Poller.waitUntil("Merge activity should be added",
                pullRequestPage.getActivitiesOfType(MergeActivityItem.class),
                Matchers.iterableWithSize(1));
        MergeActivityItem activity = Iterables.getFirst(pullRequestPage.getActivitiesOfType(MergeActivityItem.class).now(), null);
        String mergeCommitId = activity.getCommitId();

        assertNotNull(mergeCommitId);

        activity.openMergeCommitPage();
        ChangesetUnifiedDiffPage changesetPage = BITBUCKET.getPageBinder().bind(ChangesetUnifiedDiffPage.class,
                PROJECT_KEY, REPO_SLUG, mergeCommitId);

        CommitMetadata metadata = changesetPage.getMetadata();
        waitUntilTrue("Commit meta-data should be visible", metadata.timed().isVisible());
        assertFalse(metadata.isMerge());
        assertTrue(DateTime.parse(metadata.getMachineDate()).isAfter(now.getMillis()));
        assertTrue(metadata.getMessage().contains("Squashed"));
    }

    @Test
    public void testMergeWithDisallowedStrategy() throws Exception {
        setMergeStrategies(URL, FF, FF, NO_FF, SQUASH, SQUASH_FF_ONLY);

        PullRequestOverviewPage pullRequestPage = createPullRequestAndLogin(BRANCH_THAT_HAS_SAME_FILE_MODIFIED_AND_MOVED_SRC,
                MASTER);

        PullRequestMergeConfirmDialog pullRequestMergeDialog = pullRequestPage.getMergeButton().clickButton();
        PageElement dropDown = pullRequestMergeDialog.getMergeStrategyDropDown();
        waitUntilTrue("Merge strategy drop-down should be visible", dropDown.timed().isVisible());
        dropDown.click();

        PageElement mergeStrategiesList = pullRequestPage.getMergeStrategiesList();
        List<PageElement> links = mergeStrategiesList
                .withTimeout(TimeoutType.COMPONENT_LOAD)
                .findAll(By.tagName(AUI_ITEM_LINK));
        links.get(2).click();

        assertEquals(SQUASH_NAME, pullRequestMergeDialog.getMergeStrategyDropDown().getText());

        setMergeStrategies(URL, NO_FF, NO_FF); //Disable the selected strategy

        pullRequestMergeDialog.getMergeButton().click();

        PageElement errorMessage = pullRequestMergeDialog.find(By.className("aui-message-error"));
        waitUntilTrue("Error message should be visible", errorMessage.timed().isVisible());
        assertTrue(errorMessage.getText().contains(NO_FF_ID));

        //Now select no-ff and try again. We don't update the drop-down with the new strategies cause this is a super
        //edge case
        dropDown.click();

        mergeStrategiesList = pullRequestPage.getMergeStrategiesList();
        links = mergeStrategiesList
                .withTimeout(TimeoutType.COMPONENT_LOAD)
                .findAll(By.tagName(AUI_ITEM_LINK));
        links.get(1).click();
        pullRequestMergeDialog.confirmMerge();

        Poller.waitUntil("Merge activity should be added",
                pullRequestPage.getActivitiesOfType(MergeActivityItem.class),
                Matchers.iterableWithSize(1));
        MergeActivityItem activity = Iterables.getFirst(pullRequestPage.getActivitiesOfType(MergeActivityItem.class).now(), null);
        String mergeCommitId = activity.getCommitId();

        assertNotNull(mergeCommitId);
    }
}
