package com.atlassian.stash.internal.scm.git.protocol.http;

import com.atlassian.bitbucket.event.repository.RepositoryCloneEvent;
import com.atlassian.bitbucket.event.repository.RepositoryEvent;
import com.atlassian.bitbucket.event.repository.RepositoryPullEvent;
import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.mockito.MockitoUtils;
import com.atlassian.bitbucket.project.Project;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.request.RequestContext;
import com.atlassian.bitbucket.scm.CommandOutputHandler;
import com.atlassian.bitbucket.scm.git.GitScmConfig;
import com.atlassian.bitbucket.scm.git.command.GitCommand;
import com.atlassian.bitbucket.scm.git.command.GitCommandBuilderFactory;
import com.atlassian.bitbucket.scm.git.command.GitScmCommandBuilder;
import com.atlassian.bitbucket.scm.git.protocol.GitScmRequestType;
import com.atlassian.bitbucket.scm.git.protocol.UploadPackRequestAnalyzer;
import com.atlassian.event.api.EventPublisher;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.time.Duration;
import java.util.Enumeration;
import java.util.Optional;

import static java.util.Arrays.asList;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.*;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class HttpUploadPackTest {
    @Mock
    private UploadPackRequestAnalyzer analyzer;
    @Mock
    private GitCommandBuilderFactory builderFactory;
    @Mock
    private GitCommand<Void> command;
    @Mock
    private GitScmConfig config;
    @Mock
    private EventPublisher eventPublisher;
    @Mock
    private I18nService i18nService;
    @Mock
    private Project project;
    @Mock
    private Repository repository;
    @Mock
    private HttpServletRequest request;
    @Mock
    private RequestContext requestContext;
    @Mock
    private HttpServletResponse response;
    @Mock
    private ServletContext servletContext;

    private HttpUploadPack uploadPack;

    @Before
    public void setUp() throws Exception {
        GitScmCommandBuilder commandBuilder = mock(GitScmCommandBuilder.class, MockitoUtils.returnSelf(GitScmCommandBuilder.class));
        when(commandBuilder.build(any(CommandOutputHandler.class))).thenReturn(command);
        when(builderFactory.builder(any(Repository.class))).thenReturn(commandBuilder);
        when(config.getRepositoryDir(repository)).thenReturn(new File(""));
        when(request.getPathInfo()).thenReturn("/PROJECT/repo.git/git-upload-pack");
        Enumeration enumeration = mock(Enumeration.class);
        when(request.getHeaderNames()).thenReturn(enumeration);
        when(repository.getProject()).thenReturn(project);
        uploadPack = new HttpUploadPack(repository, builderFactory, analyzer, config, eventPublisher, i18nService,
                request, response, servletContext, requestContext);
    }

    @Test
    public void testPublishEventIfNotACloneRequest() throws Exception {
        when(analyzer.getType()).thenReturn(GitScmRequestType.FETCH);
        when(requestContext.getDuration()).thenReturn(Optional.of(Duration.ofMillis(1)));

        uploadPack.handleRequest();
        uploadPack.onSuccess();
        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
        verify(requestContext).addCleanupCallback(runnableCaptor.capture());
        runnableCaptor.getValue().run();

        RepositoryEvent event = GitSmartRequestTestBase.onSuccessCaptureEvent(eventPublisher, uploadPack);
        assertNotNull(event);
        assertThat(event, instanceOf(RepositoryPullEvent.class));
        assertSame(repository, event.getRepository());
        assertSame(uploadPack, event.getSource());
    }

    @Test
    public void testPublishEventIfACloneRequest() throws Exception {
        for (GitScmRequestType type : asList(GitScmRequestType.CLONE, GitScmRequestType.SHALLOW_CLONE)) {

            Mockito.reset(eventPublisher, analyzer, requestContext);
            when(requestContext.getDuration()).thenReturn(Optional.of(Duration.ofMillis(1)));
            when(analyzer.getType()).thenReturn(type);

            uploadPack.handleRequest();
            uploadPack.onSuccess();
            ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
            verify(requestContext).addCleanupCallback(runnableCaptor.capture());
            runnableCaptor.getValue().run();

            RepositoryEvent event = GitSmartRequestTestBase.onSuccessCaptureEvent(eventPublisher, uploadPack);

            assertNotNull(event);
            assertThat(event, instanceOf(RepositoryCloneEvent.class));
            assertSame(repository, event.getRepository());
            assertSame(uploadPack, event.getSource());
            reset(requestContext);
        }
    }

    @Test
    public void testIsOperationComplete() throws Exception {
        assertTrue(uploadPack.isOperationComplete());
    }
}
