package com.atlassian.bitbucket.mesh.git.batch;

import com.atlassian.bitbucket.mesh.AbstractWiredExecutionContextTest;
import com.atlassian.bitbucket.mesh.git.exception.CommitNotFoundException;
import com.atlassian.bitbucket.mesh.git.exception.ObjectNotFoundException;
import com.atlassian.bitbucket.mesh.repository.Repository;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcGitTimeouts;
import com.atlassian.bitbucket.mesh.test.annotation.Repo;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;

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

import static com.atlassian.bitbucket.mesh.git.batch.BatchChangeType.*;
import static org.mockito.Mockito.*;

public class DefaultBatchManagerTest extends AbstractWiredExecutionContextTest {

    private static final RpcGitTimeouts TIMEOUTS =
            RpcGitTimeouts.newBuilder().setExecution(3600L).setIdle(120L).build();

    @Rule
    public final ExpectedException thrown = ExpectedException.none();

    @Resource
    private DefaultBatchManager manager;
    @Repo
    private Repository repository;

    @Test
    public void testBatchChangesFull() {
        BatchChangeCallback callback = mock(BatchChangeCallback.class);
        manager.changes(new BatchChangesParameters.Builder(repository, TIMEOUTS)
                        .untilCommitId("6d8e9736357657738d15cd29069f21bf44553e19")
                        .build(),
                callback);

        ArgumentCaptor<BatchChange> changeCaptor = ArgumentCaptor.forClass(BatchChange.class);
        InOrder ordered = inOrder(callback);
        ordered.verify(callback, times(3)).onBatchChange(changeCaptor.capture());
        ordered.verifyNoMoreInteractions();

        List<BatchChange> changes = changeCaptor.getAllValues();
        assertEquals(3, changes.size());
        assertBatchChange(changes.get(0), ADD, "493021b1c9e0ecfebcd5ebce646a44138bc60b39",
                "add_file/add_file.txt", 20L);
        assertBatchChange(changes.get(1), ADD, "b1d99e581f0bb8138b8997f9268294e41faee05b",
                "modification/mod_file.txt", 33L);
        assertBatchChange(changes.get(2), ADD, "2e6b03cbbe90d3c9afcfc98967a5a71b4c9cff1a",
                "out_of_order/file.txt", 66L);
    }

    @Test
    public void testBatchChangesIncremental() {
        BatchChangeCallback callback = mock(BatchChangeCallback.class);
        manager.changes(new BatchChangesParameters.Builder(repository, TIMEOUTS)
                        .sinceCommitId("bf3346620ec9161977fc23dee1240a7991e64ca1")
                        .untilCommitId("409a2180fd95b744df8d817e770b7f8c853f78dc")
                        .build(),
                callback);

        ArgumentCaptor<BatchChange> changeCaptor = ArgumentCaptor.forClass(BatchChange.class);
        InOrder ordered = inOrder(callback);
        ordered.verify(callback, times(2)).onBatchChange(changeCaptor.capture());
        ordered.verifyNoMoreInteractions();

        List<BatchChange> changes = changeCaptor.getAllValues();
        assertEquals(2, changes.size());
        assertBatchChange(changes.get(0), DELETE, "b1d99e581f0bb8138b8997f9268294e41faee05b",
                "modification/mod_file.txt", 0L);
        assertBatchChange(changes.get(1), MODIFY, "30bda1ba1e8e99781b4fafcc450da84f97a29630",
                "out_of_order/file.txt", 30L);
    }

    @Test
    public void testBatchChangesForInvalidSinceCommitId() {
        thrown.expect(CommitNotFoundException.class);
        thrown.expectMessage("No such commit: 1");

        manager.changes(new BatchChangesParameters.Builder(repository, TIMEOUTS)
                        .sinceCommitId("1")
                        .untilCommitId("306d584aa574fa74901d3e288b2931b2c4f0e927")
                        .build(),
                mock(BatchChangeCallback.class));
    }

    @Test
    public void testBatchChangesForInvalidUntilCommitId() {
        thrown.expect(ObjectNotFoundException.class);
        thrown.expectMessage("No such object: 1");

        manager.changes(new BatchChangesParameters.Builder(repository, TIMEOUTS)
                        .untilCommitId("1")
                        .build(),
                mock(BatchChangeCallback.class));
    }

    private static void assertBatchChange(BatchChange batchChange, BatchChangeType expectedChangeType,
                                          String expectedContentId, String expectedPath, long expectedSize) {
        assertNotNull(batchChange);
        assertEquals(expectedChangeType, batchChange.getChangeType());
        assertEquals(expectedContentId, batchChange.getContentId());
        assertEquals(expectedPath, batchChange.getPath());
        assertEquals(expectedSize, batchChange.getSize());
    }
}
