package completer;


import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Vector;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

import de.renew.console.CHDependencyListener;
import de.renew.console.completer.DrawingsCompleter;
import de.renew.plugin.IPlugin;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;

class DrawingsCompleterTest {

    private DrawingsCompleter _completer;
    private TestCHPlugin _mockPlugin;
    private TestGui _mockGui;

    interface TestCHPlugin extends IPlugin {
        Object getGui();
    }

    interface TestGui {
        Enumeration<TestDrawing> drawings();
    }

    interface TestDrawing {
        String getName();
    }

    @BeforeEach
    void setUp() {
        _completer = new DrawingsCompleter();
        _mockPlugin = mock(TestCHPlugin.class);
        _mockGui = mock(TestGui.class);
    }

    private Vector<TestDrawing> createDrawings(String... names) {
        Vector<TestDrawing> drawings = new Vector<>();
        for (String name : names) {
            TestDrawing drawing = mock(TestDrawing.class);
            when(drawing.getName()).thenReturn(name);
            drawings.add(drawing);
        }
        return drawings;
    }

    private List<CharSequence> newCandidatesList() {
        return new ArrayList<>();
    }

    @Test
    void testCompleteCHPluginAvailableHasDrawings() {
        try (MockedStatic<CHDependencyListener> mockedCH =
            Mockito.mockStatic(CHDependencyListener.class)) {
            // given
            mockedCH.when(CHDependencyListener::getChPlugin).thenReturn(_mockPlugin);
            Vector<TestDrawing> drawings = createDrawings("drawing1", "drawing2");
            when(_mockPlugin.getGui()).thenReturn(_mockGui);
            when(_mockGui.drawings()).thenReturn(drawings.elements());
            List<CharSequence> candidates = newCandidatesList();

            // when
            int result = _completer.complete("draw", 0, candidates);

            // then
            assertThat(result, is(0));
            assertThat(candidates, containsInAnyOrder("drawing1", "drawing2"));
            mockedCH.verify(CHDependencyListener::getChPlugin, times(3));
        }
    }

    @Test
    void testCompleteDrawingsIsEmpty() {
        try (MockedStatic<CHDependencyListener> mockedCH =
            Mockito.mockStatic(CHDependencyListener.class)) {
            // given
            mockedCH.when(CHDependencyListener::getChPlugin).thenReturn(_mockPlugin);
            when(_mockPlugin.getGui()).thenReturn(_mockGui);
            when(_mockGui.drawings()).thenReturn(new Vector<TestDrawing>().elements());
            List<CharSequence> candidates = newCandidatesList();

            // when
            int result = _completer.complete("draw", 0, candidates);

            // then
            assertThat(result, is(-1));
            assertThat(candidates, is(empty()));
            mockedCH.verify(CHDependencyListener::getChPlugin, times(3));
        }
    }

    @Test
    void testCompleteGuiIsNull() {
        try (MockedStatic<CHDependencyListener> mockedCH =
            Mockito.mockStatic(CHDependencyListener.class)) {
            // given
            mockedCH.when(CHDependencyListener::getChPlugin).thenReturn(_mockPlugin);
            when(_mockPlugin.getGui()).thenReturn(null);
            List<CharSequence> candidates = new ArrayList<>();

            // when
            int result = _completer.complete("draw", 0, candidates);

            // then
            assertThat(result, is(-1));
            assertThat(candidates, is(empty()));
            mockedCH.verify(CHDependencyListener::getChPlugin, times(3));
        }
    }

    @Test
    void testCompleteSomeCandidatesMatch() {
        try (MockedStatic<CHDependencyListener> mockedCH =
            Mockito.mockStatic(CHDependencyListener.class)) {
            // given
            mockedCH.when(CHDependencyListener::getChPlugin).thenReturn(_mockPlugin);
            Vector<TestDrawing> drawings = createDrawings("drawing1", "draw2", "example");
            when(_mockPlugin.getGui()).thenReturn(_mockGui);
            when(_mockGui.drawings()).thenReturn(drawings.elements());
            List<CharSequence> candidates = newCandidatesList();

            // when
            int result = _completer.complete("draw", 0, candidates);

            // then
            assertThat(result, is(0));
            assertThat(candidates, containsInAnyOrder("drawing1", "draw2"));
            mockedCH.verify(CHDependencyListener::getChPlugin, times(3));
        }
    }

    @Test
    void testCompleteNoSuchMethodExceptionOccurs() {
        try (MockedStatic<CHDependencyListener> mockedCH =
            Mockito.mockStatic(CHDependencyListener.class)) {
            // given
            mockedCH.when(CHDependencyListener::getChPlugin).thenReturn(_mockPlugin);
            when(_mockPlugin.getGui()).thenReturn(new Object());
            List<CharSequence> candidates = newCandidatesList();

            // when
            int result = _completer.complete("draw", 0, candidates);

            // then
            assertThat(result, is(-1));
            assertThat(candidates, is(empty()));
            mockedCH.verify(CHDependencyListener::getChPlugin, times(3));
        }
    }

    @Test
    @Disabled("Temporarily ignored due to NPE")
    void testCompleteCHPluginUnavailable() {
        try (MockedStatic<CHDependencyListener> mockedCH =
            Mockito.mockStatic(CHDependencyListener.class)) {
            // given
            mockedCH.when(CHDependencyListener::getChPlugin).thenReturn(null);
            List<CharSequence> candidates = newCandidatesList();

            // when
            int result = _completer.complete("draw", 0, candidates);

            // then
            assertThat(result, is(-1));
            assertThat(candidates, is(empty()));

            // to resolve:
            // IPlugin chPlugin = CHDependencyListener.getChPlugin();
            // if (chPlugin == null) {
            //     return drawingNames; // Early exit if plugin is unavailable
            // }
            // in DrawingsCompleter getDrawingNames
        }
    }
}
