package com.atlassian.stash.internal.notification.pull.handlers;

import com.atlassian.bitbucket.commit.Commit;
import com.atlassian.bitbucket.commit.CommitService;
import com.atlassian.bitbucket.commit.CommitsBetweenRequest;
import com.atlassian.bitbucket.throttle.ResourceBusyException;
import com.atlassian.bitbucket.pull.PullRequest;
import com.atlassian.bitbucket.pull.PullRequestRef;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.user.DummySecurityService;
import com.atlassian.bitbucket.user.SecurityService;
import com.atlassian.bitbucket.util.Page;
import com.atlassian.bitbucket.util.PageRequest;
import org.hamcrest.core.IsCollectionContaining;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.*;
import org.mockito.runners.MockitoJUnitRunner;

import java.util.Collections;

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

@RunWith(MockitoJUnitRunner.class)
public class PullRequestNotificationHelperTest {

    @Captor
    private ArgumentCaptor<CommitsBetweenRequest> commitRequestCaptor;
    @Mock
    private Commit commit;
    @Mock
    private PullRequestRef fromRef;
    @Mock
    private CommitService commitService;
    @InjectMocks
    private PullRequestNotificationHelper notificationHelper;
    @Captor
    private ArgumentCaptor<PageRequest> pageRequestCaptor;
    @Mock
    private Page<Commit> page;
    @Mock
    private PullRequest pullRequest;
    @Mock
    private Repository repository;
    @Spy
    @SuppressWarnings("unused")
    private SecurityService securityService = new DummySecurityService();
    @Mock
    private PullRequestRef toRef;

    @Before
    public void setUp() throws Exception {
        when(fromRef.getLatestCommit()).thenReturn("from-ref");
        when(fromRef.getRepository()).thenReturn(repository);

        when(commitService.getCommitsBetween(commitRequestCaptor.capture(), pageRequestCaptor.capture()))
                .thenReturn(page);

        when(page.getValues()).thenReturn(Collections.singletonList(commit));

        when(pullRequest.getFromRef()).thenReturn(fromRef);
        when(pullRequest.getToRef()).thenReturn(toRef);

        when(toRef.getRepository()).thenReturn(repository);
        when(toRef.getLatestCommit()).thenReturn("to-ref");
    }

    @Test
    public void testGetNewCommits() throws Exception {
        notificationHelper.getNewCommits(pullRequest, "previous-hash", 42);

        verify(commitService).getCommitsBetween(isA(CommitsBetweenRequest.class), isA(PageRequest.class));

        CommitsBetweenRequest commitRequest = commitRequestCaptor.getValue();
        assertSame(repository, commitRequest.getRepository());
        assertNull(commitRequest.getSecondaryRepository());
        assertThat(commitRequest.getIncludes(), IsCollectionContaining.hasItems("from-ref"));
        assertThat(commitRequest.getExcludes(), IsCollectionContaining.hasItems("to-ref", "previous-hash"));
        assertTrue(commitRequest.getPaths().isEmpty());

        PageRequest pageRequest = pageRequestCaptor.getValue();
        assertEquals(0, pageRequest.getStart());
        assertEquals(42, pageRequest.getLimit());
    }

    @Test
    public void testGetCommits() throws Exception {
        notificationHelper.getCommits(pullRequest, 42);

        verify(commitService).getCommitsBetween(isA(CommitsBetweenRequest.class), isA(PageRequest.class));

        CommitsBetweenRequest commitRequest = commitRequestCaptor.getValue();
        assertSame(repository, commitRequest.getRepository());
        assertNull(commitRequest.getSecondaryRepository());
        assertThat(commitRequest.getIncludes(), IsCollectionContaining.hasItems("from-ref"));
        assertThat(commitRequest.getExcludes(), IsCollectionContaining.hasItems("to-ref"));
        assertTrue(commitRequest.getPaths().isEmpty());

        PageRequest pageRequest = pageRequestCaptor.getValue();
        assertEquals(0, pageRequest.getStart());
        assertEquals(42, pageRequest.getLimit());
    }

    @Test
    public void testGetNewCommitsWithThrottling() throws Exception {
        reset(commitService);

        doThrow(ResourceBusyException.class)
                .when(commitService)
                .getCommitsBetween(any(CommitsBetweenRequest.class), any(PageRequest.class));

        assertEquals(PullRequestNotificationHelper.THROTTLED, notificationHelper.getNewCommits(pullRequest, "previous-from-hash", 42));
    }

    @Test
    public void testGetCommitsWithThrottling() throws Exception {
        reset(commitService);

        doThrow(ResourceBusyException.class)
                .when(commitService)
                .getCommitsBetween(any(CommitsBetweenRequest.class), any(PageRequest.class));

        assertEquals(PullRequestNotificationHelper.THROTTLED, notificationHelper.getCommits(pullRequest, 42));
    }
}
