package com.example.views;

import com.example.Application;
import com.example.components.BarChart;
import com.example.components.GridStackItem;
import com.example.components.GridStackLayout;
import com.example.components.LineChart;
import com.example.components.PieChart;
import com.vaadin.browserless.SpringBrowserlessTest;
import com.vaadin.browserless.ViewPackages;
import com.vaadin.browserless.internal.ElementUtilsKt;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.html.Div;
import com.vaadin.flow.dom.DomEvent;
import com.vaadin.flow.internal.JacksonUtils;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;

@SpringBootTest(classes = Application.class)
@ViewPackages(classes = DashboardView.class)
class DashboardViewTest extends SpringBrowserlessTest {

    @Test
    void view_rendersGridWithDefaultWidgets() {
        navigate(DashboardView.class);

        GridStackLayout grid = $view(GridStackLayout.class).first();
        assertNotNull(grid);

        List<GridStackItem.Position> layout = grid.getLayout();
        assertEquals(4, layout.size(), "expected the four default widgets");
        assertTrue(layout.stream().allMatch(p -> p.w() > 0 && p.h() > 0));
    }

    @Test
    void view_rendersTheThreeCharts() {
        navigate(DashboardView.class);

        assertNotNull($view(LineChart.class).first());
        assertNotNull($view(BarChart.class).first());
        assertNotNull($view(PieChart.class).first());
    }

    @Test
    void addAndCloseWidget_changesItemCount() {
        navigate(DashboardView.class);

        GridStackLayout grid = $view(GridStackLayout.class).first();
        int initial = grid.getLayout().size();

        button("gridstack.addWidget").click();
        assertEquals(initial + 1, grid.getLayout().size());

        clickCloseButton($view(GridStackItem.class).last());
        assertEquals(initial, grid.getLayout().size());
    }

    @Test
    void closeButton_removesTheClickedWidgetOnly() {
        navigate(DashboardView.class);

        GridStackLayout grid = $view(GridStackLayout.class).first();
        GridStackItem first = $view(GridStackItem.class).first();
        String closedId = first.getItemId();

        clickCloseButton(first);

        List<GridStackItem.Position> layout = grid.getLayout();
        assertEquals(3, layout.size());
        assertFalse(layout.stream().anyMatch(p -> closedId.equals(p.id())),
                "the closed widget must be gone from the layout");
    }

    @Test
    void closeListener_firesOnceItemIsDetached() {
        navigate(DashboardView.class);

        GridStackLayout grid = $view(GridStackLayout.class).first();
        GridStackItem item = $view(GridStackItem.class).first();
        int[] fired = {0};
        item.addCloseListener(e -> fired[0]++);

        item.close();

        assertEquals(1, fired[0]);
        assertEquals(3, grid.getLayout().size());
    }

    @Test
    void nonClosableItem_hasNoCloseButton() {
        navigate(DashboardView.class);

        GridStackItem item = $view(GridStackItem.class).first();
        assertTrue(item.isClosable(), "items are closable by default");

        item.setClosable(false);
        assertFalse(item.isClosable());
        assertFalse(item.getChildren().anyMatch(child -> child == item.getCloseButton()));
    }

    /** Fires the DOM click the close button listens for, rather than calling
     *  {@code close()} directly, so the button's own wiring is covered too. */
    private void clickCloseButton(GridStackItem item) {
        Div closeButton = item.getCloseButton();
        ElementUtilsKt._fireDomEvent(closeButton.getElement(),
                new DomEvent(closeButton.getElement(), "click", JacksonUtils.createObjectNode()));
    }

    private Button button(String translationKey) {
        String caption = getCurrentView().getElement().getComponent()
                .orElseThrow().getTranslation(translationKey);
        return $view(Button.class).withText(caption).first();
    }
}
