package com.atlassian.stash.internal.audit;

import com.atlassian.event.api.EventPublisher;
import com.atlassian.bitbucket.audit.Priority;
import com.atlassian.bitbucket.event.audit.AuditEvent;
import com.atlassian.bitbucket.project.Project;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.request.RequestManager;
import com.atlassian.bitbucket.user.ApplicationUser;
import com.atlassian.bitbucket.auth.AuthenticationContext;
import org.hamcrest.Matchers;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;

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

@RunWith(MockitoJUnitRunner.class)
public abstract class AbstractAuditEventListenerTest {

    protected static final String USERNAME = "USERNAME";
    protected static final String REPOSITORY = "sluggy";
    protected static final String PROJECT = "project-key";

    @Mock
    private AuthenticationContext authenticationContext;
    @Captor
    private ArgumentCaptor<AuditEvent> eventCaptor;
    @Mock
    private EventPublisher eventPublisher;
    @Mock
    private AuditEntryLoggingService loggingService;
    @Mock
    private RequestManager requestManager;

    @Before
    public void setup() {
        doReturn(mockUser(USERNAME)).when(authenticationContext).getCurrentUser();
    }

    protected static void assertContains(String details, String... subStrings) {
        for (String subString : subStrings) {
            assertThat(details, Matchers.containsString(subString));
        }
    }

    protected static void assertNotContains(String details, String... subStrings) {
        for (String subString : subStrings) {
            assertThat(details, Matchers.not(Matchers.containsString(subString)));
        }
    }

    protected static Project mockProject() {
        Project project = mock(Project.class);
        when(project.getKey()).thenReturn(PROJECT);
        return project;
    }

    protected static Repository mockRepository(int number) {
        Repository repository = mock(Repository.class, Mockito.RETURNS_DEEP_STUBS);
        when(repository.getSlug()).thenReturn(REPOSITORY + number);
        when(repository.getProject().getKey()).thenReturn(PROJECT + number);
        return repository;
    }

    protected static Repository mockRepositoryWithOrigin(int number) {
        Repository repository = mockRepository(number);
        Repository origin = mockRepository(++number);
        when(repository.getOrigin()).thenReturn(origin);
        return repository;
    }

    protected static ApplicationUser mockUser(String name) {
        ApplicationUser user = mock(ApplicationUser.class);
        when(user.getName()).thenReturn(name);
        return user;
    }

    protected void assertEventCommonDetails(AuditEvent event, Priority priority, String... channels) {
        assertEquals(priority, event.getPriority());
        if (channels.length == 0) {
            assertTrue(event.getChannels().isEmpty());
        } else {
            assertThat(event.getChannels(), Matchers.containsInAnyOrder(channels));
        }
    }

    protected AuditEvent getAuditEvent() {
        verify(eventPublisher).publish(eventCaptor.capture());

        return eventCaptor.getValue();
    }

    protected void assertNoAuditEvent() {
        verify(eventPublisher, never()).publish(anyObject());
    }
}
