package com.atlassian.bitbucket.mesh.transaction;

import com.atlassian.bitbucket.mesh.config.HostingConfig;
import com.atlassian.bitbucket.mesh.execution.ExecutionContext;
import com.atlassian.bitbucket.mesh.execution.ExecutionManager;
import com.atlassian.bitbucket.mesh.git.GitAgent;
import com.atlassian.bitbucket.mesh.git.hosting.GitReceivePackFilter;
import com.atlassian.bitbucket.mesh.repository.Repository;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcStreamChunk;
import com.google.protobuf.ByteString;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import java.util.Optional;

import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class TransactionGitReceivePackListenerTest {

    private static final RpcStreamChunk INITIAL_PUSH_ATOMIC = chunk(
            "00c50000000000000000000000000000000000000000 12deed740128377130fcb04c1ac8793f67b67551 " +
                    "refs/heads/BBSDEV-24445-initial-push\0 report-status-v2 side-band-64k atomic object-format=sha1 " +
                    "agent=git/2.33.0");
    private static final RpcStreamChunk INITIAL_PUSH_NON_ATOMIC = chunk(
            "00be0000000000000000000000000000000000000000 12deed740128377130fcb04c1ac8793f67b67551 " +
                    "refs/heads/BBSDEV-24445-initial-push\0 report-status-v2 side-band-64k object-format=sha1 " +
                    "agent=git/2.33.0");
    private static final RpcStreamChunk INITIAL_PUSH_NON_ATOMIC_ENRICHED = chunk(
            "00c50000000000000000000000000000000000000000 12deed740128377130fcb04c1ac8793f67b67551 " +
                    "refs/heads/BBSDEV-24445-initial-push\0 atomic report-status-v2 side-band-64k object-format=sha1 " +
                    "agent=git/2.33.0");
    private static final RpcStreamChunk NON_INITIAL_PUSH_NON_ATOMIC = chunk(
            "00c56af8f3f176f233c30e26a86649de830d21220252 12deed740128377130fcb04c1ac8793f67b67551 " +
                    "refs/heads/BBSDEV-24445-initial-push\0 report-status-v2 side-band-64k atomic object-format=sha1 " +
                    "agent=git/2.33.0");
    private static final RpcStreamChunk PUSH_CERT = chunk(
            "0051push-cert\0 report-status-v2 side-band-64k object-format=sha1 agent=git/2.33.0");

    @Mock
    private GitAgent agent;
    @Mock
    private ExecutionManager executionManager;
    @Mock
    private HostingConfig hostingConfig;
    private TransactionGitReceivePackListener listener;
    @Mock
    private Repository repository;
    @Mock
    private TransactionManager transactionManager;

    @Before
    public void setUp() {
        when(agent.isEmpty(repository)).thenReturn(true);
        ExecutionContext executionContext = mock(ExecutionContext.class);
        when(executionManager.getContext()).thenReturn(Optional.of(executionContext));
        when(hostingConfig.isAtomicInitialPush()).thenReturn(true);
        listener = new TransactionGitReceivePackListener(agent, executionManager, hostingConfig, transactionManager);
    }

    @Test
    public void testNonAtomicInitialPushWhenEnabled() {
        when(hostingConfig.isAtomicInitialPush()).thenReturn(true);
        GitReceivePackFilter filter = listener.createFilter(repository);

        assertNotNull(filter);
        assertTrue(filter.wantsPacketLines());

        RpcStreamChunk enriched = filter.onPacketLine(INITIAL_PUSH_NON_ATOMIC);
        assertEquals(INITIAL_PUSH_NON_ATOMIC_ENRICHED, enriched);
    }

    @Test
    public void testDoesNotEnrichIfNotEmpty() {
        when(agent.isEmpty(repository)).thenReturn(false);

        doTestDoesNotFilter(INITIAL_PUSH_NON_ATOMIC);
    }

    @Test
    public void testDoesNotEnrichIfNotNewRef() {
        doTestDoesNotFilter(NON_INITIAL_PUSH_NON_ATOMIC);
    }

    @Test
    public void testDoesNotEnrichIfAlreadyAtomic() {
        doTestDoesNotFilter(INITIAL_PUSH_ATOMIC);
    }

    @Test
    public void testDoesNotEnrichIfAtomicInitialPushDisabled() {
        when(hostingConfig.isAtomicInitialPush()).thenReturn(false);

        doTestDoesNotFilter(INITIAL_PUSH_ATOMIC);
    }

    @Test
    public void testDoesNotEnrichPushCert() {
        doTestDoesNotFilter(PUSH_CERT);
    }

    private void doTestDoesNotFilter(RpcStreamChunk packetLine) {
        GitReceivePackFilter filter = listener.createFilter(repository);

        assertNotNull(filter);

        RpcStreamChunk filtered = filter.onPacketLine(packetLine);
        assertEquals("Should not have injected atomic", packetLine, filtered);
    }

    private static RpcStreamChunk chunk(String data) {
        return RpcStreamChunk.newBuilder()
                .setData(ByteString.copyFromUtf8(data))
                .build();
    }
}