package de.renew.logging.gui.test;

import java.util.List;

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

import de.renew.logging.gui.LoggerTableModel;
import de.renew.logging.gui.MainRepositoryManager;
import de.renew.logging.gui.StepTrace;
import de.renew.logging.gui.StepTraceRepository;
import de.renew.logging.gui.test.helper.TestRepositoryChangeListener;
import de.renew.logging.gui.test.helper.TestStepTraceRepository;

import static de.renew.logging.gui.test.helper.LoggingMocks.getLoggerTableModel;
import static de.renew.logging.gui.test.helper.LoggingMocks.getStepIdentifier;
import static de.renew.logging.gui.test.helper.LoggingMocks.getStepTrace;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class LoggerTableModelTest {

    @Test
    public void testConstructor() {
        final MainRepositoryManager mainRepositoryMock = mock(MainRepositoryManager.class);
        final StepTraceRepository stepTraceRepositoryMock = mock(StepTraceRepository.class);
        final StepTrace[] stepTraces = new StepTrace[0];

        when(stepTraceRepositoryMock.getAllStepTraces()).thenReturn(stepTraces);

        try (final MockedStatic<MainRepositoryManager> statics =
            Mockito.mockStatic(MainRepositoryManager.class)) {
            // main repository is null
            statics.when(MainRepositoryManager::getInstance).thenReturn(mainRepositoryMock);
            final String myLoggerTableModel = "myLoggerTableModel";
            new LoggerTableModel(myLoggerTableModel); // Test is that no Exception is thrown

            // main repository is != null
            when(mainRepositoryMock.getCurrentRepository("myLoggerTableModel"))
                .thenReturn(stepTraceRepositoryMock);
            new LoggerTableModel(myLoggerTableModel); // Test is that no Exception is thrown

        }
    }

    @Test
    public void testGetIndexOf() {
        final StepTrace stepTrace = new StepTrace(getStepIdentifier());
        final LoggerTableModel ltm = getLoggerTableModel(stepTrace);

        assertEquals(-1, ltm.getIndexOf(getStepTrace()));
        assertEquals(0, ltm.getIndexOf(stepTrace));
    }

    @Test
    public void testFireStepTraceAdded() {
        // given
        final StepTrace stepTrace = new StepTrace(getStepIdentifier());
        final LoggerTableModel ltm = getLoggerTableModel(stepTrace);
        final TestStepTraceRepository repository = new TestStepTraceRepository();
        final TestRepositoryChangeListener listener = new TestRepositoryChangeListener();

        // when/then: no listeners
        ltm.fireStepTraceAdded(repository, stepTrace); // no error thrown

        // when: with 1 listener
        ltm.addRepositoryChangeListener(listener);
        ltm.fireStepTraceAdded(repository, stepTrace); // no error thrown

        // then
        final List<StepTraceRepository> stepTraceRepositoryList =
            listener.getStepTraceRepositoryAddedList();
        assertEquals(1, stepTraceRepositoryList.size());
        assertEquals(repository, stepTraceRepositoryList.get(0));

        final List<StepTrace> stepTraceList = listener.getStepTraceAddedList();
        assertEquals(1, stepTraceList.size());
        assertEquals(stepTrace, stepTraceList.get(0));

        // given: with 2 listeners
        ltm.addRepositoryChangeListener(new TestRepositoryChangeListener());

        // when: with 2 listeners
        ltm.addRepositoryChangeListener(listener);
        ltm.fireStepTraceAdded(repository, stepTrace); // no error thrown

        // then
        assertEquals(2, stepTraceRepositoryList.size());
        assertEquals(2, stepTraceList.size());
    }

    /**
     * This is rather hard to test alot of this depends on internal functionality which is not really tested by this test.
     */
    @Test
    public void testStepTraceAddedRemoved() {
        // given
        final StepTrace stepTrace = new StepTrace(getStepIdentifier());
        final StepTrace stepTrace2 = new StepTrace(getStepIdentifier());
        final LoggerTableModel ltm = getLoggerTableModel(stepTrace);
        final TestStepTraceRepository repository = new TestStepTraceRepository();
        final TestRepositoryChangeListener listener = new TestRepositoryChangeListener();
        final List<StepTrace> stepTraceRemovedList = listener.getStepTraceRemovedList();
        final List<StepTraceRepository> stepTraceRepositoryRemovedList =
            listener.getStepTraceRepositoryRemovedList();
        ltm.addRepositoryChangeListener(listener);

        // when
        ltm.stepTraceAdded(repository, stepTrace2);

        // then
        assertEquals(0, ltm.getIndexOf(stepTrace));
        assertEquals(1, ltm.getIndexOf(stepTrace2));
        assertEquals(1, listener.getStepTraceAddedList().size());
        assertEquals(0, stepTraceRepositoryRemovedList.size());
        assertEquals(0, stepTraceRemovedList.size());

        // when
        ltm.stepTraceRemoved(repository, stepTrace);
        ltm.stepTraceRemoved(repository, stepTrace); // no error thrown
        ltm.stepTraceRemoved(repository, stepTrace2);

        // then
        assertEquals(-1, ltm.getIndexOf(stepTrace));
        assertEquals(-1, ltm.getIndexOf(stepTrace2));
        assertEquals(3, stepTraceRepositoryRemovedList.size());
        assertEquals(3, stepTraceRemovedList.size());

    }

    @Test
    public void testStepTraceAddedRemoved2() {
        // given
        final StepTrace stepTrace = new StepTrace(getStepIdentifier());
        final StepTrace stepTrace2 = new StepTrace(getStepIdentifier());
        final LoggerTableModel ltm = getLoggerTableModel(stepTrace);
        final TestStepTraceRepository repository = new TestStepTraceRepository();
        final TestRepositoryChangeListener listener = new TestRepositoryChangeListener();
        ltm.addRepositoryChangeListener(listener);
        ltm.setPermanentUpdate(false);

        // when
        ltm.stepTraceAdded(repository, stepTrace);

        // then
        assertEquals(0, ltm.getIndexOf(stepTrace));
        assertEquals(-1, ltm.getIndexOf(stepTrace2));
        assertEquals(0, listener.getStepTraceAddedList().size()); // listener was added after creation, so it cannot know of the previous StepTrace
        assertEquals(0, listener.getStepTraceRepositoryAddedList().size());

        // when
        ltm.stepTraceRemoved(repository, stepTrace);
        ltm.stepTraceRemoved(repository, stepTrace2);

        // then
        assertEquals(0, ltm.getIndexOf(stepTrace));
        assertEquals(-1, ltm.getIndexOf(stepTrace2));
        assertEquals(0, listener.getStepTraceRemovedList().size());
        assertEquals(0, listener.getStepTraceRepositoryRemovedList().size());
    }

    @Test
    public void testStepTraceChanged() {
        final StepTrace stepTrace = new StepTrace(getStepIdentifier());
        final StepTrace stepTrace2 = new StepTrace(getStepIdentifier());
        final LoggerTableModel ltm = getLoggerTableModel(stepTrace);
        final TestRepositoryChangeListener listener = new TestRepositoryChangeListener();
        ltm.addRepositoryChangeListener(listener);

        ltm.stepTraceChanged(stepTrace2);

        assertEquals(1, listener.getStepTraceChangedList().size());
        assertEquals(stepTrace2, listener.getStepTraceChangedList().get(0));

        ltm.stepTraceChanged(stepTrace);
        assertEquals(2, listener.getStepTraceChangedList().size());
    }
}
