package de.renew.navigator.gui;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreePath;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.stubbing.Answer;

import de.renew.navigator.FilesystemController;
import de.renew.navigator.NavigatorAction;
import de.renew.navigator.NavigatorConfigurator;
import de.renew.navigator.NavigatorExtension;
import de.renew.navigator.NavigatorPlugin;
import de.renew.navigator.models.Directory;
import de.renew.navigator.models.NavigatorFileTree;
import de.renew.plugin.PluginProperties;
import de.renew.windowmanagement.Workbench;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
class NavigatorGuiImplTest {

    @Mock
    private FilesystemController filesystem;

    @Mock
    private Workbench workbench;

    @Mock
    private NavigatorPlugin plugin;

    @Mock
    private NavigatorFileTree model;

    @Mock
    private PluginProperties plugincfg;

    private NavigatorGuiImpl gui;

    @BeforeEach
    void setUp() {
        when(plugin.getModel()).thenReturn(model);
        when(model.getTreeRoots()).thenReturn(Collections.emptyList());
        when(model.getTextSearch()).thenReturn(null);
        when(model.getBackgroundTasks()).thenReturn(Collections.emptyList());
        when(plugin.getProperties()).thenReturn(plugincfg);

        gui = new NavigatorGuiImpl(filesystem, workbench, plugin);
    }

    @Test
    void testCloseAll() {
        DefaultMutableTreeNode root = gui.getRootNode();
        root.add(new DefaultMutableTreeNode("child"));
        assertEquals(1, root.getChildCount());

        gui.closeAll();
        assertEquals(0, root.getChildCount());
    }

    @Test
    void testCollapseAll() {
        Directory dir1 = mock(Directory.class);
        Directory dir2 = mock(Directory.class);
        when(model.getTreeRoots()).thenReturn(Arrays.asList(dir1, dir2));

        gui.collapseAll();

        verify(dir1).setOpened(false);
        verify(dir2).setOpened(false);
        verify(model).notifyObservers();
    }

    @Test
    void testExpandWithNoSelection() {
        gui.expand();
        verify(model).expandAll();
        verify(model).notifyObservers();
    }

    @Test
    void testGetSelectedTreePathsAndNodesInitiallyEmpty() {
        TreePath[] paths = gui.getSelectedTreePaths();
        assertNotNull(paths);
        assertEquals(0, paths.length);

        List<?> nodes = gui.getSelectedNodes();
        assertNotNull(nodes);
        assertTrue(nodes.isEmpty());
    }

    @Test
    void testGetTreeAndRootNode() {
        assertNotNull(gui.getTree());
        assertNotNull(gui.getRootNode());
    }

    @Test
    void testRemoveMenuButton() throws Exception {
        var menuField = NavigatorGuiImpl.class.getDeclaredField("_menuPanel");
        menuField.setAccessible(true);
        JPanel menuPanel = (JPanel) menuField.get(gui);

        int before = menuPanel.getComponentCount();
        var first = menuPanel.getComponent(0);
        assertInstanceOf(JButton.class, first);

        gui.removeMenuButton((JButton) first);

        assertEquals(before - 1, menuPanel.getComponentCount());
        assertFalse(Arrays.asList(menuPanel.getComponents()).contains(first));
    }

    @Test
    void testAddAndRemoveExtension() throws Exception {
        var menuField = NavigatorGuiImpl.class.getDeclaredField("_menuPanel");
        var filterField = NavigatorGuiImpl.class.getDeclaredField("_filterPanel");
        menuField.setAccessible(true);
        filterField.setAccessible(true);

        JPanel menuPanel = (JPanel) menuField.get(gui);
        JPanel filterPanel = (JPanel) filterField.get(gui);

        int menuCount = menuPanel.getComponentCount();
        int filterCount = filterPanel.getComponentCount();

        NavigatorAction menuAction = mock(NavigatorAction.class);
        NavigatorAction filterAction = mock(NavigatorAction.class);

        NavigatorExtension ext = mock(NavigatorExtension.class);
        doAnswer((Answer<Void>) inv -> {
            NavigatorConfigurator cfg = inv.getArgument(0);
            cfg.addMenuAction(menuAction);
            cfg.addFilterAction(filterAction);
            return null;
        }).when(ext).configure(any());

        gui.addExtension(ext);
        assertTrue(gui.getExtensions().contains(ext));
        assertEquals(menuCount + 1, menuPanel.getComponentCount());
        assertEquals(filterCount + 1, filterPanel.getComponentCount());

        boolean removed = gui.removeExtension(ext);
        assertTrue(removed);
        assertFalse(gui.getExtensions().contains(ext));
        assertEquals(menuCount, menuPanel.getComponentCount());
        assertEquals(filterCount, filterPanel.getComponentCount());
    }
}
