package com.atlassian.bitbucket.mesh.git.builder.difftree;

import com.atlassian.bitbucket.mesh.git.GitChangeType;
import com.atlassian.bitbucket.mesh.git.GitNodeType;
import com.atlassian.bitbucket.mesh.git.content.Change;
import com.atlassian.bitbucket.mesh.git.content.Changeset;
import com.atlassian.bitbucket.mesh.git.content.ChangesetCallback;
import com.google.common.collect.ImmutableList;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import java.util.List;

import static com.atlassian.bitbucket.mesh.util.ProcessStdioHandlerUtils.handleStdout;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class CallbackChangesetDiffTreeStdioHandlerTest {

    private CallbackChangesetDiffTreeStdioHandler handler;

    @Mock
    private ChangesetCallback callback;

    @Before
    public void setup() {
        doReturn(true).when(callback).onChangeset(any());

        handler = new CallbackChangesetDiffTreeStdioHandler(
                ImmutableList.of("1b8cf5345a7f8fcbc6ca0849969a5f76c6937a1a",
                        "ab414db951a7f450f06864ca85eaab0c77d4abc0"),
                callback, true, 100, -1);
    }

    @Test
    public void testProcess() {
        List<Changeset> changesets = process("git-diff-tree-batch-change.out");
        assertThat(changesets, hasSize(2));

        Changeset changeset = changesets.get(0);
        List<Change> changes = changeset.getChanges();
        assertChangeset(changeset, "91c7ff49fd8519e9313bc2584f3e4dc75ee5a26f", "1b8cf5345a7f8fcbc6ca0849969a5f76c6937a1a");
        assertThat(changes.size(), equalTo(3));
        assertChange("59ee96b9e54eb7aa0ec5a5f95e10881f4a8f2592", "89e01585365a872d0b85844b0e8a3312ab3ef39c",
                "plugins/mirroring-mirror/src/main/java/com/atlassian/bitbucket/internal/mirroring/mirror/client/AuthRetryingUpstreamClient.java", changes.get(0));
        assertChange("85e9ec1333b075dff093d3f695e368a56ff46c3f", "227a3e65e2a0f84449f3aec6a9ccb6d3dd78e35a",
                "plugins/mirroring-mirror/src/main/java/com/atlassian/bitbucket/internal/mirroring/mirror/client/DefaultUpstreamClient.java", changes.get(1));
        assertChange("7ebc957d652f81b3809c0b4ebe134e71a797f37c", "4b0d62653c4af2051db5c79fa85963f15718fbdd",
                "plugins/mirroring-mirror/src/main/java/com/atlassian/bitbucket/internal/mirroring/mirror/client/InternalUpstreamClient.java", changes.get(2));

        changeset = changesets.get(1);
        changes = changeset.getChanges();
        assertChangeset(changeset, "91c7ff49fd8519e9313bc2584f3e4dc75ee5a26f", "ab414db951a7f450f06864ca85eaab0c77d4abc0");
        assertThat(changes.size(), equalTo(3));
        assertChange("59ee96b9e54eb7aa0ec5a5f95e10881f4a8f2592", "89e01585365a872d0b85844b0e8a3312ab3ef39c",
                "plugins/mirroring-mirror/src/main/java/com/atlassian/bitbucket/internal/mirroring/mirror/client/AuthRetryingUpstreamClient.java", changes.get(0));
        assertChange("85e9ec1333b075dff093d3f695e368a56ff46c3f", "227a3e65e2a0f84449f3aec6a9ccb6d3dd78e35a",
                "plugins/mirroring-mirror/src/main/java/com/atlassian/bitbucket/internal/mirroring/mirror/client/DefaultUpstreamClient.java", changes.get(1));
        assertChange("7ebc957d652f81b3809c0b4ebe134e71a797f37c", "4b0d62653c4af2051db5c79fa85963f15718fbdd",
                "plugins/mirroring-mirror/src/main/java/com/atlassian/bitbucket/internal/mirroring/mirror/client/InternalUpstreamClient.java", changes.get(2));
    }

    private static void assertChange(String contentId, String fromContentId, String path, Change change) {
        assertChange(contentId, fromContentId, path, null, GitChangeType.MODIFY, -1, change);
    }

    private static void assertChange(String contentId, String fromContentId, String path, String srcPath,
                                     GitChangeType type, int score, Change change) {
        Assert.assertEquals(contentId, change.getContentId());
        Assert.assertEquals(fromContentId, change.getFromContentId());
        assertNotNull(change.getPath());
        Assert.assertEquals(path, change.getPath());
        Assert.assertEquals(score, change.getPercentUnchanged());
        Assert.assertSame(type, change.getType());
        Assert.assertEquals(GitNodeType.FILE, change.getNodeType());
        if (srcPath == null) {
            Assert.assertNull(change.getSrcPath());
        } else {
            assertNotNull(change.getSrcPath());
            Assert.assertEquals(srcPath, change.getSrcPath());
        }
    }

    private static void assertChangeset(Changeset changeset, String fromHash, String toHash) {
        assertThat(changeset.getFromCommitId(), equalTo(fromHash));
        assertThat(changeset.getToCommit().getId(), equalTo(toHash));
    }

    private List<Changeset> process(String filename) {
        handleStdout(handler, filename);

        ArgumentCaptor<Changeset> changesetCaptor = ArgumentCaptor.forClass(Changeset.class);
        verify(callback, atLeastOnce()).onChangeset(changesetCaptor.capture());

        return changesetCaptor.getAllValues();
    }
}
