package com.atlassian.stash.internal.idx;

import com.atlassian.bitbucket.concurrent.BucketedExecutor;
import com.atlassian.bitbucket.concurrent.BucketedExecutorSettings;
import com.atlassian.bitbucket.concurrent.ConcurrencyService;
import com.atlassian.bitbucket.event.repository.RepositoryPushEvent;
import com.atlassian.bitbucket.repository.RefChange;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.server.ApplicationMode;
import com.google.common.collect.Lists;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import java.util.Collections;

import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class CommitIndexingSchedulerTest {

    private static final int REPO_ID = 9;
    private static final String REPO_HIERARCHY_ID = "hierachy-id";

    @Mock
    private ConcurrencyService concurrencyService;
    @Mock
    private BucketedExecutor<HierarchyIndexingRequest> executor;
    @Mock
    private HierarchyIndexer hierarchyIndexer;
    @Mock
    private Repository repository;

    @Before
    public void setup() {
        when(repository.getId()).thenReturn(REPO_ID);
        when(repository.getHierarchyId()).thenReturn(REPO_HIERARCHY_ID);
        when(concurrencyService.<HierarchyIndexingRequest>getBucketedExecutor(anyString(),
                any(BucketedExecutorSettings.class))).thenReturn(executor);
    }

    @Test
    public void testEmptyEventDoesNotTriggerIndexing() {
        CommitIndexingScheduler scheduler = createScheduler(ApplicationMode.DEFAULT);
        scheduler.onRefsChanged(new RepositoryPushEvent(this, repository, Collections.<RefChange>emptyList()));

        verifyZeroInteractions(executor);
    }

    @Test
    public void testEventWithChangesTriggersIndexing() {
        CommitIndexingScheduler scheduler = createScheduler(ApplicationMode.DEFAULT);
        scheduler.onRefsChanged(new RepositoryPushEvent(this, repository, Lists.newArrayList(mock(RefChange.class))));
        ArgumentCaptor<HierarchyIndexingRequest> request = ArgumentCaptor.forClass(HierarchyIndexingRequest.class);

        verify(executor).submit(request.capture());
        assertEquals(REPO_ID, request.getValue().getRepositoryId());
        assertEquals(REPO_HIERARCHY_ID, request.getValue().getHierarchyId());
    }

    @Test
    public void testLifecycle() {
        CommitIndexingScheduler scheduler = createScheduler(ApplicationMode.DEFAULT);

        //noinspection unchecked
        verify(concurrencyService).getBucketedExecutor(anyString(), any(BucketedExecutorSettings.class));

        scheduler.shutdown();

        verify(executor).shutdown();
    }

    @Test
    public void testEventIgnoredInMirrorMode() {
        CommitIndexingScheduler scheduler = createScheduler(ApplicationMode.MIRROR);

        scheduler.onRefsChanged(new RepositoryPushEvent(this, repository, Lists.newArrayList(mock(RefChange.class))));

        verify(executor, never()).submit(any(HierarchyIndexingRequest.class));
    }

    private CommitIndexingScheduler createScheduler(ApplicationMode mode) {
        return new CommitIndexingScheduler(concurrencyService, hierarchyIndexer, mode, 3);
    }

}
