package com.atlassian.bitbucket.mesh.repository;

import com.atlassian.bitbucket.mesh.io.MoreFiles;
import com.atlassian.bitbucket.mesh.junit.TemporaryDirectory;
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 org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class WorkTreeCleanupTaskTest {

    @Rule
    public final TemporaryDirectory tempDir = new TemporaryDirectory();
    @Mock
    private DataStoreManager dataStoreManager;
    @Mock
    private Repository repository;
    @Mock
    private PlatformTransactionManager transactionManager;
    private Path workTreeDirectory;
    @Mock
    private WorkTreeStateDao workTreeStateDao;

    @Before
    public void setUp() throws IOException {
        workTreeDirectory = tempDir.newFolder();
        when(repository.getWorkTreesDir()).thenReturn(workTreeDirectory);
    }

    @Test
    public void testWorkTreeCleanupLeavesUnknownFileAndDirectories() throws IOException {
        Path otherDir = MoreFiles.mkdir(workTreeDirectory, "otherDir");
        Path file = workTreeDirectory.resolve("file");
        MoreFiles.touch(file);
        when(dataStoreManager.getWorkTreesDirs()).thenReturn(Collections.singleton(workTreeDirectory));
        WorkTreeCleanupTask task =
                new WorkTreeCleanupTask(dataStoreManager, new TransactionTemplate(transactionManager),
                        workTreeStateDao);

        task.run();

        verify(workTreeStateDao, times(0)).deleteById(5);
        assertTrue(MoreFiles.dirExists(otherDir));
        assertTrue(Files.exists(file));
    }

    @Test
    public void testWorkTreeCleanupLeavesValidWorkTrees() {
        long workTreeId = 5L;
        Path workTreePath = DefaultWorkTreeManager.resolveWorkTreeDirectory(repository, workTreeId);
        WorkTreeState workTreeState = mock(WorkTreeState.class);
        when(workTreeState.getExpiry()).thenReturn(Instant.now().plusSeconds(60));
        when(dataStoreManager.getWorkTreesDirs()).thenReturn(Collections.singleton(workTreeDirectory));
        MoreFiles.mkdir(workTreePath);
        //create a pretend work tree that does not exist in the DB (yet) when the cleanup runs
        Path notYetPersistedWorkTree = MoreFiles.mkdir(DefaultWorkTreeManager.resolveWorkTreeDirectory(repository, 6));
        List<Long> states = new ArrayList<>();
        states.add(workTreeId);
        when(workTreeStateDao.getAllIds()).thenReturn(states);
        when(workTreeStateDao.findById(workTreeId)).thenReturn(workTreeState);
        WorkTreeCleanupTask task =
                new WorkTreeCleanupTask(dataStoreManager, new TransactionTemplate(transactionManager),
                        workTreeStateDao);

        task.run();

        verify(workTreeStateDao, times(0)).deleteById(5);
        assertTrue(MoreFiles.dirExists(workTreePath));
        assertTrue(MoreFiles.dirExists(notYetPersistedWorkTree));
    }

    @Test
    public void testWorkTreeCleanupRemovesExpiredWorkTrees() throws IOException {
        long workTreeId = 5L;
        Path workTreePath = DefaultWorkTreeManager.resolveWorkTreeDirectory(repository, workTreeId);
        WorkTreeState workTreeState = mock(WorkTreeState.class);
        Instant expiry = Instant.now().minusSeconds(60);
        when(workTreeState.getExpiry()).thenReturn(expiry);
        when(dataStoreManager.getWorkTreesDirs()).thenReturn(Collections.singleton(workTreeDirectory));
        MoreFiles.mkdir(workTreePath);
        when(workTreeStateDao.getAllIds()).thenReturn(Collections.emptyList());
        when(workTreeStateDao.findById(workTreeId)).thenReturn(workTreeState);
        WorkTreeCleanupTask task =
                new WorkTreeCleanupTask(dataStoreManager, new TransactionTemplate(transactionManager),
                        workTreeStateDao);

        task.run();

        verify(workTreeStateDao, times(1)).deleteById(5);
        assertFalse(MoreFiles.dirExists(workTreePath));
    }
}