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

import com.atlassian.bitbucket.commit.Commit;
import com.atlassian.bitbucket.commit.CommitCallback;
import com.atlassian.bitbucket.commit.MinimalCommit;
import com.atlassian.bitbucket.content.ChangeType;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.scm.CommitsCommandParameters;
import com.atlassian.bitbucket.util.PageRequest;
import com.atlassian.bitbucket.util.PageRequestImpl;
import com.atlassian.utils.process.Watchdog;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.io.ClassPathResource;

import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.atlassian.stash.internal.scm.git.command.revlist.RevListCommandUtils.assertCommit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class PagedCommitRevListOutputHandlerTest {

    private PageRequest pageRequest;
    @Mock
    private Repository repository;
    @Mock
    private CommitsCommandParameters commandParameters;

    @Before
    public void setup() {
        when(commandParameters.isFollowingRenames()).thenReturn(true);
        when(commandParameters.isWithMessages()).thenReturn(true);
        when(commandParameters.getMaxMessageLength()).thenReturn(1000);
        pageRequest = new PageRequestImpl(0, 100);
    }

    @Test
    public void testExecute() throws Exception {
        when(commandParameters.isFollowingRenames()).thenReturn(false);

        PagedCommitRevListOutputHandler handler = new PagedCommitRevListOutputHandler(repository, commandParameters, pageRequest);
        handler.setWatchdog(mock(Watchdog.class));
        handler.process(new ClassPathResource("git-rev-list-commit-callback.out", getClass()).getInputStream());

        List<Commit> commits = new ArrayList<>();
        Iterables.addAll(commits, handler.getOutput().getValues());
        assertEquals(3, commits.size());

        assertCommit(commits.get(0), "Chris Szmajda", "cszmajda@atlassian.com", 1468282180,
                "91a15d6dc3f2c089b867589774969075b317e7e9",
                "Automatic merge from release/4.8 -> master",
                null, "9d262e0bea03e6da5751ebefbd143be29caf53f5", "7daedb446369adf34d2ad170e3d34c1af7742c80");

        assertCommit(commits.get(1), "Chris Szmajda", "cszmajda@atlassian.com", 1468282177,
                "7daedb446369adf34d2ad170e3d34c1af7742c80",
                "Merge pull request #8126 in STASH/stash from BSERVDEV-13244-zero-downtime-backup-developer-docs to release/4.8",
                null, "708de963258716ee98c87b5ee4ed6c187990c105", "513f807ef362634099856f61fd8c2af006f42b97");

        assertCommit(commits.get(2), "Marcin Szczepanski", "mszczepanski@atlassian.com", 1468281647,
                "9d262e0bea03e6da5751ebefbd143be29caf53f5",
                "Automatic merge from release/4.8 -> master",
                null, "d378878026d6cc8268153c7bd81d8ca7606a8ebd", "708de963258716ee98c87b5ee4ed6c187990c105");
    }

    @Test
    public void testExecuteMovedFile() throws Exception {
        String path = "c";
        when(commandParameters.getPaths()).thenReturn(Collections.singleton(path));

        PagedCommitRevListOutputHandler handler = new PagedCommitRevListOutputHandler(repository, commandParameters, pageRequest);
        handler.setWatchdog(mock(Watchdog.class));
        handler.process(new ClassPathResource("git-rev-list-commit-move-callback.out", getClass()).getInputStream());

        List<Commit> commits = new ArrayList<>();
        Iterables.addAll(commits, handler.getOutput().getValues());
        assertEquals(4, commits.size());

        String srcPath = "a";
        assertCommit(commits.get(0), "Christian Rolf", "crolf@atlassian.com", 1468552101,
                "45d84cb6e4f0c3672c8f3a383cf914cdc6463503",
                "Move a to c",
                ImmutableMap.of(
                        "type", ChangeType.MOVE,
                        "path", path,
                        "srcPath", srcPath,
                        "percentUnchanged", 100),
                "984b34cfb8c1b5cc4c43690ea390c5d584666829");

        String srcPathFirstMove = "b";
        assertCommit(commits.get(1), "Christian Rolf", "crolf@atlassian.com", 1468552068,
                "984b34cfb8c1b5cc4c43690ea390c5d584666829",
                "Move b back to a",
                ImmutableMap.of(
                        "type", ChangeType.MOVE,
                        "path", srcPath,
                        "srcPath", srcPathFirstMove,
                        "percentUnchanged", 100),
                "22c070f60616653931baec9a64fa7a357fde928f");

        assertCommit(commits.get(2), "Christian Rolf", "crolf@atlassian.com", 1468552028,
                "22c070f60616653931baec9a64fa7a357fde928f",
                "Move a to b",
                ImmutableMap.of(
                        "type", ChangeType.MOVE,
                        "path", srcPathFirstMove,
                        "srcPath", srcPath,
                        "percentUnchanged", 100),
                "fba1aa558a928e678d1208e6c1f0be3fb8f84f6f");

        assertCommit(commits.get(3), "Christian Rolf", "crolf@atlassian.com", 1468552004,
                "fba1aa558a928e678d1208e6c1f0be3fb8f84f6f",
                "First commit",
                ImmutableMap.of(
                        "type", ChangeType.ADD,
                        "path", srcPath));
    }

    @Test
    public void testExecuteDirectoryBecameFile() throws Exception {
        String path = "dir2";
        when(commandParameters.getPaths()).thenReturn(Collections.singleton(path));

        PagedCommitRevListOutputHandler handler = new PagedCommitRevListOutputHandler(repository, commandParameters, pageRequest);
        handler.setWatchdog(mock(Watchdog.class));
        handler.process(new ClassPathResource("git-rev-list-commit-dir-became-file-callback.out", getClass()).getInputStream());

        List<Commit> commits = new ArrayList<>();
        Iterables.addAll(commits, handler.getOutput().getValues());
        assertEquals(3, commits.size());

        String srcPath = "dir";
        assertCommit(commits.get(0), "Christian Rolf", "crolf@atlassian.com", 1468552522,
                "59270574dcc4f4f90388a7b7a5e40b56a511cdeb", "Moved dir to dir2",
                ImmutableMap.of(
                        "type", ChangeType.MOVE,
                        "path", path,
                        "srcPath", srcPath,
                        "percentUnchanged", 100),
                "0e087c6b70b9fc2a283947de677ae19b8cd0f28f");

        assertCommit(commits.get(1), "Christian Rolf", "crolf@atlassian.com", 1468552449,
                "0e087c6b70b9fc2a283947de677ae19b8cd0f28f", "Replaced directory with file",
                ImmutableMap.of(
                        "type", ChangeType.ADD,
                        "path", srcPath),
                "5720e0b5a0f0a8cf5c87db568be4094ef6106b51");

        assertCommit(commits.get(2), "Christian Rolf", "crolf@atlassian.com", 1468552392,
                "5720e0b5a0f0a8cf5c87db568be4094ef6106b51", "First commit", null);

    }

    @Test
    public void testExecuteFileBecameDirectory() throws Exception {
        String path = "sailbot/libraries/drivers/WinchController.cpp";
        when(commandParameters.getPaths()).thenReturn(Collections.singleton(path));

        PagedCommitRevListOutputHandler handler = new PagedCommitRevListOutputHandler(repository, commandParameters, pageRequest);
        handler.setWatchdog(mock(Watchdog.class));
        handler.process(new ClassPathResource("git-rev-list-commit-file-became-dir-callback.out", getClass()).getInputStream());

        List<Commit> commits = new ArrayList<>();
        Iterables.addAll(commits, handler.getOutput().getValues());
        assertEquals(3, commits.size());

        assertCommit(commits.get(0), "Christian Rolf", "crolf@atlassian.com", 1464322106,
                "7bc4b634f873209d698be30ca69550e0db96036d", "Message",
                ImmutableMap.of(
                        "type", ChangeType.MODIFY,
                        "path", path),
                "306d77535542ed929cf53810a16df9f4783bf74f");

        assertCommit(commits.get(1), "Christian Rolf", "crolf@atlassian.com", 1455969242,
                "410063ff36ea069989c2f496dec50b7d8c3eac80", "Changed file to directory",
                ImmutableMap.of(
                        "type", ChangeType.ADD,
                        "path", path),
                "a1e6ab7522b55c56b67690d6256e584c9ffbdc1a");

        assertCommit(commits.get(2), "Christian Rolf", "crolf@atlassian.com", 1447091442,
                "132add377dd02e09a3cc12ed3eb700b7bd26cf4f", "First commit", null,
                "743a7ac5599b52dd852fc4c20bb0c6ebfef6661f");
    }

    @Test
    public void testExecuteForDirectory() throws Exception {
        String path = ".";
        when(commandParameters.getPaths()).thenReturn(Collections.singleton(path));

        PagedCommitRevListOutputHandler handler = new PagedCommitRevListOutputHandler(repository, commandParameters, pageRequest);
        handler.setWatchdog(mock(Watchdog.class));
        handler.process(new ClassPathResource("git-rev-list-commit-dir-callback.out", getClass()).getInputStream());

        List<Commit> commits = new ArrayList<>();
        Iterables.addAll(commits, handler.getOutput().getValues());
        assertEquals(3, commits.size());

        assertCommit(commits.get(0), "Bitbucket Server release (spetrucev)", "spetrucev@atlassian.com", 1468274820,
                "8ecb11b900d620075674f1876b6ab27a4be38f3a", "[maven-release-plugin] prepare for next development iteration", null,
                "9d5019bdd17df92aa5c94a5b5b4db95749e1becb");

        assertCommit(commits.get(1), "Bitbucket Server release (spetrucev)", "spetrucev@atlassian.com", 1468274808,
                "9d5019bdd17df92aa5c94a5b5b4db95749e1becb", "[maven-release-plugin] prepare release bitbucket-parent-4.8.0-rc10", null,
                "94346a7dfc064ef2e73b9f1dd7f215ec200bd90e");

        assertCommit(commits.get(2), "Benjamin Woskow", "bwoskow@atlassian.com", 1468270371,
                "e837b7e8a0a6a7c176e88f94b17ad8d23f32ca59", "BSERVDEV-13296 bump upm version", null,
                "94346a7dfc064ef2e73b9f1dd7f215ec200bd90e");
    }
}
