package com.atlassian.stash.internal.scm.git.pull;

import com.atlassian.event.api.EventPublisher;
import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.stash.internal.scm.git.DefaultGitRepositoryConfig;
import com.atlassian.stash.internal.scm.git.DefaultGitScmConfig;
import com.atlassian.stash.internal.scm.git.GitRepositoryConfig;
import com.atlassian.stash.internal.scm.git.InternalGitScmConfig;
import com.atlassian.stash.internal.scm.git.porcelain.GitPorcelain;
import com.atlassian.bitbucket.scm.git.GitAgent;
import com.atlassian.bitbucket.scm.git.command.GitCommandBuilderFactory;
import com.google.common.util.concurrent.MoreExecutors;
import org.junit.Test;

import javax.annotation.Resource;
import java.util.Properties;

import static org.mockito.Mockito.*;

@SuppressWarnings("ConstantConditions")
public class DefaultPullRequestAutoMergeStrategyTest extends AbstractPullRequestGitTest {

    @Resource
    private GitAgent agent;
    @Resource
    private GitCommandBuilderFactory builderFactory;
    @Resource
    private InternalGitScmConfig config;
    @Resource
    private EventPublisher eventPublisher;
    @Resource
    private I18nService i18nService;
    @Resource
    private GitPorcelain porcelain;
    @Resource(name = "applicationProperties")
    private Properties properties;
    @Resource
    private GitRepositoryConfig repositoryConfig;
    private DefaultPullRequestAutoMergeStrategy strategy;

    public DefaultPullRequestAutoMergeStrategyTest() {
        super(2L);
    }

    @Override
    public void setup() {
        super.setup();

        strategy = new DefaultPullRequestAutoMergeStrategy(agent, builderFactory, config, eventPublisher,
                MoreExecutors.newDirectExecutorService(), i18nService, porcelain, repositoryConfig);
    }

    @Test
    public void testMerge() throws Exception {
        verifyMerge(PullRequestMergeType.CLEAN,
                "branch_that_has_same_text_file_modified_on_both_clean_merge_src",
                "306d584aa574fa74901d3e288b2931b2c4f0e927",
                "branch_that_has_same_text_file_modified_on_both_clean_merge_trgt",
                "7fa77c6e34c6c53de0bb880fc2b0b7924a601cd2");
    }

    @Test
    public void testMergeWithAutoMergeDisabled() {
        String key = DefaultGitScmConfig.PROP_PREFIX + DefaultGitRepositoryConfig.PROP_AUTO_MERGE_ENABLED;
        Object oldValue = properties.setProperty(key, "false");
        try {
            PullRequestAutoMergeRequest request = mock(PullRequestAutoMergeRequest.class);
            when(request.getToRepository()).thenReturn(repository);

            //Null is passed for the refs because they should never be used if the auto-merge is disabled
            assertNull(strategy.merge(request, null));

            verify(request).getToRepository();
            verifyNoMoreInteractions(request);
        } finally {
            //Restore any previous merge property setting to ensure subsequent tests are not affected
            if (oldValue == null) {
                properties.remove(key);
            } else {
                properties.put(key, oldValue);
            }
        }
    }

    @Test
    public void testMergeWithConflicts() throws Exception {
        verifyMerge(PullRequestMergeType.CONFLICTED,
                "branch_that_has_same_text_file_modified_src", "5bb214e6dd15643dabfe4b6b0739becfb1e59d9f",
                "branch_that_has_same_text_file_modified_trgt", "c1654113b22f65f988f5a570d8cf32c7288ee7d2");
    }

    //WARNING: This is relying on a bug in git to trigger the merge to fail. More is explained about this in
    //         DefaultPullRequestRefHelperTest.testResolveEffectiveDiffWithIgnoreConflict()
    @Test
    public void testMergeWhenMergeFails() throws Exception {
        prepareRefs("branch_with_ignore_conflicts_src", "c927809216a016aee8635896daf7af1fce50f6e1",
                "branch_with_ignore_conflicts_trgt", "74de5497dfca9731e455d60552f9a8906e5dc1ac");

        assertNull(strategy.merge(createMergeRequest(), new DefaultPullRequestRefs(agent, pullRequest)));
    }

    @Test
    public void testMergeWithIgnoreConflictAndForceAddEnabled() {
        String key = DefaultGitScmConfig.PROP_PREFIX + DefaultGitRepositoryConfig.PROP_AUTO_MERGE_FORCE_ADD;
        Object oldValue = properties.setProperty(key, "true");
        try {
            verifyMerge(PullRequestMergeType.CONFLICTED,
                    "branch_with_ignore_conflicts_src", "c927809216a016aee8635896daf7af1fce50f6e1",
                    "branch_with_ignore_conflicts_trgt", "74de5497dfca9731e455d60552f9a8906e5dc1ac");
        } finally {
            //Restore any previous merge property setting to ensure subsequent tests are not affected
            if (oldValue == null) {
                properties.remove(key);
            } else {
                properties.put(key, oldValue);
            }
        }
    }

    private PullRequestAutoMergeRequest createMergeRequest() {
        return new PullRequestAutoMergeRequest.Builder(pullRequest)
                .author(author)
                .repositoryDir(config.getRepositoryDir(repository))
                .build();
    }

    private void verifyMerge(PullRequestMergeType type, String fromBranch, String fromHash,
                             String toBranch, String toHash) {
        prepareRefs(fromBranch, fromHash, toBranch, toHash);

        DefaultPullRequestRefs refs = new DefaultPullRequestRefs(agent, pullRequest);

        PullRequestMerge merge = strategy.merge(createMergeRequest(), refs);
        assertNotNull(merge);
        assertEquals(agent.resolveBranch(repository, refs.getMerge(), true).getLatestCommit(), merge.getHash());
        assertSame(type, merge.getType());
    }
}
