package com.atlassian.stash.internal.migration;

import com.atlassian.bitbucket.AbstractServiceTest;
import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.i18n.SimpleI18nService;
import com.atlassian.stash.internal.db.DatabaseManager;
import com.atlassian.stash.internal.jdbc.DataSourceConfiguration;
import com.atlassian.stash.internal.maintenance.MaintenanceService;
import com.atlassian.stash.internal.maintenance.MaintenanceTaskFactory;
import com.atlassian.stash.internal.maintenance.MaintenanceTaskMonitor;
import com.atlassian.stash.internal.maintenance.MaintenanceType;
import com.atlassian.stash.internal.maintenance.migration.DatabaseMigrationTask;
import com.atlassian.stash.internal.maintenance.migration.DatabaseSetupTask;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;

import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.same;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
@SuppressWarnings("ConstantConditions")
public class DefaultMigrationServiceTest extends AbstractServiceTest {

    @Mock
    private DataSourceConfiguration configuration;
    @Spy
    @SuppressWarnings("unused")
    private I18nService i18nService = new SimpleI18nService();
    @Mock
    private MaintenanceService maintenanceService;
    @Mock
    private MaintenanceTaskFactory maintenanceTaskFactory;
    @Mock
    private DatabaseManager databaseManager;
    @InjectMocks
    private DefaultMigrationService service;

    public DefaultMigrationServiceTest() {
        super(DefaultMigrationService.class, MigrationService.class);
    }

    @Test
    public void testMigrate() {
        DatabaseMigrationTask task = mock(DatabaseMigrationTask.class);
        MaintenanceTaskMonitor future = mock(MaintenanceTaskMonitor.class);

        when(maintenanceTaskFactory.migrationTask(configuration)).thenReturn(task);
        doReturn(future).when(maintenanceService).start(same(task), eq(MaintenanceType.MIGRATION));

        assertSame(future, service.migrate(configuration));

        verify(maintenanceService).start(same(task), eq(MaintenanceType.MIGRATION));
        verify(maintenanceTaskFactory).migrationTask(configuration);
    }

    @Test(expected = NullPointerException.class)
    public void testMigrateThrowsOnNullConfiguration() {
        service.migrate(null);
    }

    @Test(expected = MigrationException.class)
    public void testMigrateThrowsWhenMaintenanceInProgress() {
        DatabaseMigrationTask task = mock(DatabaseMigrationTask.class);

        when(maintenanceTaskFactory.migrationTask(configuration)).thenReturn(task);
        doThrow(IllegalStateException.class).when(maintenanceService).start(same(task), eq(MaintenanceType.MIGRATION));

        try {
            service.migrate(configuration);
        } finally {
            verify(maintenanceService).start(same(task), eq(MaintenanceType.MIGRATION));
            verify(maintenanceTaskFactory).migrationTask(configuration);
        }
    }

    @Test
    public void testSetup() {
        DatabaseSetupTask task = mock(DatabaseSetupTask.class);
        MaintenanceTaskMonitor future = mock(MaintenanceTaskMonitor.class);

        when(maintenanceTaskFactory.setupTask(configuration)).thenReturn(task);
        doReturn(future).when(maintenanceService).start(same(task), eq(MaintenanceType.MIGRATION));

        assertSame(future, service.setup(configuration));

        verify(maintenanceService).start(same(task), eq(MaintenanceType.MIGRATION));
        verify(maintenanceTaskFactory).setupTask(configuration);
    }

    @Test(expected = NullPointerException.class)
    public void testSetupThrowsOnNullConfiguration() {
        service.setup(null);
    }

    @Test(expected = MigrationException.class)
    public void testSetupThrowsWhenMaintenanceInProgress() {
        DatabaseSetupTask task = mock(DatabaseSetupTask.class);

        when(maintenanceTaskFactory.setupTask(configuration)).thenReturn(task);
        doThrow(IllegalStateException.class).when(maintenanceService).start(same(task), eq(MaintenanceType.MIGRATION));

        try {
            service.setup(configuration);
        } finally {
            verify(maintenanceService).start(same(task), eq(MaintenanceType.MIGRATION));
            verify(maintenanceTaskFactory).setupTask(configuration);
        }
    }

    @Test
    public void testValidateConfiguration() {
        service.validateConfiguration(configuration);

        verify(databaseManager).validateConfiguration(same(configuration));
    }
}
