package com.atlassian.bitbucket.mesh.repository;

import com.atlassian.bitbucket.mesh.git.hook.GitHookService;
import com.atlassian.bitbucket.mesh.io.MoreFiles;
import com.atlassian.bitbucket.mesh.junit.LogListener;
import com.atlassian.bitbucket.mesh.junit.TemporaryDirectory;
import com.atlassian.bitbucket.mesh.layout.StorageLayout;
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.Path;

import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class PathDataStoreTest {

    @Rule
    public final LogListener logListener = new LogListener(PathDataStore.class);
    @Rule
    public final TemporaryDirectory tempDir = new TemporaryDirectory();

    @Mock
    private GitHookService hookService;
    @Mock
    private StorageLayout storageLayout;

    @Test
    public void testAttachStorageInsideRoot() throws IOException {
        Path repositoryDir = tempDir.newFolder("repositories", "1");
        DefaultPathRepository repository = new DefaultPathRepository(repositoryDir);

        StorageStatus status = new PathDataStore(tempDir.getRoot()).attachStorage(repository);
        assertSame(StorageStatus.ATTACHED, status);

        //Verify the hooks directory defaults to a "hooks" subdirectory in the repository
        assertEquals(repositoryDir.resolve("hooks"), repository.getHooksDir());
    }

    @Test
    public void testAttachStorageOutsideRoot() throws IOException {
        DefaultPathRepository repository = new DefaultPathRepository(tempDir.newFolder("repositories", "1"));

        StorageStatus status = new PathDataStore(tempDir.newFolder("data")).attachStorage(repository);
        assertSame(StorageStatus.NO_STORE, status);
    }

    @Test
    public void testAttachStorageOutsideRootUsingTraversal() throws IOException {
        Path sneaky = MoreFiles.resolve(tempDir.getRoot(), "data", "..", "repositories", "1");
        DefaultPathRepository repository = new DefaultPathRepository(sneaky);

        StorageStatus status = new PathDataStore(tempDir.newFolder("data")).attachStorage(repository);
        assertSame(StorageStatus.NO_STORE, status);
    }

    @Test
    public void testAttachStorageSetsHooksDirIfAvailable() throws IOException {
        Path binDir = tempDir.newFolder("bin");
        Path repositoryDir = tempDir.newFolder("repositories", "1");

        when(storageLayout.getBinDir()).thenReturn(binDir);

        DefaultPathRepository repository = new DefaultPathRepository(repositoryDir);

        StorageStatus status = new PathDataStore(tempDir.getRoot(), hookService, storageLayout)
                .attachStorage(repository);
        assertSame(StorageStatus.ATTACHED, status);

        //Verify the hooks directory is overwritten with the shared hooks directory
        assertEquals(binDir.resolve("git-hooks"), repository.getHooksDir());
    }

    @Test
    public void testAttachStorageWithoutRoot() throws IOException {
        DefaultPathRepository repository = new DefaultPathRepository(tempDir.newFolder("repositories", "1"));

        StorageStatus status = new PathDataStore(null).attachStorage(repository);
        assertSame(StorageStatus.ATTACHED, status);
    }

    @Test
    public void testStartHandlesExceptionInstallingHooks() throws IOException {
        Path binDir = tempDir.newFolder("bin");
        when(storageLayout.getBinDir()).thenReturn(binDir);

        doThrow(IOException.class).when(hookService).installHooks(any());

        PathDataStore store = new PathDataStore(null, hookService, storageLayout);
        store.start();

        logListener.assertWarn("Failed to install hooks");
    }

    @Test
    public void testStartInstallsHooksIfAvailable() throws IOException {
        Path binDir = tempDir.newFolder("bin");
        when(storageLayout.getBinDir()).thenReturn(binDir);

        PathDataStore store = new PathDataStore(null, hookService, storageLayout);
        store.start();

        verify(hookService).installHooks(eq(binDir.resolve("git-hooks")));
    }

    @Test
    public void testStartWithoutHooks() {
        new PathDataStore(null).start();
    }
}