package com.atlassian.bitbucket.mesh.git.gc;

import com.atlassian.bitbucket.mesh.junit.TemporaryDirectory;
import com.atlassian.bitbucket.mesh.repository.Repository;
import com.google.common.collect.ImmutableList;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;

import static java.nio.charset.StandardCharsets.UTF_8;
import static java.util.stream.Collectors.joining;
import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.Silent.class)
public class ReflogMergerTest {

    @Rule
    public TemporaryDirectory temporaryDirectory = new TemporaryDirectory();
    private Path destination;
    @Mock
    private Repository repository;
    private Path source;

    @Before
    public void setup() throws IOException {
        when(repository.getPath()).thenReturn(temporaryDirectory.getRoot());

        source = temporaryDirectory.newFile("source");
        destination = temporaryDirectory.newFile("destination");
    }

    @Test
    public void testMergesIdenticalReflogs() throws IOException {
        String line1 = "0000000000000000000000000000000000000000 d36984a542c2cddb3c29797a11aec1199c7d75d2 " +
                "author name <author@localhost> 1606356610 +0000";
        String line2 = "d36984a542c2cddb3c29797a11aec1199c7d75d2 54ff66a5d38dd99607207883cc3189b6cc17ad47 " +
                "author name <author@localhost> 1606356620 +0000";
        String line3 = "d36984a542c2cddb3c29797a11aec1199c7d75d2 54ff66a5d38dd99607207883cc3189b6cc17ad47 " +
                "author name <author@localhost> 1606356621 +0000";
        mockSourceReflog(line1, line2, line3);
        mockDestinationReflog(line1, line2, line3);

        new ReflogMerger(repository, source, destination).merge();

        verifyResult(line1, line2, line3);
    }

    @Test
    public void testMergesReflogsWithSameTimestampInStableWay() throws IOException {
        String line1 = "0000000000000000000000000000000000000000 d36984a542c2cddb3c29797a11aec1199c7d75d2 " +
                "author name <author@localhost> 1606356610 +0000";
        String line2 = "aaaa84a542c2cddb3c29797a11aec1199c7d75d2 54ff66a5d38dd99607207883cc3189b6cc17ad47 " +
                "author name <author@localhost> 1606356620 +0000";
        String line3 = "54ff66a5d38dd99607207883cc3189b6cc17ad47 54ff66a5d38dd99607207883cc3189b6cc17ad47 " +
                "author name <author@localhost> 1606356620 +0000";
        String line4 = "d36984a542c223444244797a11aec1199c7d75d2 54ff66a5d38dd99607207883cc3189b6cc17ad47 " +
                "author name <author@localhost> 1606356620 +0000";
        String line5 = "d36984a542c2cddb3c29797a11aec1199c7d75d2 d36984a542c223444244797a11aec1199c7d75d2 " +
                "author name <author@localhost> 1606356623 +0000";
        String line6 = "d36984a542c223444244797a11aec1199c7d75d2 54ff66a5d38dd99607207883cc3189b6cc17ad47 " +
                "author name <author@localhost> 1606356624 +0000";
        mockDestinationReflog(line1, line2, line3);
        mockSourceReflog(line4, line5, line6);

        new ReflogMerger(repository, source, destination).merge();

        verifyResult(line1, line2, line3, line4, line5, line6);
    }

    @Test
    public void testMergesUnsortedReflogs() throws IOException {
        String line1 = "0000000000000000000000000000000000000000 d36984a542c2cddb3c29797a11aec1199c7d75d2 " +
                "author name <author@localhost> 1606356610 +0000";
        String line2 = "d36984a542c2cddb3c29797a11aec1199c7d75d2 54ff66a5d38dd99607207883cc3189b6cc17ad47 " +
                "author name <author@localhost> 1606356620 +0000";
        String line3 = "54ff66a5d38dd99607207883cc3189b6cc17ad47 54ff66a5d38dd99607207883cc3189b6cc17ad47 " +
                "author name <author@localhost> 1606356621 +0000";
        String line4 = "d36984a542c223444244797a11aec1199c7d75d2 54ff66a5d38dd99607207883cc3189b6cc17ad47 " +
                "author name <author@localhost> 1606356622 +0000";
        String line5 = "d36984a542c2cddb3c29797a11aec1199c7d75d2 d36984a542c223444244797a11aec1199c7d75d2 " +
                "author name <author@localhost> 1606356623 +0000";
        String line6 = "d36984a542c223444244797a11aec1199c7d75d2 54ff66a5d38dd99607207883cc3189b6cc17ad47 " +
                "author name <author@localhost> 1606356624 +0000";
        mockSourceReflog(line4, line1, line2);
        mockDestinationReflog(line3, line6, line5);

        new ReflogMerger(repository, source, destination).merge();

        verifyResult(line1, line2, line3, line4, line5, line6);
    }

    @Test
    public void testMergesInterleavingReflogs() throws IOException {
        String line1 = "0000000000000000000000000000000000000000 d36984a542c2cddb3c29797a11aec1199c7d75d2 " +
                "author name <author@localhost> 1606356610 +0000";
        String line2 = "d36984a542c2cddb3c29797a11aec1199c7d75d2 54ff66a5d38dd99607207883cc3189b6cc17ad47 " +
                "author name <author@localhost> 1606356620 +0000";
        String line3 = "54ff66a5d38dd99607207883cc3189b6cc17ad47 54ff66a5d38dd99607207883cc3189b6cc17ad47 " +
                "author name <author@localhost> 1606356621 +0000";
        String line4 = "d36984a542c223444244797a11aec1199c7d75d2 54ff66a5d38dd99607207883cc3189b6cc17ad47 " +
                "author name <author@localhost> 1606356622 +0000";
        String line5 = "d36984a542c2cddb3c29797a11aec1199c7d75d2 d36984a542c223444244797a11aec1199c7d75d2 " +
                "author name <author@localhost> 1606356623 +0000";
        String line6 = "d36984a542c223444244797a11aec1199c7d75d2 54ff66a5d38dd99607207883cc3189b6cc17ad47 " +
                "author name <author@localhost> 1606356624 +0000";
        mockSourceReflog(line1, line3, line4);
        mockDestinationReflog(line2, line5, line6);

        new ReflogMerger(repository, source, destination).merge();

        verifyResult(line1, line2, line3, line4, line5, line6);
    }

    @Test
    public void testMergesSuccessiveReflogs() throws IOException {
        String line1 = "0000000000000000000000000000000000000000 d36984a542c2cddb3c29797a11aec1199c7d75d2 " +
                "author name <author@localhost> 1606356610 +0000";
        String line2 = "d36984a542c2cddb3c29797a11aec1199c7d75d2 54ff66a5d38dd99607207883cc3189b6cc17ad47 " +
                "author name <author@localhost> 1606356620 +0000";
        String line3 = "54ff66a5d38dd99607207883cc3189b6cc17ad47 54ff66a5d38dd99607207883cc3189b6cc17ad47 " +
                "author name <author@localhost> 1606356621 +0000";
        String line4 = "d36984a542c223444244797a11aec1199c7d75d2 54ff66a5d38dd99607207883cc3189b6cc17ad47 " +
                "author name <author@localhost> 1606356622 +0000";
        String line5 = "d36984a542c2cddb3c29797a11aec1199c7d75d2 d36984a542c223444244797a11aec1199c7d75d2 " +
                "author name <author@localhost> 1606356623 +0000";
        String line6 = "d36984a542c223444244797a11aec1199c7d75d2 54ff66a5d38dd99607207883cc3189b6cc17ad47 " +
                "author name <author@localhost> 1606356624 +0000";
        mockSourceReflog(line1, line2, line3);
        mockDestinationReflog(line4, line5, line6);

        new ReflogMerger(repository, source, destination).merge();

        verifyResult(line1, line2, line3, line4, line5, line6);
    }

    private void mockDestinationReflog(String... lines) throws IOException {
        Files.write(destination, ImmutableList.copyOf(lines), UTF_8);
    }

    private void mockSourceReflog(String... lines) throws IOException {
        Files.write(source, ImmutableList.copyOf(lines), UTF_8);
    }

    private void verifyResult(CharSequence... lines) throws IOException {
        try (Stream<String> actualLines = Files.lines(destination)) {
            assertEquals(String.join("\n", lines).trim(), actualLines.collect(joining("\n")).trim());
        }
    }
}