package com.atlassian.stash.internal.pull.comment.drift;

import com.atlassian.bitbucket.content.DiffSegmentType;
import com.atlassian.stash.internal.pull.InternalPullRequestDiffCommentAnchor;
import com.atlassian.bitbucket.scm.pull.PullRequestEffectiveDiff;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.junit.Before;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;

/**
 * Repository branches are in the following chronological order (suffixes):
 * <ol>
 * <li>{@code _initial}</li>
 * <li>{@code _after_rescope_on_source} -> {@link #testFastForwardOnSourceRescope()}</li>
 * <li>{@code _after_rescope_on_target} -> {@link #testFastForwardOnTargetRescope()}</li>
 * <li>{@code _after_rescope_on_both} -> {@link #testFastForwardOnFullRescope()}</li>
 * <li>{@code _after_rescope_on_source_that_breaks_ff} -> {@link #testNoFastForwardOnChangeToSource()}</li>
 * <li>{@code _after_rescope_on_target_that_breaks_ff} -> {@link #testNoFastForwardOnChangeToTarget()}</li>
 * </ol>
 * The branches are always grouped in tuples of three, with {@code from}, {@code to} and {@code merge} prefixes
 * identifying the pull request's from and to revisions and the automatic merge commit produced for the diff.
 * <p>
 * Each test mocks out the pull request with its hashes at the revisions identified by the branches in the tuple
 * above which is linked to the test, with the "previous" hashes being those from the previous entry in the list.
 */
public class GitFastForwardCommentDriftStrategyTest
        extends AbstractCommentDriftStrategyTest<FastForwardCommentDriftStrategy> {

    /**
     * Prefix for the {@link com.atlassian.stash.internal.pull.InternalPullRequest#getFromRef() from ref}.
     */
    private static final String PREFIX = "normal_file_ff_";
    /**
     * Suffix for {@link #testFastForwardOnFullRescope()}. Previous: {@link #SUFFIX_AFTER_RESCOPE_ON_TARGET}
     */
    private static final String SUFFIX_AFTER_RESCOPE_ON_BOTH = "after_rescope_on_both";
    /**
     * Suffix for {@link #testFastForwardOnSourceRescope()}. Previous: {@link #SUFFIX_INITIAL}
     */
    private static final String SUFFIX_AFTER_RESCOPE_ON_SOURCE = "after_rescope_on_source";
    /**
     * Suffix for {@link #testNoFastForwardOnChangeToSource()}. Previous: {@link #SUFFIX_AFTER_RESCOPE_ON_BOTH}
     */
    private static final String SUFFIX_AFTER_RESCOPE_ON_SOURCE_THAT_BREAKS_FF =
            "after_rescope_on_source_that_breaks_ff";
    /**
     * Suffix for {@link #testFastForwardOnTargetRescope()}. Previous: {@link #SUFFIX_AFTER_RESCOPE_ON_SOURCE}
     */
    private static final String SUFFIX_AFTER_RESCOPE_ON_TARGET = "after_rescope_on_target";
    /**
     * Suffix for {@link #testNoFastForwardOnChangeToTarget()}. Previous:
     * {@link #SUFFIX_AFTER_RESCOPE_ON_SOURCE_THAT_BREAKS_FF}
     */
    private static final String SUFFIX_AFTER_RESCOPE_ON_TARGET_THAT_BREAKS_FF =
            "after_rescope_on_target_that_breaks_ff";
    /**
     * Initial state for the pull request, only used as a previous state for {@link #testFastForwardOnSourceRescope()}.
     */
    private static final String SUFFIX_INITIAL = "initial";

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

        strategy = new FastForwardCommentDriftStrategy(scmService);
    }

    @Test
    public void testFastForwardOnFullRescope() {
        runFastForwardTest(SUFFIX_AFTER_RESCOPE_ON_BOTH, SUFFIX_AFTER_RESCOPE_ON_TARGET);
    }

    @Test
    public void testFastForwardOnSourceRescope() {
        runFastForwardTest(SUFFIX_AFTER_RESCOPE_ON_SOURCE, SUFFIX_INITIAL);
    }

    @Test
    public void testFastForwardOnTargetRescope() {
        runFastForwardTest(SUFFIX_AFTER_RESCOPE_ON_TARGET, SUFFIX_AFTER_RESCOPE_ON_SOURCE);
    }

    @Test
    public void testNoFastForwardOnChangeToSource() {
        runTest(SUFFIX_AFTER_RESCOPE_ON_SOURCE_THAT_BREAKS_FF, SUFFIX_AFTER_RESCOPE_ON_BOTH,
                new UnprocessedTest() {

                    @Override
                    protected List<InternalPullRequestDiffCommentAnchor> doCreateAnchors(String untilId,
                                                                                         String sinceId) {
                        return forFirstFile(untilId, sinceId);
                    }
                });
    }

    @Test
    public void testNoFastForwardOnChangeToTarget() {
        runTest(SUFFIX_AFTER_RESCOPE_ON_TARGET_THAT_BREAKS_FF, SUFFIX_AFTER_RESCOPE_ON_SOURCE_THAT_BREAKS_FF,
                new UnprocessedTest() {

                    @Override
                    protected List<InternalPullRequestDiffCommentAnchor> doCreateAnchors(String untilId,
                                                                                         String sinceId) {
                        return forSecondFile(untilId, sinceId);
                    }
                });
    }

    private void runFastForwardTest(String currentSuffix, String previousSuffix) {
        runTest(currentSuffix, previousSuffix, new AnchorTest() {

            private final List<InternalPullRequestDiffCommentAnchor> anchors = Lists.newArrayListWithCapacity(5);

            @Override
            public Iterable<InternalPullRequestDiffCommentAnchor> createAnchors(String untilId, String sinceId) {
                anchors.addAll(forFirstFile(untilId, sinceId));
                anchors.addAll(forSecondFile(untilId, sinceId));

                return anchors;
            }

            @Override
            public void validateAnchors(PullRequestEffectiveDiff currentDiff, List<InternalPullRequestDiffCommentAnchor> updates) {
                for (InternalPullRequestDiffCommentAnchor anchor : anchors) {
                    assertRetained(anchor, currentDiff, updates);
                }
            }
        });
    }

    private void runTest(String currentSuffix, String previousSuffix, StrategyTest test) {
        runTest(PREFIX, currentSuffix, previousSuffix, test);
    }

    private List<InternalPullRequestDiffCommentAnchor> forFirstFile(String untilId, String sinceId) {
        return Arrays.asList(
                createAnchor(untilId, sinceId, "normal_file_ff.txt", 21, DiffSegmentType.CONTEXT),
                createAnchor(untilId, sinceId, "normal_file_ff.txt", 23, DiffSegmentType.REMOVED),
                createAnchor(untilId, sinceId, "normal_file_ff.txt", 25, DiffSegmentType.ADDED));
    }

    private List<InternalPullRequestDiffCommentAnchor> forSecondFile(String untilId, String sinceId) {
        return Arrays.asList(
                createAnchor(untilId, sinceId, "normal_file_ff_2.txt", 1, DiffSegmentType.CONTEXT),
                createAnchor(untilId, sinceId, "normal_file_ff_2.txt", 2, DiffSegmentType.ADDED));
    }

    private abstract static class UnprocessedTest implements StrategyTest {

        private final List<InternalPullRequestDiffCommentAnchor> anchors = Lists.newArrayListWithCapacity(3);

        @Override
        public Iterable<InternalPullRequestDiffCommentAnchor> createAnchors(String untilId, String sinceId) {
            anchors.addAll(doCreateAnchors(untilId, sinceId));

            return anchors;
        }

        @Override
        public void validateContext(PullRequestEffectiveDiff currentDiff, DriftContext context) {
            assertEquals("No anchors should have been fast-forwarded", anchors.size(), Iterables.size(context));
        }

        protected abstract List<InternalPullRequestDiffCommentAnchor> doCreateAnchors(String untilId, String sinceId);
    }
}
