package de.renew.propertymanagementgui.gui;

import java.awt.Color;
import java.awt.Dimension;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.swing.BoxLayout;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.MockedConstruction;
import org.mockito.Mockito;

import static org.mockito.Mockito.mock;

/**
 * Unit test class for SideNavItemsComponent
 *
 * @author 7kraft
 */
class SideNavItemsComponentTest {

    // helpers

    private static Field field(
        @SuppressWarnings("SameParameterValue") Class<?> theClass, String name) throws Exception
    {
        Field field = theClass.getDeclaredField(name);
        field.setAccessible(true);
        return field;
    }

    private Map<String, Runnable> sampleEntries() {
        Map<String, Runnable> map = new LinkedHashMap<>();
        map.put("Plugin A", Mockito.mock(Runnable.class));
        map.put("Plugin B", Mockito.mock(Runnable.class));
        map.put("Plugin C", Mockito.mock(Runnable.class));
        return map;
    }

    // test methods

    @Test
    public void testRender() throws Exception {
        //given
        Map<String, Runnable> entries = sampleEntries();
        SideNavItemsComponent comp = new SideNavItemsComponent(entries);

        JPanel panelA = new JPanel();
        JPanel panelB = new JPanel();
        JPanel panelC = new JPanel();

        List<Object[]> ctorArgs = new ArrayList<>();

        try (@SuppressWarnings("unused")
        MockedConstruction<PluginNavigationComponent> mockedPlugins =
            Mockito.mockConstruction(PluginNavigationComponent.class, (mock, ctx) -> {
                ctorArgs.add(ctx.arguments().toArray());
                String name = (String) ctx.arguments().get(0);
                if ("Plugin A".equals(name))
                    Mockito.when(mock.get()).thenReturn(panelA);
                if ("Plugin B".equals(name))
                    Mockito.when(mock.get()).thenReturn(panelB);
                if ("Plugin C".equals(name))
                    Mockito.when(mock.get()).thenReturn(panelC);
            })) {

            //when
            JComponent rendered = comp.render();

            //then
            Assertions.assertThat(rendered).isNotNull();
            Assertions.assertThat(rendered).isInstanceOf(JScrollPane.class);

            JScrollPane scroll = (JScrollPane) rendered;
            Assertions.assertThat(scroll.getHorizontalScrollBarPolicy())
                .isEqualTo(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
            Assertions.assertThat(scroll.getVerticalScrollBarPolicy())
                .isEqualTo(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);

            JComponent list = (JComponent) scroll.getViewport().getView();
            Assertions.assertThat(list).isNotNull();
            Assertions.assertThat(list.getLayout()).isInstanceOf(BoxLayout.class);

            Color expectedBg =
                (Color) field(SideNavItemsComponent.class, "BACKGROUND_COLOR_WHITE").get(null);
            Assertions.assertThat(list.getBackground()).isEqualTo(expectedBg);

            int expectedWidth =
                (int) field(SideNavItemsComponent.class, "SIDE_NAV_ITEMS_WIDTH").get(null);
            int expectedHeight = entries.size() * 20;
            Assertions.assertThat(list.getPreferredSize())
                .isEqualTo(new Dimension(expectedWidth, expectedHeight));

            Assertions.assertThat(list.getComponentCount()).isEqualTo(3);
            Assertions.assertThat(list.getComponent(0)).isSameAs(panelA);
            Assertions.assertThat(list.getComponent(1)).isSameAs(panelB);
            Assertions.assertThat(list.getComponent(2)).isSameAs(panelC);

            Assertions.assertThat(ctorArgs.size()).isEqualTo(3);
            Object[][] expected = {
                { "Plugin A", entries.get("Plugin A") }, { "Plugin B", entries.get("Plugin B") },
                { "Plugin C", entries.get("Plugin C") } };
            for (int i = 0; i < 3; i++) {
                Assertions.assertThat(ctorArgs.get(i)[0]).isEqualTo(expected[i][0]);
                Assertions.assertThat(ctorArgs.get(i)[1]).isSameAs(expected[i][1]);
            }
        }
    }
}