package com.atlassian.bitbucket.internal.branch.web;

import com.atlassian.bitbucket.internal.branch.automerge.AutoMergeConfig;
import com.atlassian.bitbucket.internal.branch.model.configuration.BranchModelConfiguration;
import com.atlassian.bitbucket.internal.branch.model.configuration.BranchModelConfigurationService;
import com.atlassian.bitbucket.internal.branch.model.configuration.SimpleBranchConfiguration;
import com.atlassian.bitbucket.internal.branch.model.configuration.SimpleBranchModelConfiguration;
import com.atlassian.bitbucket.internal.branch.model.web.BranchModelSettingsServlet;
import com.atlassian.bitbucket.nav.NavBuilder;
import com.atlassian.bitbucket.permission.PermissionValidationService;
import com.atlassian.bitbucket.repository.*;
import com.atlassian.sal.api.xsrf.XsrfTokenAccessor;
import com.atlassian.sal.api.xsrf.XsrfTokenValidator;
import com.atlassian.soy.renderer.SoyTemplateRenderer;
import com.atlassian.webresource.api.assembler.PageBuilderService;
import com.atlassian.webresource.api.assembler.RequiredResources;
import com.atlassian.webresource.api.assembler.WebResourceAssembler;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

import static com.atlassian.bitbucket.hamcrest.RepositoryMatchers.resolveBranchRequest;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;

public class BranchModelSettingsServletTest {

    private static final String XSRF_TOKEN_VALUE = "xsrf_token_value";
    private static final String XSRF_TOKEN_NAME = "xsrf_token_name";
    @Rule
    public MockitoRule mockitoRule = MockitoJUnit.rule();

    @Mock
    private RepositoryService repositoryService;
    @Mock
    private RefService refService;
    @Mock
    private BranchModelConfigurationService branchModelConfigurationService;
    @Mock
    private AutoMergeConfig autoMergeConfig;
    @Mock
    private NavBuilder navBuilder;
    @Mock
    private SoyTemplateRenderer soyTemplateRenderer;
    @Mock
    private PageBuilderService pageBuilderService;
    @Mock
    private PermissionValidationService permissionValidationService;
    @Mock
    private WebResourceAssembler webResourceAssembler;
    @Mock
    private XsrfTokenAccessor xsrfTokenAccessor;
    @Mock
    private XsrfTokenValidator xsrfTokenValidator;

    @InjectMocks
    private BranchModelSettingsServlet servlet;

    @Mock
    private HttpServletRequest req;
    @Mock
    private RequestDispatcher reqDispatcher;
    @Mock
    private HttpServletResponse resp;
    @Mock
    private Repository repo;
    @Captor
    private ArgumentCaptor<Map<String, Object>> dataCaptor;
    @Captor
    private ArgumentCaptor<Map<String, Object>> ijCaptor;


    @Before
    public void setup() {
        when(req.getMethod()).thenReturn("GET");
        when(req.getPathInfo()).thenReturn("/projects/TEST/repos/test");
        when(repositoryService.getBySlug("TEST", "test")).thenReturn(repo);
        when(pageBuilderService.assembler()).thenReturn(webResourceAssembler);
        when(webResourceAssembler.resources()).thenReturn(mock(RequiredResources.class));
        when(branchModelConfigurationService.getDefaultConfiguration()).thenReturn(mock(BranchModelConfiguration.class));
        when(xsrfTokenAccessor.getXsrfToken(any(), any(), eq(true))).thenReturn(XSRF_TOKEN_VALUE);
        when(xsrfTokenValidator.getXsrfParameterName()).thenReturn(XSRF_TOKEN_NAME);
        when(xsrfTokenValidator.validateFormEncodedToken(any())).thenReturn(true);
    }

    @Test
    public void testResetDevToDefaultValueIfTheCorrespondingBranchDoesNotExist() throws Exception {
        when(branchModelConfigurationService.getConfiguration(repo)).thenReturn(new SimpleBranchModelConfiguration.Builder()
                .development(new SimpleBranchConfiguration("refs/heads/some", false)).build());
        when(refService.resolveRef(notNull(ResolveRefRequest.class))).thenReturn(null);

        BranchModelConfiguration configuration = call();
        assertThat(configuration.getDevelopment().isUseDefault(), is(true));

        verify(refService).resolveRef(argThat(resolveBranchRequest(repo, "refs/heads/some")));
    }

    @Test
    public void testResetProductionToEmptyIfTheCorrespondingBranchDoesNotExist() throws Exception {
        when(branchModelConfigurationService.getConfiguration(repo)).thenReturn(new SimpleBranchModelConfiguration.Builder()
                .development(new SimpleBranchConfiguration(null, true))
                .production(new SimpleBranchConfiguration("refs/heads/some", false))
                .build());
        when(refService.resolveRef(notNull(ResolveRefRequest.class))).thenReturn(null);

        BranchModelConfiguration configuration = call();
        assertThat(configuration.getProduction(), nullValue());

        verify(refService).resolveRef(argThat(resolveBranchRequest(repo, "refs/heads/some")));
    }

    @SuppressWarnings("unchecked")
    @Test
    public void testXsrfFailureWhilePOST() throws Exception {
        when(req.getMethod()).thenReturn("POST");
        when(xsrfTokenValidator.validateFormEncodedToken(any())).thenReturn(false);
        when(req.getRequestDispatcher(any())).thenReturn(reqDispatcher);
        when(navBuilder.xsrfNotification()).thenReturn(mock(NavBuilder.Builder.class));

        servlet.service(req, resp);

        verify(navBuilder).xsrfNotification();
    }

    private BranchModelConfiguration call() throws Exception {
        servlet.service(req, resp);

        verify(soyTemplateRenderer).render(any(), any(), any(), dataCaptor.capture(), ijCaptor.capture());

        Map<String, Object> ij = ijCaptor.getValue();
        assertEquals(XSRF_TOKEN_NAME, ij.get("xsrfTokenName"));
        assertEquals(XSRF_TOKEN_VALUE, ij.get("xsrfTokenValue"));

        Map<String, Object> context = dataCaptor.getValue();
        assertThat(context, hasKey("branchModel"));
        Object branchModel = context.get("branchModel");
        assertThat(branchModel, instanceOf(BranchModelConfiguration.class));
        return (BranchModelConfiguration) branchModel;
    }
}
