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

import com.atlassian.soy.renderer.SoyTemplateRenderer;
import com.atlassian.soy.springmvc.SoyView;
import com.atlassian.bitbucket.i18n.I18nService;
import com.atlassian.bitbucket.i18n.SimpleI18nService;
import com.atlassian.stash.internal.maintenance.*;
import com.atlassian.stash.internal.scm.InternalScmService;
import com.atlassian.bitbucket.request.RequestManager;
import com.atlassian.bitbucket.util.Progress;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.*;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import java.util.Map;
import java.util.concurrent.TimeUnit;

import static org.hamcrest.CoreMatchers.instanceOf;
import static org.mockito.Matchers.anyString;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

@RunWith(MockitoJUnitRunner.class)
public class MaintenanceControllerTest {

    @InjectMocks
    private MaintenanceController controller;
    @Spy
    @SuppressWarnings("UnusedDeclaration")
    private I18nService i18nService = new SimpleI18nService();
    @Mock
    private MaintenanceLock lockContext;
    @Mock
    private MaintenanceService maintenanceService;
    @Mock
    private Progress progress;
    @Mock
    private RequestManager requestManager;
    @Mock
    private InternalScmService scmService;
    @Mock
    private MaintenanceTaskMonitor taskMonitor;
    @Mock
    private SoyTemplateRenderer templateRenderer;

    @Before
    public void setUp() throws Exception {
        doReturn("Blah").when(progress).getMessage();
        doReturn(0).when(progress).getPercentage();

        doReturn(progress).when(taskMonitor).getProgress();

        doReturn(taskMonitor).when(maintenanceService).getRunningTask();
        when(taskMonitor.getType()).thenReturn(MaintenanceType.MIGRATION);
    }

    @Test
    public void testCancelMaintenance() throws Exception {
        RedirectAttributes redirectAttributes = mock(RedirectAttributes.class);

        ModelAndView modelAndView = controller.cancelMaintenance("foo", redirectAttributes);
        assertEquals(MaintenanceController.VIEW_DB_REDIRECT, modelAndView.getViewName());
        verify(taskMonitor).cancel(eq("foo"), anyLong(), any(TimeUnit.class));
    }

    @Test
    public void testCancelMaintenanceWithNoContext() throws Exception {
        reset(maintenanceService);

        ModelAndView modelAndView = controller.cancelMaintenance("foo", null);
        assertNotNull(modelAndView);
        assertViewName(MaintenanceController.VIEW_HOME_REDIRECT, modelAndView);
    }

    @Test
    public void testCancelMaintenanceWithWrongToken() throws Exception {
        IncorrectTokenMaintenanceException exception = mock(IncorrectTokenMaintenanceException.class);
        when(exception.getLocalizedMessage()).thenReturn("msg");
        doThrow(exception).when(taskMonitor).cancel(anyString(), anyLong(), any(TimeUnit.class));
        RedirectAttributes redirectAttributes = mock(RedirectAttributes.class);

        ModelAndView modelAndView = controller.cancelMaintenance("bar", redirectAttributes);
        assertViewName(MaintenanceController.VIEW_MAINTENANCE_MIGRATION, modelAndView);
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testViewMaintenance() throws Exception {
        ModelAndView modelAndView = controller.viewMaintenance();
        assertNotNull(modelAndView);
        View view = modelAndView.getView();
        Map<String, Object> model = modelAndView.getModel();
        assertThat(view, instanceOf(SoyView.class));
        assertNotNull(model);
        assertEquals(null, modelAndView.getModel().get("cancelToken"));
        view.render(modelAndView.getModel(), mock(HttpServletRequest.class), mock(HttpServletResponse.class));
        verify(templateRenderer)
                .render(any(Appendable.class), anyString(), eq(MaintenanceController.VIEW_MAINTENANCE_MIGRATION),
                        anyMap(), anyMap());
        reset(templateRenderer);
    }


    private static void assertViewName(String expected, ModelAndView modelAndView) {
        if (modelAndView.getViewName() != null) {
            assertEquals(expected, modelAndView.getViewName());
        } else {
            View view = modelAndView.getView();
            assertNotNull(view);

            DirectFieldAccessor accessor = new DirectFieldAccessor(view);
            assertEquals(expected, accessor.getPropertyValue("viewName"));
        }
    }
}
