package com.atlassian.stash.internal.scm.git.command.catfile;

import com.atlassian.bitbucket.content.ContentTreeNode;
import com.atlassian.bitbucket.content.FileContentCallback;
import com.atlassian.bitbucket.content.FileContext;
import com.atlassian.bitbucket.content.FileSummary;
import com.atlassian.bitbucket.io.SingleLineOutputHandler;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.scm.Command;
import com.atlassian.bitbucket.scm.CommandResult;
import com.atlassian.bitbucket.scm.annotation.Repo;
import com.atlassian.bitbucket.scm.git.command.GitCommand;
import com.atlassian.bitbucket.scm.git.command.GitCommandBuilderFactory;
import com.atlassian.bitbucket.scm.git.command.catfile.GitCatFile;
import com.atlassian.bitbucket.util.PageUtils;
import com.atlassian.stash.internal.scm.git.AbstractGitTest;
import com.atlassian.stash.internal.scm.git.GitFactoriesTestHelper.StringInputHandler;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InOrder;
import org.springframework.beans.factory.annotation.Autowired;

import static org.mockito.Mockito.*;

public class CatFileCommandTest extends AbstractGitTest {

    @Autowired
    private GitCommandBuilderFactory builderFactory;
    @Captor
    ArgumentCaptor<FileSummary> summaryCaptor;
    @Repo
    private Repository repository;

    @Test
    public void testPrettyWithFile() throws Exception {
        FileContentCallback callback = mock(FileContentCallback.class);
        when(callback.onLine(anyInt(), anyString(), anyBoolean())).thenReturn(true); //Return true to not get canceled

        FileContext context = new FileContext.Builder("master")
                .maxLineLength(15)
                .build();

        createBuilder().pretty()
                .object("master", "modification/mod_file.txt")
                .build(new CallbackCatFileOutputHandler(callback, context, PageUtils.newRequest(0, 10)))
                .call();

        ArgumentCaptor<FileSummary> summaryCaptor = ArgumentCaptor.forClass(FileSummary.class);

        InOrder ordered = inOrder(callback);
        ordered.verify(callback).onStart(same(context));
        ordered.verify(callback).onLine(eq(1), eq("this is a test "), eq(true));
        ordered.verify(callback).onLine(eq(2), eq("another line"), eq(false));
        ordered.verify(callback).onEnd(summaryCaptor.capture());
        ordered.verifyNoMoreInteractions();

        FileSummary summary = summaryCaptor.getValue();
        assertNotNull(summary);
        assertEquals(10, summary.getPageRequest().getLimit());
        assertEquals(0, summary.getPageRequest().getStart());
        assertEquals(2, summary.getSize());
        assertSame(CommandResult.SUCCEEDED, summary.getResult());
        assertTrue(summary.isLastPage());
    }

    @Test
    public void testPrettyWithFileOnCancel() throws Exception {
        FileContentCallback callback = mock(FileContentCallback.class); //onLine will return false by default
        FileContext context = new FileContext.Builder("master")
                .maxLineLength(15)
                .build();

        createBuilder().pretty()
                .object("master", "modification/mod_file.txt")
                .build(new CallbackCatFileOutputHandler(callback, context, PageUtils.newRequest(0, 10)))
                .call();

        ArgumentCaptor<FileSummary> summaryCaptor = ArgumentCaptor.forClass(FileSummary.class);

        InOrder ordered = inOrder(callback);
        ordered.verify(callback).onStart(same(context));
        ordered.verify(callback).onLine(eq(1), eq("this is a test "), eq(true));
        ordered.verify(callback).onEnd(summaryCaptor.capture());
        ordered.verifyNoMoreInteractions();

        FileSummary summary = summaryCaptor.getValue();
        assertNotNull(summary);
        assertEquals(10, summary.getPageRequest().getLimit());
        assertEquals(0, summary.getPageRequest().getStart());
        assertEquals(1, summary.getSize());
        assertSame(CommandResult.CANCELED, summary.getResult());
        assertTrue(summary.isLastPage());
    }

    @Test
    public void testTypeWithBlob() {
        Command<ContentTreeNode.Type> command = createBuilder().type()
                .object("master", "modification/mod_file.txt")
                .build(new TypeCatFileOutputHandler());
        assertSame(ContentTreeNode.Type.FILE, command.call());
    }

    @Test
    public void testTypeWithCommit() {
        Command<ContentTreeNode.Type> command = createBuilder().type()
                .object("master")
                .build(new TypeCatFileOutputHandler());
        assertSame(ContentTreeNode.Type.SUBMODULE, command.call());
    }

    @Test
    public void testTypeWithDirectory() {
        Command<ContentTreeNode.Type> command = createBuilder().type()
                .object("master:")
                .build(new TypeCatFileOutputHandler());
        assertSame(ContentTreeNode.Type.DIRECTORY, command.call());
    }

    @Test
    public void testTypeWithTag() {
        Command<ContentTreeNode.Type> command = createBuilder().type()
                .object("signed_tag")
                .build(new TypeCatFileOutputHandler());
        assertNull(command.call());
    }

    @Test
    public void testTypeWithTree() {
        Command<ContentTreeNode.Type> command = createBuilder().type()
                .object("master:")
                .build(new TypeCatFileOutputHandler());
        assertSame(ContentTreeNode.Type.DIRECTORY, command.call());
    }

    @Test
    public void testBatchOneFile() {
        String result = callBatch("30bda1ba1e8e99781b4fafcc450da84f97a29630");
        assertEquals("30bda1ba1e8e99781b4fafcc450da84f97a29630 blob 30\n" +
                        "out of order commits scenario\n",
                result);
    }

    @Test
    public void testBatchMultipleFiles() {
        String result = callBatch("30bda1ba1e8e99781b4fafcc450da84f97a29630\n493021b1c9e0ecfebcd5ebce646a44138bc60b39");
        assertEquals("30bda1ba1e8e99781b4fafcc450da84f97a29630 blob 30\n" +
                        "out of order commits scenario\n\n" +
                        "493021b1c9e0ecfebcd5ebce646a44138bc60b39 blob 20\n" +
                        "this is a test file\n",
                result);
    }

    @Test
    public void testBatchMissing() {
        String result = callBatch("0000000000000000000000000000000000000000\n30bda1ba1e8e99781b4fafcc450da84f97a29630");
        assertEquals("0000000000000000000000000000000000000000 missing\n" +
                "30bda1ba1e8e99781b4fafcc450da84f97a29630 blob 30\n" +
                "out of order commits scenario\n", result);
    }

    @Test
    public void testBatchCheckMultiple() {
        String result = callBatchCheck("30bda1ba1e8e99781b4fafcc450da84f97a29630\n493021b1c9e0ecfebcd5ebce646a44138bc60b39");
        assertEquals("30bda1ba1e8e99781b4fafcc450da84f97a29630 blob 30\n" +
                        "493021b1c9e0ecfebcd5ebce646a44138bc60b39 blob 20",
                result);
    }

    @Test
    public void testBatchCheckMissing() {
        String result = callBatchCheck("0000000000000000000000000000000000000000\n30bda1ba1e8e99781b4fafcc450da84f97a29630");
        assertEquals("0000000000000000000000000000000000000000 missing\n" +
                "30bda1ba1e8e99781b4fafcc450da84f97a29630 blob 30", result);
    }

    private String callBatch(String input) {
        GitCommand<String> command = createBuilder()
                .batch(new StringInputHandler(input))
                .build(new SingleLineOutputHandler());
        return command.call();
    }

    private String callBatchCheck(String input) {
        GitCommand<String> command = createBuilder()
                .batchCheck(new StringInputHandler(input))
                .build(new SingleLineOutputHandler());
        return command.call();
    }

    private GitCatFile createBuilder() {
        return builderFactory.builder(repository).catFile();
    }
}
