package com.atlassian.stash.internal.maintenance.backup;

import com.atlassian.event.api.EventPublisher;
import com.atlassian.johnson.Johnson;
import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.i18n.SimpleI18nService;
import com.atlassian.stash.internal.db.DatabaseLatch;
import com.atlassian.stash.internal.db.DatabaseManager;
import com.atlassian.stash.internal.maintenance.*;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.*;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.io.ClassPathResource;

import java.io.IOException;
import java.util.List;

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

@RunWith(MockitoJUnitRunner.class)
public class BackupTaskTest {

    @Mock
    private BackupPhase backupPhase;
    @Mock
    private BackupPhase.Builder backupPhaseBuilder;
    @Mock
    private DatabaseManager databaseManager;
    @Mock
    private EventPublisher eventPublisher;
    @Spy
    private I18nService i18nService = new SimpleI18nService(SimpleI18nService.Mode.RETURN_KEYS);
    @Mock
    private DatabaseLatch latch;
    @Mock
    private MaintenanceModePhase maintenanceModePhase;
    @Mock
    private MaintenanceModePhase.Builder maintenanceModePhaseBuilder;
    @Mock(answer = Answers.RETURNS_MOCKS)
    private MaintenanceTaskFactory taskFactory;

    @BeforeClass
    public static void setupJohnson() throws IOException {
        Johnson.initialize(new ClassPathResource("minimal-johnson-config.xml").getURL().getPath());
    }

    @AfterClass
    public static void tearDownJohnson() {
        Johnson.terminate();
    }

    @Before
    public void setUp() {
        when(backupPhaseBuilder.build()).thenReturn(backupPhase);
        when(backupPhaseBuilder.add(any(MaintenanceTask.class), anyInt())).thenReturn(backupPhaseBuilder);

        when(databaseManager.getCurrentLatch()).thenReturn(latch);

        when(maintenanceModePhaseBuilder.build()).thenReturn(maintenanceModePhase);
        when(maintenanceModePhaseBuilder.add(any(MaintenanceTask.class), anyInt())).thenReturn(maintenanceModePhaseBuilder);
        when(maintenanceModePhaseBuilder.event(any(JohnsonMaintenanceEvent.class))).thenReturn(maintenanceModePhaseBuilder);

        when(taskFactory.backupPhaseBuilder(any(BackupState.class))).thenReturn(backupPhaseBuilder);
        when(taskFactory.maintenanceModePhaseBuilder()).thenReturn(maintenanceModePhaseBuilder);

        new BackupTask(databaseManager, eventPublisher, i18nService, taskFactory, databaseManager);
    }

    @Test
    public void testConfig() {
        ArgumentCaptor<MaintenanceTask> taskCaptor = ArgumentCaptor.forClass(MaintenanceTask.class);
        List<MaintenanceTask> capturedTasks = taskCaptor.getAllValues();

        ArgumentCaptor<Integer> weightCaptor = ArgumentCaptor.forClass(Integer.class);
        List<Integer> capturedWeights = weightCaptor.getAllValues();

        //Verify that the MaintenanceEvent was provided to the builder
        verify(maintenanceModePhaseBuilder).event(any(JohnsonMaintenanceEvent.class));

        //Verify the _overall tasks_ on the root builder
        verify(maintenanceModePhaseBuilder, times(4)).add(taskCaptor.capture(), weightCaptor.capture());

        assertEquals(backupPhase, capturedTasks.get(0));
        assertEquals(50, capturedWeights.get(0).intValue());

        assertTrue(BackupClientPlaceholderStep.class.isAssignableFrom(capturedTasks.get(1).getClass()));
        assertEquals(50, capturedWeights.get(1).intValue());

        assertTrue(UnlatchScmStep.class.isAssignableFrom(capturedTasks.get(2).getClass()));
        assertEquals(0, capturedWeights.get(2).intValue());

        assertTrue(UnlatchDatabaseStep.class.isAssignableFrom(capturedTasks.get(3).getClass()));
        assertEquals(0, capturedWeights.get(3).intValue());

        verify(maintenanceModePhaseBuilder).build();

        //Clear the previously captured values to prepare for validating the _backup tasks_
        capturedTasks.clear();
        capturedWeights.clear();

        verify(backupPhaseBuilder, times(6)).add(taskCaptor.capture(), weightCaptor.capture());

        assertTrue(ChangelogsBackupStep.class.isAssignableFrom(capturedTasks.get(0).getClass()));
        assertEquals(2, capturedWeights.get(0).intValue());

        assertTrue(ActiveObjectsBackupStep.class.isAssignableFrom(capturedTasks.get(1).getClass()));
        assertEquals(14, capturedWeights.get(1).intValue());

        assertTrue(LatchAndDrainDatabaseStep.class.isAssignableFrom(capturedTasks.get(2).getClass()));
        assertEquals(3, capturedWeights.get(2).intValue());

        assertTrue(LatchAndDrainScmStep.class.isAssignableFrom(capturedTasks.get(3).getClass()));
        assertEquals(3, capturedWeights.get(3).intValue());

        assertTrue(DatabaseBackupStep.class.isAssignableFrom(capturedTasks.get(4).getClass()));
        assertEquals(76, capturedWeights.get(4).intValue());

        assertTrue(ConfigurationBackupStep.class.isAssignableFrom(capturedTasks.get(5).getClass()));
        assertEquals(2, capturedWeights.get(5).intValue());

        verify(backupPhaseBuilder).build();
    }
}
