package com.atlassian.stash.internal.content;

import com.atlassian.bitbucket.AbstractServiceTest;
import com.atlassian.bitbucket.content.*;
import com.atlassian.bitbucket.hamcrest.SimpleTypeSafeMatcher;
import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.io.TypeAwareOutputSupplier;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.scm.*;
import com.atlassian.bitbucket.user.UserService;
import com.atlassian.bitbucket.util.PageRequest;
import com.google.common.collect.Lists;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import java.util.List;

import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
@SuppressWarnings("deprecation")
public class DefaultContentServiceTest extends AbstractServiceTest {

    @Mock
    private ScmCommandFactory commandFactory;
    @Mock
    private I18nService i18nService;
    @Mock
    private PageRequest pageRequest;
    @Mock
    private Repository repository;
    @Mock
    private ScmService scmService;
    @InjectMocks
    private DefaultContentService service;
    @Mock
    private UserService userService;

    public DefaultContentServiceTest() {
        super(DefaultContentService.class, ContentService.class);
    }

    @Before
    public void setup() {
        when(scmService.getCommandFactory(same(repository))).thenReturn(commandFactory);
    }

    @Test
    public void testGetBlame() {
        List<Blame> list = Lists.newArrayList();

        @SuppressWarnings("unchecked")
        Command<List<Blame>> command = mock(Command.class);
        when(command.call()).thenReturn(list);

        when(commandFactory.blame(argThat(new SimpleTypeSafeMatcher<BlameCommandParameters>() {

            @Override
            protected boolean matchesSafely(BlameCommandParameters parameter) {
                return "master".equals(parameter.getCommitId()) &&
                        "pom.xml".equals(parameter.getPath());
            }
        }), same(pageRequest)))
                .thenReturn(command);

        assertSame(list, service.getBlame(repository, "master", "pom.xml", pageRequest));
    }

    @Test
    public void testGetDirectory() throws Exception {
        @SuppressWarnings("unchecked")
        Command<Void> command = mock(Command.class);

        ContentTreeCallback callback = mock(ContentTreeCallback.class);
        when(commandFactory.directory(argThat(new SimpleTypeSafeMatcher<DirectoryCommandParameters>() {

            @Override
            protected boolean matchesSafely(DirectoryCommandParameters parameter) {
                return "master".equals(parameter.getCommitId()) &&
                        "src".equals(parameter.getPath()) &&
                        !parameter.isRecursive();
            }
        }), same(callback), same(pageRequest)))
                .thenReturn(command);
        when(pageRequest.buildRestrictedPageRequest(eq(50))).thenReturn(pageRequest);

        setPageLimit(service, "maxDirectoryChildren", 50);
        service.streamDirectory(repository, "master", "src", false, callback, pageRequest);

        verify(pageRequest).buildRestrictedPageRequest(eq(50));
        verifyZeroInteractions(callback);
    }

    @Test
    public void testGetDirectoryRecursive() throws Exception {
        @SuppressWarnings("unchecked")
        Command<Void> command = mock(Command.class);

        ContentTreeCallback callback = mock(ContentTreeCallback.class);
        when(commandFactory.directory(argThat(new SimpleTypeSafeMatcher<DirectoryCommandParameters>() {
            @Override
            protected boolean matchesSafely(DirectoryCommandParameters parameter) {
                return "master".equals(parameter.getCommitId()) &&
                        !parameter.hasPath() &&
                        parameter.isRecursive();
            }
        }), same(callback), same(pageRequest)))
            .thenReturn(command);
        when(pageRequest.buildRestrictedPageRequest(eq(100))).thenReturn(pageRequest);

        setPageLimit(service, "maxDirectoryRecursiveChildren", 100);
        service.streamDirectory(repository, "master", null, true, callback, pageRequest);

        verify(pageRequest).buildRestrictedPageRequest(eq(100));
        verifyZeroInteractions(callback);
    }

    @Test
    public void testGetType() {
        @SuppressWarnings("unchecked")
        Command<ContentTreeNode.Type> command = mock(Command.class);
        when(command.call()).thenReturn(ContentTreeNode.Type.DIRECTORY);

        when(commandFactory.type(argThat(new SimpleTypeSafeMatcher<TypeCommandParameters>() {

            @Override
            protected boolean matchesSafely(TypeCommandParameters parameter) {
                return "master".equals(parameter.getCommitId()) && !parameter.hasPath();
            }
        }))).thenReturn(command);

        assertSame(ContentTreeNode.Type.DIRECTORY, service.getType(repository, "master", null));
    }

    @Test
    public void testStreamFile() throws Exception {
        FileContentCallback callback = mock(FileContentCallback.class);
        @SuppressWarnings("unchecked")
        Command<Void> command = mock(Command.class);

        when(commandFactory.file(argThat(new SimpleTypeSafeMatcher<FileCommandParameters>() {

            @Override
            protected boolean matchesSafely(FileCommandParameters parameter) {
                return parameter.isAnnotated() &&
                        5000 == parameter.getMaxLineLength() &&
                        "master".equals(parameter.getCommitId()) &&
                        "pom.xml".equals(parameter.getPath());
            }
        }), isNotNull(FileContentCallback.class), same(pageRequest))).thenReturn(command);
        when(pageRequest.buildRestrictedPageRequest(eq(500))).thenReturn(pageRequest);

        setMaxLineLength(5000);
        setMaxSourceLines(500);
        service.streamFile(repository, "master", "pom.xml", pageRequest, true, callback);

        verify(command).call();
        verifyNoMoreInteractions(command);
        verify(pageRequest).buildRestrictedPageRequest(eq(500));
    }

    @Test
    public void testStreamFileUnpaged() {
        TypeAwareOutputSupplier streamSupplier = mock(TypeAwareOutputSupplier.class);

        @SuppressWarnings("unchecked")
        Command<Void> command = mock(Command.class);

        when(commandFactory.rawFile(argThat(new SimpleTypeSafeMatcher<RawFileCommandParameters>() {

            @Override
            protected boolean matchesSafely(RawFileCommandParameters parameter) {
                return "master".equals(parameter.getCommitId()) &&
                        "pom.xml".equals(parameter.getPath());
            }
        }), same(streamSupplier))).thenReturn(command);

        service.streamFile(repository, "master", "pom.xml", streamSupplier);
        verify(command).call();
        verifyNoMoreInteractions(command);
    }

    private void setMaxLineLength(int length) throws Exception {
        setPageLimit(service, "maxLineLength", length);
    }

    private void setMaxSourceLines(int lines) throws Exception {
        setPageLimit(service, "maxSourceLines", lines);
    }
}
