package com.atlassian.bitbucket.mesh.git;

import com.atlassian.bitbucket.mesh.git.builder.GitProcessBuilderSupport;
import com.atlassian.bitbucket.mesh.git.hook.GitHookService;
import com.atlassian.bitbucket.mesh.git.hook.HookRequestHandle;
import com.atlassian.bitbucket.mesh.process.OsProcess;
import com.atlassian.bitbucket.mesh.repository.Repository;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcHostingOperationType;
import com.atlassian.bitbucket.mesh.rpc.v1.RpcStreamChunk;
import org.junit.Test;
import org.slf4j.Logger;

import javax.annotation.Nonnull;
import java.util.function.Supplier;

import static com.atlassian.bitbucket.mesh.rpc.v1.RpcHostingOperationType.HOSTING_OP_PUSH;
import static com.atlassian.bitbucket.mesh.rpc.v1.RpcHostingOperationType.HOSTING_OP_UNSPECIFIED;
import static org.mockito.Mockito.*;

@SuppressWarnings("rawtypes")
public class AbstractHostingFragmentHandlerTest {

    @SuppressWarnings("rawtypes")
    @Test
    public void testLogsWhenHooksAreNotInvokedForDefaultWriteOperation() {
        Logger logger = mock(Logger.class);
        Repository repository = mock(Repository.class);

        HookRequestHandle hookHandle = mock(HookRequestHandle.class);
        when(hookHandle.getRepository()).thenReturn(repository);

        GitHookService hookService = mock(GitHookService.class);
        when(hookService.registerRequest(any(Repository.class), any())).thenReturn(hookHandle);

        AbstractHostingFragmentHandler handler = createHandler(HOSTING_OP_PUSH, hookService, logger, repository);

        handler.getResult(0);

        verify(logger, times(1)).warn(anyString(), eq(repository));
    }

    @Test
    public void testLogsWhenHooksAreNotInvokedForCustomWriteOperation() {
        Logger logger = mock(Logger.class);
        Repository repository = mock(Repository.class);

        HookRequestHandle hookHandle = mock(HookRequestHandle.class);
        when(hookHandle.getRepository()).thenReturn(repository);

        GitHookService hookService = mock(GitHookService.class);
        when(hookService.registerRequest(any(Repository.class), any())).thenReturn(hookHandle);

        AbstractHostingFragmentHandler handler = createHandler(HOSTING_OP_UNSPECIFIED, hookService, logger, repository, () -> true);

        handler.getResult(0);

        verify(logger, times(1)).warn(anyString(), eq(repository));
    }

    @Test
    public void testDoesNotLogHooksAreNotInvokedForDefaultNonUpdateOperation() {
        Logger logger = mock(Logger.class);
        Repository repository = mock(Repository.class);

        HookRequestHandle hookHandle = mock(HookRequestHandle.class);
        when(hookHandle.getRepository()).thenReturn(repository);

        GitHookService hookService = mock(GitHookService.class);
        when(hookService.registerRequest(any(Repository.class), any())).thenReturn(hookHandle);

        AbstractHostingFragmentHandler handler = createHandler(HOSTING_OP_UNSPECIFIED, hookService, logger, repository);

        handler.getResult(0);

        verify(logger, never()).warn(anyString(), any(Repository.class));
    }

    @Test
    public void testDoesNotLogHooksAreNotInvokedForCustomWriteOperation() {
        Logger logger = mock(Logger.class);
        Repository repository = mock(Repository.class);

        HookRequestHandle hookHandle = mock(HookRequestHandle.class);
        when(hookHandle.getRepository()).thenReturn(repository);

        GitHookService hookService = mock(GitHookService.class);
        when(hookService.registerRequest(any(Repository.class), any())).thenReturn(hookHandle);

        AbstractHostingFragmentHandler handler = createHandler(HOSTING_OP_PUSH, hookService, logger, repository, () -> false);

        handler.getResult(0);

        verify(logger, never()).warn(anyString(), eq(repository));
    }

    @Test
    public void testLogsWhenHooksAreNotInvokedForFailedWrite() {
        Logger logger = mock(Logger.class);
        Repository repository = mock(Repository.class);

        HookRequestHandle hookHandle = mock(HookRequestHandle.class);
        when(hookHandle.getRepository()).thenReturn(repository);

        GitHookService hookService = mock(GitHookService.class);
        when(hookService.registerRequest(any(Repository.class), any())).thenReturn(hookHandle);

        AbstractHostingFragmentHandler handler = createHandler(HOSTING_OP_PUSH, hookService, logger, repository);

        handler.getResult(143);

        verify(logger, never()).warn(anyString(), eq(repository));
    }

    private AbstractHostingFragmentHandler createHandler(RpcHostingOperationType operationType,
                                                         GitHookService hookService, Logger logger,
                                                         Repository repository) {
        return new AbstractHostingFragmentHandler(operationType, hookService) {

            {
                registerHookRequest(repository);
            }

            @Nonnull
            @Override
            public GitProcessBuilderSupport<?> createBuilder(@Nonnull Object fragment) {
                throw new UnsupportedOperationException("Not implemented yet");
            }

            @Nonnull
            @Override
            public Object createResultFragment(int exitCode) {
                throw new UnsupportedOperationException("Not implemented yet");
            }

            @Nonnull
            @Override
            public Object createStartFragment(@Nonnull OsProcess process) {
                throw new UnsupportedOperationException("Not implemented yet");
            }

            @Nonnull
            @Override
            public Object createStderrFragment(@Nonnull RpcStreamChunk stderr) {
                throw new UnsupportedOperationException("Not implemented yet");
            }

            @Nonnull
            @Override
            public Object createStdoutFragment(@Nonnull RpcStreamChunk stdout) {
                throw new UnsupportedOperationException("Not implemented yet");
            }

            @Override
            public RpcStreamChunk onFragment(@Nonnull Object fragment) {
                throw new UnsupportedOperationException("Not implemented yet");
            }

            @Override
            protected Logger getLogger() {
                return logger;
            }
        };
    }

    private AbstractHostingFragmentHandler createHandler(RpcHostingOperationType operationType,
                                                         GitHookService hookService, Logger logger,
                                                         Repository repository, Supplier<Boolean> isUpdate) {
        return new AbstractHostingFragmentHandler(operationType, hookService) {

            {
                registerHookRequest(repository);
            }

            @Nonnull
            @Override
            public GitProcessBuilderSupport<?> createBuilder(@Nonnull Object fragment) {
                throw new UnsupportedOperationException("Not implemented yet");
            }

            @Nonnull
            @Override
            public Object createResultFragment(int exitCode) {
                throw new UnsupportedOperationException("Not implemented yet");
            }

            @Nonnull
            @Override
            public Object createStartFragment(@Nonnull OsProcess process) {
                throw new UnsupportedOperationException("Not implemented yet");
            }

            @Nonnull
            @Override
            public Object createStderrFragment(@Nonnull RpcStreamChunk stderr) {
                throw new UnsupportedOperationException("Not implemented yet");
            }

            @Nonnull
            @Override
            public Object createStdoutFragment(@Nonnull RpcStreamChunk stdout) {
                throw new UnsupportedOperationException("Not implemented yet");
            }

            @Override
            public RpcStreamChunk onFragment(@Nonnull Object fragment) {
                throw new UnsupportedOperationException("Not implemented yet");
            }

            @Override
            protected Logger getLogger() {
                return logger;
            }

            @Override
            protected boolean isUpdate() {
                return isUpdate.get();
            }
        };
    }
}