package com.atlassian.bitbucket.mesh.repository;

import com.atlassian.bitbucket.mesh.app.LifecyclePhases;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import java.nio.file.Paths;
import java.util.function.Consumer;

import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class DefaultDataStoreManagerTest {

    private static final String HIERARCHY_ID = "deadbeefcafe12345678";

    @Mock
    private BitbucketServerDataStore bitbucketServerDataStore;
    @Mock
    private ManagedDataStore managedDataStore;
    @Mock
    private PathDataStore pathDataStore;

    @Test
    public void testAddShard() {
        AddShardParameters parameters = new AddShardParameters.Builder("1", Paths.get(".")).build();

        when(bitbucketServerDataStore.addShard(any())).thenReturn(true);

        DefaultDataStoreManager manager = new DefaultDataStoreManager(bitbucketServerDataStore, null, null);
        assertTrue(manager.addShard(parameters));

        verify(bitbucketServerDataStore).addShard(same(parameters));
    }

    @Test
    public void testAddShardWithoutBitbucketServerDataStore() {
        DefaultDataStoreManager manager = new DefaultDataStoreManager(null, managedDataStore, null);
        assertFalse(manager.addShard(new AddShardParameters.Builder("1", Paths.get(".")).build()));
    }

    @Test
    public void testAttachStorageForBitbucketServerRepository() {
        DefaultBitbucketServerRepository repository = new DefaultBitbucketServerRepository("1", HIERARCHY_ID, "1");

        when(bitbucketServerDataStore.attachStorage(any())).thenReturn(StorageStatus.ATTACHED);

        DefaultDataStoreManager manager = new DefaultDataStoreManager(bitbucketServerDataStore, null, null);
        assertSame(StorageStatus.ATTACHED, manager.attachStorage(repository));

        verify(bitbucketServerDataStore).attachStorage(same(repository));
    }

    @Test
    public void testAttachStorageForManagedRepository() {
        DefaultManagedRepository repository = new DefaultManagedRepository("1", HIERARCHY_ID, "1");

        when(managedDataStore.attachStorage(any())).thenReturn(StorageStatus.ATTACHED);

        DefaultDataStoreManager manager = new DefaultDataStoreManager(null, managedDataStore, null);
        assertSame(StorageStatus.ATTACHED, manager.attachStorage(repository));

        verify(managedDataStore).attachStorage(same(repository));
    }

    @Test
    public void testAttachStorageForPathRepository() {
        DefaultPathRepository repository = new DefaultPathRepository(Paths.get("."));

        when(pathDataStore.attachStorage(any())).thenReturn(StorageStatus.ATTACHED);

        DefaultDataStoreManager manager = new DefaultDataStoreManager(null, null, pathDataStore);
        assertSame(StorageStatus.ATTACHED, manager.attachStorage(repository));

        verify(pathDataStore).attachStorage(same(repository));
    }

    @Test
    public void testAttachStorageWithoutTargetStore() {
        DefaultManagedRepository repository = new DefaultManagedRepository("1", HIERARCHY_ID, "1");

        DefaultDataStoreManager manager = new DefaultDataStoreManager(bitbucketServerDataStore, null, pathDataStore);
        assertSame(StorageStatus.NO_STORE, manager.attachStorage(repository));
    }

    @SuppressWarnings("ConstantConditions")
    @Test
    public void testConstructorRequiresAtLeastOneDataStore() {
        assertThrows(IllegalStateException.class, () -> new DefaultDataStoreManager(null, null, null));
    }

    @Test
    public void testGetPhase() {
        DefaultDataStoreManager manager = new DefaultDataStoreManager(null, null, pathDataStore);
        assertEquals(LifecyclePhases.DATA_STORES, manager.getPhase());
    }

    @Test
    public void testLifecycle() {
        DefaultDataStoreManager manager =
                new DefaultDataStoreManager(bitbucketServerDataStore, managedDataStore, pathDataStore);
        assertFalse(manager.isRunning());

        manager.start();
        assertTrue(manager.isRunning());

        verify(bitbucketServerDataStore).start();
        verify(managedDataStore).start();
        verify(pathDataStore).start();

        manager.stop();
        assertFalse(manager.isRunning());

        verify(bitbucketServerDataStore).stop();
        verify(managedDataStore).stop();
        verify(pathDataStore).stop();
    }

    @Test
    public void testStreamRepositories() {
        DefaultDataStoreManager manager =
                new DefaultDataStoreManager(bitbucketServerDataStore, managedDataStore, pathDataStore);

        Consumer<Repository> callback = mock(Consumer.class);
        manager.streamRepositories("0000", "aaaaaaaaaabbbbbbbbbb", callback);

        verify(managedDataStore).streamRepositories("0000", "aaaaaaaaaabbbbbbbbbb", callback);

        verifyNoInteractions(bitbucketServerDataStore, pathDataStore);
    }
}