package com.atlassian.stash.internal.web.soy.functions;

import com.atlassian.bitbucket.auth.AuthenticationContext;
import com.atlassian.plugin.web.api.DynamicWebInterfaceManager;
import com.atlassian.plugin.web.api.WebItem;
import com.atlassian.plugin.web.api.WebSection;
import com.atlassian.plugin.web.api.model.WebFragmentBuilder;
import com.atlassian.sal.api.web.context.HttpContext;
import com.atlassian.stash.internal.web.fragments.WebFragmentData;
import com.atlassian.stash.internal.web.fragments.WebFragmentSupport;
import com.atlassian.stash.internal.web.fragments.WebItemData;
import com.atlassian.stash.internal.web.fragments.WebSectionData;
import org.junit.Test;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static com.atlassian.stash.internal.web.fragments.WebFragmentData.Type.ITEM;
import static com.atlassian.stash.internal.web.fragments.WebFragmentData.Type.SECTION;
import static org.junit.Assert.assertEquals;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.anyMapOf;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class WebFragmentFunctionTest {

    private WebSection sectionWeight5 = createWebSection(5);
    private WebSection sectionWeight15 = createWebSection(15);
    private WebSection sectionWeight25 = createWebSection(25);
    private WebItem itemWeight30 = createWebItem(30);
    private WebItem itemWeight10 = createWebItem(10);
    private WebItem itemWeight20 = createWebItem(20);
    private WebSection sectionWeight20Also = createWebSection(20);

    private HttpContext httpContext = mock(HttpContext.class);
    private DynamicWebInterfaceManager manager = mock(DynamicWebInterfaceManager.class);
    private AuthenticationContext authenticationContext = mock(AuthenticationContext.class);
    private WebFragmentSupport webFragmentSupport = new WebFragmentSupport(manager, authenticationContext, httpContext);
    private WebFragmentFunction function = new WebFragmentFunction(webFragmentSupport);

    @Test
    public void shouldSortItemsAndSectionsByWeight() throws Exception {
        given(manager.getDisplayableWebItems(eq("location"), anyMapOf(String.class, Object.class))).willReturn(Arrays.asList(itemWeight10, itemWeight20, itemWeight30));
        given(manager.getDisplayableWebSections(eq("location"), anyMapOf(String.class, Object.class))).willReturn(Arrays.asList(sectionWeight5, sectionWeight15, sectionWeight25));

        List<WebFragmentData> sortedFragments = function.apply("location");

        assertEquals(6, sortedFragments.size());
        assertTypeAndWeight(SECTION, 5, sortedFragments.get(0));
        assertTypeAndWeight(ITEM, 10, sortedFragments.get(1));
        assertTypeAndWeight(SECTION, 15, sortedFragments.get(2));
        assertTypeAndWeight(ITEM, 20, sortedFragments.get(3));
        assertTypeAndWeight(SECTION, 25, sortedFragments.get(4));
        assertTypeAndWeight(ITEM, 30, sortedFragments.get(5));
    }

    @Test
    public void shouldKeepItemsWithSameWeight() throws Exception {
        given(manager.getDisplayableWebItems(eq("location"), anyMapOf(String.class, Object.class))).willReturn(Arrays.asList(itemWeight10, itemWeight20, itemWeight30));
        given(manager.getDisplayableWebSections(eq("location"), anyMapOf(String.class, Object.class))).willReturn(Arrays.asList(sectionWeight20Also));

        List<WebFragmentData> sortedFragments = function.apply("location");

        assertEquals(4, sortedFragments.size());
        assertEquals(10, sortedFragments.get(0).getWeight());
        // We don't know the order of the item and section with weight 20... and don't care
        assertEquals(20, sortedFragments.get(1).getWeight());
        assertEquals(20, sortedFragments.get(2).getWeight());
        assertEquals(30, sortedFragments.get(3).getWeight());
    }

    @Test
    public void testWebSectionDataFields() {
        WebSection section = new WebFragmentBuilder(42)
                .id("id")
                .label("label")
                .webSection("location")
                .build();
        when(manager.getDisplayableWebItems(eq("location"), anyMapOf(String.class, Object.class)))
                .thenReturn(Collections.emptyList());
        when(manager.getDisplayableWebSections(eq("location"), anyMapOf(String.class, Object.class)))
                .thenReturn(Collections.singleton(section));

        List<WebFragmentData> fragments = function.apply("location");
        assertEquals(1, fragments.size());
        WebSectionData sectionData = (WebSectionData) fragments.get(0);
        assertEquals(section.getId(), sectionData.getKey());
        assertEquals(section.getLocation(), sectionData.getLocation());
        assertEquals(section.getLabel(), sectionData.getLabelText());
        assertEquals(section.getWeight(), sectionData.getWeight());
    }

    @Test
    public void testWebItemDataFields() {
        WebItem item = new WebFragmentBuilder(42)
                .id("id")
                .label("label")
                .webItem("location")
                .url("URL")
                .build();
        when(manager.getDisplayableWebItems(eq("location"), anyMapOf(String.class, Object.class)))
                .thenReturn(Collections.singleton(item));
        when(manager.getDisplayableWebSections(eq("location"), anyMapOf(String.class, Object.class)))
                .thenReturn(Collections.emptyList());

        List<WebFragmentData> fragments = function.apply("location");
        assertEquals(1, fragments.size());
        WebItemData itemData = (WebItemData) fragments.get(0);
        assertEquals(item.getId(), itemData.getId());
        assertEquals(item.getSection(), itemData.getLocation());
        assertEquals(item.getLabel(), itemData.getLinkText());
        assertEquals(item.getUrl(), itemData.getUrl());
        assertEquals(item.getWeight(), itemData.getWeight());
    }

    private void assertTypeAndWeight(WebFragmentData.Type expectedType, int expectedWeight, WebFragmentData actual) {
        assertEquals(expectedWeight, actual.getWeight());
        assertEquals(expectedType.name(), actual.getType());
    }

    private WebSection createWebSection(int weight) {
        return new WebFragmentBuilder(weight).webSection("unused").build();
    }

    private WebItem createWebItem(int weight) {
        return new WebFragmentBuilder(weight).webItem("unused").build();
    }
}
