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

import com.atlassian.bitbucket.scm.annotation.Repo;
import com.atlassian.bitbucket.scm.CommandFailedException;
import com.atlassian.stash.internal.scm.git.AbstractGitTest;
import com.atlassian.stash.internal.scm.git.util.ConfigurableGitScmConfig;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.scm.git.command.GitCommandBuilderFactory;
import com.atlassian.bitbucket.scm.git.command.rm.GitRmBuilder;
import com.google.common.base.Charsets;
import com.google.common.io.Files;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mock;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;

import static com.atlassian.bitbucket.hamcrest.FileMatchers.exists;
import static com.atlassian.bitbucket.hamcrest.FileMatchers.isDirectory;
import static com.atlassian.bitbucket.hamcrest.FileMatchers.isFile;
import static com.atlassian.stash.internal.scm.git.GitFactoriesTestHelper.StringHandler;
import static org.hamcrest.Matchers.not;

public class RmCommandTest extends AbstractGitTest {

    @Autowired
    private GitCommandBuilderFactory builderFactory;
    @Autowired
    private ConfigurableGitScmConfig config;
    @Repo
    private Repository sourceRepository;

    @Mock
    private Repository repository;
    @Rule
    public TemporaryFolder tempFolder = new TemporaryFolder();

    private File directory;

    @Before
    public void setUp() throws Exception {
        directory = tempFolder.getRoot();
        builderFactory.builder().clone().normal().origin(config.getRepositoryDir(sourceRepository))
                .directory(directory).quiet(true).build().call();
        config.setRepositoryDir(repository, directory);
    }

    @Test
    public void testRm() throws Exception {
        assertThat(new File(directory, "basic_branching/file.txt"), isFile());
        createBuilder().path("basic_branching/file.txt").build().call();
        assertThat(new File(directory, "basic_branching/file.txt"), not(exists()));
    }

    @Test
    public void testRmRecursively() throws Exception {
        assertThat(new File(directory, "basic_branching"), isDirectory());
        createBuilder().path("basic_branching").recursive(true).build().call();
        assertThat(new File(directory, "basic_branching"), not(exists()));
    }

    @Test
    public void testRmMultipleFiles() throws Exception {
        assertThat(new File(directory, "basic_branching/file.txt"), isFile());
        assertThat(new File(directory, "modification/mod_file.txt"), isFile());
        createBuilder().paths("basic_branching/file.txt", "modification/mod_file.txt").build().call();
        assertThat(new File(directory, "basic_branching/file.txt"), not(exists()));
        assertThat(new File(directory, "modification/mod_file.txt"), not(exists()));
    }

    @Test
    public void testRmMultipleFiles2() throws Exception {
        assertThat(new File(directory, "basic_branching/file.txt"), isFile());
        assertThat(new File(directory, "modification/mod_file.txt"), isFile());
        createBuilder().paths(Arrays.asList("basic_branching/file.txt", "modification/mod_file.txt")).build().call();
        assertThat(new File(directory, "basic_branching/file.txt"), not(exists()));
        assertThat(new File(directory, "modification/mod_file.txt"), not(exists()));
    }

    @Test
    public void testRmMultipleFilesRecursively() throws Exception {
        assertThat(new File(directory, "basic_branching"), isDirectory());
        assertThat(new File(directory, "modification"), isDirectory());
        createBuilder().paths("basic_branching", "modification").recursive(true).build().call();
        assertThat(new File(directory, "basic_branching/file.txt"), not(exists()));
        assertThat(new File(directory, "modification/mod_file.txt"), not(exists()));
    }

    @Test(expected = CommandFailedException.class)
    public void testRmNonExistentFile() throws Exception {
        assertThat(new File(directory, "unknown-file.txt"), not(exists()));
        createBuilder().path("unknown-file.txt").build().call();
    }

    @Test(expected = CommandFailedException.class)
    public void testRmNonExistentFiles() throws Exception {
        assertThat(new File(directory, "unknown-file.txt"), not(exists()));
        assertThat(new File(directory, "other-unknown-file.txt"), not(exists()));
        createBuilder().paths("unknown-file.txt", "other-unknown-file.txt").build().call();
    }

    @Test(expected = CommandFailedException.class)
    public void testRmMixOfValidAndNonExistentFiles() throws Exception {
        assertThat(new File(directory, "basic_branching/file.txt"), isFile());
        assertThat(new File(directory, "unknown-file.txt"), not(exists()));
        createBuilder().paths("basic_branching/file.txt", "unknown-file.txt").build().call();
    }

    @Test(expected = CommandFailedException.class)
    public void testRmDirectoryWithoutRecursion() throws Exception {
        assertThat(new File(directory, "basic_branching"), isDirectory());
        createBuilder().path("basic_branching").build().call();
    }

    @Test(expected = CommandFailedException.class)
    public void testRmModifiedFile() throws Exception {
        modifyFile("basic_branching/file.txt", false);
        createBuilder().path("basic_branching/file.txt").build().call();
    }

    @Test(expected = CommandFailedException.class)
    public void testRmModifiedFileWithUpdatesInTheIndex() throws Exception {
        modifyFile("basic_branching/file.txt", true);
        createBuilder().path("basic_branching/file.txt").build().call();
        assertThat(new File(directory, "basic_branching/file.txt"), not(exists()));
    }

    @Test(expected = CommandFailedException.class)
    public void testRmModifiedFiles() throws Exception {
        modifyFile("basic_branching/file.txt", false);
        modifyFile("modification/mod_file.txt", false);
        createBuilder().paths("basic_branching/file.txt", "modification/mod_file.txt").build().call();
    }

    @Test(expected = CommandFailedException.class)
    public void testRmModifiedFilesWithUpdatesInTheIndex() throws Exception {
        modifyFile("basic_branching/file.txt", true);
        modifyFile("modification/mod_file.txt", true);
        createBuilder().paths("basic_branching/file.txt", "modification/mod_file.txt").build().call();
    }

    @Test
    public void testForceDeleteModifiedFile() throws Exception {
        modifyFile("basic_branching/file.txt", false);
        createBuilder().path("basic_branching/file.txt").force(true).build().call();
        assertThat(new File(directory, "basic_branching/file.txt"), not(exists()));

        modifyFile("modification/mod_file.txt", true);
        createBuilder().path("modification/mod_file.txt").force(true).build().call();
        assertThat(new File(directory, "modification/mod_file.txt"), not(exists()));
    }

    @Test
    public void testForceDeleteModifiedFiles() throws Exception {
        modifyFile("basic_branching/file.txt", false);
        modifyFile("modification/mod_file.txt", false);
        createBuilder().paths("basic_branching/file.txt", "modification/mod_file.txt").force(true).build().call();
        assertThat(new File(directory, "basic_branching/file.txt"), not(exists()));
        assertThat(new File(directory, "modification/mod_file.txt"), not(exists()));

        modifyFile("mv_file/moved_file.txt", true);
        modifyFile("moved_dir/file.txt", true);
        createBuilder().paths("mv_file/moved_file.txt", "moved_dir/file.txt").force(true).build().call();
        assertThat(new File(directory, "mv_file/moved_file.txt"), not(exists()));
        assertThat(new File(directory, "moved_dir/file.txt"), not(exists()));
    }

    @Test(expected = CommandFailedException.class)
    public void testDeleteRecursivelyModifiedFile() throws Exception {
        assertThat(new File(directory, "basic_branching"), isDirectory());
        modifyFile("basic_branching/file.txt", false);
        createBuilder().path("basic_branching").recursive(true).build().call();
    }

    @Test
    public void testForceDeleteRecursively() throws Exception {
        assertThat(new File(directory, "basic_branching"), isDirectory());
        modifyFile("basic_branching/file.txt", false);
        createBuilder().path("basic_branching").force(true).recursive(true).build().call();
        assertThat(new File(directory, "basic_branching"), not(exists()));

        assertThat(new File(directory, "moved_dir"), isDirectory());
        modifyFile("moved_dir/file.txt", true);
        createBuilder().path("moved_dir").force(true).recursive(true).build().call();
        assertThat(new File(directory, "moved_dir"), not(exists()));
    }

    @Test
    public void testRmQuietly() throws Exception {
        assertThat(new File(directory, "basic_branching/file.txt"), isFile());
        StringHandler outputHandler = new StringHandler();
        createBuilder().path("basic_branching/file.txt").quiet(true).build(outputHandler).call();
        assertThat(new File(directory, "basic_branching/file.txt"), not(exists()));
        assertEquals("", outputHandler.getOutput());
    }

    private void modifyFile(String file, boolean updateIndex) throws IOException {
        File f = new File(directory, file);
        assertThat(f, isFile());
        Files.write("blah", f, Charsets.US_ASCII);
        if (updateIndex) {
            builderFactory.builder(repository).add().path(file).build().call();
        }
    }

    private GitRmBuilder createBuilder() {
        return builderFactory.builder(repository).rm();
    }

}
