package com.atlassian.bitbucket.internal.importer.repository;

import com.atlassian.bitbucket.i18n.KeyedMessage;
import com.atlassian.bitbucket.internal.importer.Credential;
import com.atlassian.bitbucket.internal.importer.dao.AoRepositoryImportTask;
import com.atlassian.bitbucket.repository.Repository;
import com.atlassian.bitbucket.repository.RepositoryService;
import com.atlassian.bitbucket.scm.Command;
import com.atlassian.bitbucket.scm.CommandFailedException;
import com.atlassian.bitbucket.scm.ScmFeature;
import com.atlassian.bitbucket.scm.ScmService;
import com.atlassian.bitbucket.scm.mirror.MirrorSyncCommandParameters;
import com.atlassian.bitbucket.scm.mirror.ScmMirrorCommandFactory;
import com.atlassian.bitbucket.user.ApplicationUser;
import com.atlassian.bitbucket.user.DummySecurityService;
import com.atlassian.bitbucket.user.SecurityService;
import com.atlassian.bitbucket.user.TestApplicationUser;
import com.atlassian.event.api.EventPublisher;
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.Optional;
import java.util.concurrent.TimeUnit;

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

@RunWith(MockitoJUnitRunner.class)
public class RefSyncTaskTest {

    private Optional<Credential> credential = Optional.of(new Credential.Builder("username", "password").build());
    @Mock
    private Command<Void> command;
    @Mock
    private EventPublisher eventPublisher;
    @Mock
    private Repository repository;
    @Mock
    private RepositoryImportTaskHelper repositoryImportTaskHelper;
    @Mock
    private RepositoryService repositoryService;
    @Mock
    private ScmMirrorCommandFactory commandFactory;
    @Mock
    private ScmService scmService;

    private RefSyncTaskParameters parameters;
    private SecurityService securityService = new DummySecurityService();
    private ApplicationUser user = new TestApplicationUser("the-importer", 43434);

    private RefSyncTask refSyncTask;

    @Before
    public void setup() {
        when(scmService.isSupported(any(Repository.class), eq(ScmFeature.MIRRORS))).thenReturn(true);
        AoRepositoryImportTask aoRepositoryImportTask = mock(AoRepositoryImportTask.class);
        when(aoRepositoryImportTask.getCloneUrl()).thenReturn("someURL");
        when(aoRepositoryImportTask.getState()).thenReturn(ImportTaskState.STARTED);
        parameters = new RefSyncTaskParameters.Builder()
                .cloneUrl("https://github.com/spring/spring.git")
                .credential(credential)
                .executionTimeout(10L, TimeUnit.MINUTES)
                .idleTimeout(10L, TimeUnit.MINUTES)
                .repository(repository)
                .taskId(2L)
                .user(user)
                .build();
        refSyncTask = new RefSyncTask(eventPublisher, repositoryImportTaskHelper,
                repositoryService, scmService, securityService, parameters);
        when(scmService.getMirrorCommandFactory(repository)).thenReturn(commandFactory);
        when(commandFactory.synchronize(any(), any())).thenReturn(command);
    }

    @Test
    public void testRefSyncTask() {
        ArgumentCaptor<RepositoryImportedEvent> eventCaptor = ArgumentCaptor.forClass(RepositoryImportedEvent.class);
        doNothing().when(eventPublisher).publish(eventCaptor.capture());

        ArgumentCaptor<MirrorSyncCommandParameters> parametersCaptor = ArgumentCaptor.forClass(MirrorSyncCommandParameters.class);
        when(commandFactory.synchronize(parametersCaptor.capture(), any())).thenReturn(command);

        refSyncTask.call();

        MirrorSyncCommandParameters commandParameters = parametersCaptor.getValue();
        assertEquals(parameters.getCloneUrl(), commandParameters.getRemoteUrl());
        assertEquals(credential.get().getUsername(), commandParameters.getUsername().get());
        assertEquals(credential.get().getPassword(), commandParameters.getPassword().get());
        verify(command).call();

        RepositoryImportedEvent repositoryImportedEvent = eventCaptor.getValue();
        assertEquals(repository, repositoryImportedEvent.getRepository());
        assertEquals(parameters.getCloneUrl(), repositoryImportedEvent.getSourceUrl());

        verify(repositoryImportTaskHelper).updateImportTask(eq(parameters.getTaskId()), eq(ImportTaskState.SUCCESS));
    }

    @Test
    public void testRefSyncTaskShouldSetTaskStatusFailedIfSyncFails() {
        when(command.call()).thenThrow(new CommandFailedException(new KeyedMessage("", "", "")));

        refSyncTask.call();

        verify(repositoryService).delete(repository);
        verify(repositoryImportTaskHelper).failImportTask(eq(parameters.getTaskId()), eq(ImportTaskFailureType.SYNC_FAILED));
    }
}