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

import com.atlassian.bitbucket.content.*;
import com.atlassian.bitbucket.scm.CommandResult;
import com.atlassian.bitbucket.scm.CommandSummary;
import com.atlassian.utils.process.ProcessException;
import com.atlassian.utils.process.Watchdog;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;
import java.io.InputStream;

import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class DiffOutputHandlerTest {

    @Mock
    private DiffContentCallback callback;
    private DiffContext context;

    @Before
    public void setup() {
        context = new DiffContext.Builder("from", "to")
                .contextLines(3)
                .maxLineLength(100)
                .maxLines(10)
                .whitespace(DiffWhitespace.SHOW)
                .build();
    }

    @Test
    public void testMixedCrAndCrLfMix() throws IOException, ProcessException {
        // The purpose of this test is to ensure that BBS doesn't regress when the diff output handler is modified.
        // It fails in a particularly annoying way which resulting in the exception (IllegalStateException)
        // thrown by the DiffOutputHandler being swallowed because it was raised after BBS
        // started streaming chunks of diff to the browser.

        // This input file has an interesting mix of CR, CRLF and CF combination
        // Linux file command returns:
        //      unified diff output, UTF-8 Unicode text, with CRLF, CR, LF line terminators
        //
        // This particular file broke the Diff output parser before BSERV-8424 was fixed.
        InputStream diffStream = new ClassPathResource("git-diff-with-CR-and-CRLF.out", getClass()).getInputStream();

        DiffContext ctx = new DiffContext.Builder(
                "004059d250e584215fd78ee155a2c1c050f898d0",
                "60467f65de5dc30cec260d1571bb74be8d36c92d")
                .contextLines(10)
                .maxLines(10000)
                .maxLineLength(5000)
                .whitespace(DiffWhitespace.SHOW).build();

        DiffContentCallback callback = mock(DiffContentCallback.class);
        DiffOutputHandler diffOutputHandler = new DiffOutputHandler(callback, ctx);
        diffOutputHandler.setWatchdog(mock(Watchdog.class));
        diffOutputHandler.process(diffStream);
        diffOutputHandler.onComplete(new CommandSummary.Builder(CommandResult.SUCCEEDED).build());

        verify(callback).onStart(same(ctx));
        verify(callback).onDiffStart(any(Path.class), any(Path.class));
        verify(callback, times(4)).onHunkStart(anyInt(), anyInt(), anyInt(), anyInt());
        verify(callback, times(21)).onSegmentStart(any(DiffSegmentType.class));
        verify(callback, times(199)).onSegmentLine(anyString(), any(), anyBoolean());
        verify(callback, times(21)).onSegmentEnd(anyBoolean());
        verify(callback, times(4)).onHunkEnd(anyBoolean());
        verify(callback).onDiffEnd(anyBoolean());

        ArgumentCaptor<DiffSummary> summary = ArgumentCaptor.forClass(DiffSummary.class);
        verify(callback).onEnd(summary.capture());

        CommandResult result = summary.getValue().getResult();
        Assert.assertTrue(result == CommandResult.SUCCEEDED);
    }

    @Test
    public void testEscapedNamesV2() throws Exception {
        process("git-diff-escaped-file.out");

        verify(callback).onStart(same(context));
        verify(callback, times(3)).onDiffStart(any(Path.class), any(Path.class));
        verify(callback, times(1)).onDiffStart(new SimplePath("SourceView/foo\" onerror=\"alert(666)\""), null);
        verify(callback, times(1)).onDiffStart(null, new SimplePath("\u55b6\u5c02.txt"));
        Path expected = new SimplePath("\u55b6\u5c02 \".txt");
        verify(callback, times(1)).onDiffStart(expected, expected);
        verify(callback).onEnd(isNotNull(DiffSummary.class));
    }

    @Test
    public void testSpaceNames() throws Exception {
        process("git-diff-space-trailingtab.out");
        verify(callback, times(1)).onDiffStart(null, new SimplePath("a b.txt"));
    }

    private void process(String filename) throws Exception {
        DiffOutputHandler handler = new DiffOutputHandler(callback, context);
        handler.setWatchdog(mock(Watchdog.class));
        handler.process(new ClassPathResource(filename, getClass()).getInputStream());
        handler.onComplete(new CommandSummary.Builder(CommandResult.SUCCEEDED).build());
    }
}
