package com.atlassian.bitbucket.mesh.git;

import com.atlassian.bitbucket.mesh.AbstractGrpcServiceTest;
import com.atlassian.bitbucket.mesh.repository.Repository;
import com.atlassian.bitbucket.mesh.rpc.v1.*;
import com.atlassian.bitbucket.mesh.test.annotation.Repo;
import com.google.common.collect.ImmutableList;
import com.google.protobuf.ByteString;
import io.grpc.StatusRuntimeException;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;

import java.util.Iterator;
import java.util.List;

import static com.atlassian.bitbucket.mesh.rpc.v1.RpcBatchChangeType.*;

public class GrpcBatchServiceTest extends AbstractGrpcServiceTest {

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

    private BatchServiceGrpc.BatchServiceBlockingStub client;
    @Repo
    private Repository repository;

    @Before
    public void setup() {
        client = BatchServiceGrpc.newBlockingStub(managedChannel);
    }

    @Test
    public void testBatchChangesWithSinceAndUntil() {
        List<RpcBatchChange> result = getBatchChanges(RpcBatchChangesRequest.newBuilder()
                .setSinceCommitish(ByteString.copyFromUtf8("bf3346620ec9161977fc23dee1240a7991e64ca1"))
                .setUntilCommitish(ByteString.copyFromUtf8("409a2180fd95b744df8d817e770b7f8c853f78dc")));

        assertEquals(2, result.size());
        assertBatchChange(result.get(0), BATCH_CHANGE_DELETE, "b1d99e581f0bb8138b8997f9268294e41faee05b",
                "modification/mod_file.txt", 0L);
        assertBatchChange(result.get(1), BATCH_CHANGE_MODIFY, "30bda1ba1e8e99781b4fafcc450da84f97a29630",
                "out_of_order/file.txt", 30L);
    }

    @Test
    public void testBatchChangesWithUntil() {
        List<RpcBatchChange> result = getBatchChanges(RpcBatchChangesRequest.newBuilder()
                .setUntilCommitish(ByteString.copyFromUtf8("6d8e9736357657738d15cd29069f21bf44553e19")));

        assertEquals(3, result.size());
        assertBatchChange(result.get(0), BATCH_CHANGE_ADD, "493021b1c9e0ecfebcd5ebce646a44138bc60b39",
                "add_file/add_file.txt", 20L);
        assertBatchChange(result.get(1), BATCH_CHANGE_ADD, "b1d99e581f0bb8138b8997f9268294e41faee05b",
                "modification/mod_file.txt", 33L);
        assertBatchChange(result.get(2), BATCH_CHANGE_ADD, "2e6b03cbbe90d3c9afcfc98967a5a71b4c9cff1a",
                "out_of_order/file.txt", 66L);
    }

    @Test
    public void testBatchChangesWithInvalidSinceCommitId() {
        thrown.expect(StatusRuntimeException.class);
        thrown.expectMessage("No such commit: 0000000000000000000000000000000000000000");

        getBatchChanges(RpcBatchChangesRequest.newBuilder()
                .setSinceCommitish(ByteString.copyFromUtf8("0000000000000000000000000000000000000000"))
                .setUntilCommitish(ByteString.copyFromUtf8("e26cab95a50f6e99d2c46c498a23cee307cc8e27")));
    }

    @Test
    public void testBatchChangesWithInvalidUntilCommitId() {
        thrown.expect(StatusRuntimeException.class);
        thrown.expectMessage("No such object: 0000000000000000000000000000000000000000");

        getBatchChanges(RpcBatchChangesRequest.newBuilder()
                .setUntilCommitish(ByteString.copyFromUtf8("0000000000000000000000000000000000000000")));
    }

    private static void assertBatchChange(RpcBatchChange batchChange, RpcBatchChangeType expectedChangeType,
                                          String expectedContentId, String expectedPath, long expectedSize) {
        assertNotNull(batchChange);
        assertEquals(expectedChangeType, batchChange.getChangeType());
        RpcFile file = batchChange.getFile();
        assertEquals(expectedContentId, file.getContentId());
        assertEquals(expectedPath, file.getPath().toStringUtf8());
        assertEquals(expectedSize, file.getSize());
    }

    private List<RpcBatchChange> getBatchChanges(RpcBatchChangesRequest.Builder requestBuilder) {
        ImmutableList.Builder<RpcBatchChange> builder = ImmutableList.builder();
        Iterator<RpcBatchChangesResponseFragment> it =
                client.changes(requestBuilder.setRepository(repository.getId()).build());
        while (it.hasNext()) {
            builder.addAll(it.next().getBatchChangesList());
        }
        return builder.build();
    }
}
