package de.renew.draw.storables.impl.services;

import java.awt.Rectangle;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import java.util.stream.Stream;

import org.assertj.core.data.Index;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.MockedConstruction;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

import CH.ifa.draw.IOHelper;
import CH.ifa.draw.figures.ElbowTextLocator;
import CH.ifa.draw.framework.DrawingTypeManager;
import CH.ifa.draw.standard.FilteredFigureEnumerator;
import CH.ifa.draw.standard.MergedFigureEnumerator;
import CH.ifa.draw.standard.NullDrawing;
import CH.ifa.draw.standard.OffsetLocator;
import CH.ifa.draw.standard.StandardDrawing;
import de.renew.draw.storables.impl.drawings.DrawingExportFormatHolder;
import de.renew.draw.storables.impl.drawings.DrawingImportFormatHolder;
import de.renew.draw.storables.impl.util.internal.StorableHelper;
import de.renew.draw.storables.ontology.Drawing;
import de.renew.draw.storables.ontology.DrawingChangeEvent;
import de.renew.draw.storables.ontology.Figure;
import de.renew.draw.storables.ontology.FigureChangeEvent;
import de.renew.draw.storables.ontology.FigureEnumeration;
import de.renew.draw.storables.ontology.Locator;
import de.renew.draw.storables.ontology.Storable;
import de.renew.ioontology.ExtensionFileFilter;
import de.renew.ioontology.MultiExtensionFileFilter;
import de.renew.ioontology.MultiExtensionFileFilterImpl;
import de.renew.ioontology.exporting.ExportFormat;
import de.renew.ioontology.importing.ImportFormat;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockConstruction;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

class StorableServiceImplTest {

    private static final String DUMMY_DRAWING_TYPE = "Dummy-Type";
    private static final ExportFormat<Drawing> DUMMY_EXPORT_FORMAT = mock(ExportFormat.class);
    private static final ImportFormat<Drawing> DUMMY_IMPORT_FORMAT = mock(ImportFormat.class);

    private MockedConstruction<CH.ifa.draw.util.StorableInput> _mockedConstructionStorableInput;
    private MockedConstruction<CH.ifa.draw.util.PatchingStorableInput> _mockedConstructionPatchingStorableInput;
    private MockedConstruction<CH.ifa.draw.util.StorableOutput> _mockedConstructionStorableOutput;
    private MockedConstruction<CH.ifa.draw.framework.DrawingChangeEvent> _mockedConstructionDrawingChangeEvent;
    private MockedConstruction<CH.ifa.draw.framework.FigureChangeEvent> _mockedConstructionFigureChangeEvent;
    private MockedConstruction<OffsetLocator> _mockedConstructionOffsetLocator;

    private MockedConstruction<FilteredFigureEnumerator> _mockedConstructionFilteredFigureEnumerator;
    private MockedConstruction<MergedFigureEnumerator> _mockedConstructionMergedFigureEnumerator;

    private StorableServiceImpl _service;

    private Map<Object, List<Object>> _constructorArgs;
    private Drawing _mockedDrawing;

    @BeforeEach
    void setUp() {
        _service = new StorableServiceImpl();
        _mockedDrawing = mock(Drawing.class);
        _constructorArgs = new HashMap<>();

        _mockedConstructionStorableInput =
            createMockConstruction(CH.ifa.draw.util.StorableInput.class);
        _mockedConstructionStorableOutput =
            createMockConstruction(CH.ifa.draw.util.StorableOutput.class);
        _mockedConstructionPatchingStorableInput =
            createMockConstruction(CH.ifa.draw.util.PatchingStorableInput.class);
        _mockedConstructionDrawingChangeEvent =
            createMockConstruction(CH.ifa.draw.framework.DrawingChangeEvent.class);
        _mockedConstructionFigureChangeEvent =
            createMockConstruction(CH.ifa.draw.framework.FigureChangeEvent.class);
        _mockedConstructionFilteredFigureEnumerator =
            createMockConstruction(FilteredFigureEnumerator.class);
        _mockedConstructionMergedFigureEnumerator =
            createMockConstruction(MergedFigureEnumerator.class);
        _mockedConstructionOffsetLocator = createMockConstruction(OffsetLocator.class);
    }

    @AfterEach
    void tearDown() {
        _mockedConstructionStorableInput.close();
        _mockedConstructionStorableInput = null;

        _mockedConstructionStorableOutput.close();
        _mockedConstructionStorableOutput = null;

        _mockedConstructionPatchingStorableInput.close();
        _mockedConstructionPatchingStorableInput = null;

        _mockedConstructionDrawingChangeEvent.close();
        _mockedConstructionDrawingChangeEvent = null;

        _mockedConstructionFigureChangeEvent.close();
        _mockedConstructionFigureChangeEvent = null;

        _mockedConstructionFilteredFigureEnumerator.close();
        _mockedConstructionFilteredFigureEnumerator = null;

        _mockedConstructionMergedFigureEnumerator.close();
        _mockedConstructionMergedFigureEnumerator = null;

        _mockedConstructionOffsetLocator.close();
        _mockedConstructionOffsetLocator = null;

        _service = null;
        _constructorArgs = null;
    }

    @Test
    void testCreateStorableInputForInputStream() {
        // given
        InputStream is = mock(InputStream.class);

        // when
        de.renew.draw.storables.ontology.StorableInput storableInput =
            _service.createStorableInput(is);

        // then
        assertAll(
            () -> assertThat(_mockedConstructionStorableInput)
                .returns(1, mc -> mc.constructed().size())
                .returns(storableInput, mc -> mc.constructed().get(0)),
            () -> assertThat(_constructorArgs).containsOnlyKeys(storableInput),
            () -> assertThat(_constructorArgs.get(storableInput)).containsExactly(is));
    }

    @Test
    void testCreateStorableInputForInputStreamAndBoolean() {
        // given
        InputStream is = mock(InputStream.class);
        boolean useUTF8 = false;

        // when
        de.renew.draw.storables.ontology.StorableInput storableInput =
            _service.createStorableInput(is, useUTF8);

        // then
        assertAll(
            () -> assertThat(_mockedConstructionStorableInput)
                .returns(1, mc -> mc.constructed().size())
                .returns(storableInput, mc -> mc.constructed().get(0)),
            () -> assertThat(_constructorArgs).containsOnlyKeys(storableInput),
            () -> assertThat(_constructorArgs.get(storableInput)).containsExactly(is, useUTF8));
    }

    @Test
    void testCreateStorableInputForURLAndBoolean() throws IOException {
        // given
        URL url = mock(URL.class);
        boolean useUTF8 = false;

        // when
        de.renew.draw.storables.ontology.StorableInput storableInput =
            _service.createStorableInput(url, useUTF8);

        // then
        assertAll(
            () -> assertThat(_mockedConstructionStorableInput)
                .returns(1, mc -> mc.constructed().size())
                .returns(storableInput, mc -> mc.constructed().get(0)),
            () -> assertThat(_constructorArgs).containsOnlyKeys(storableInput),
            () -> assertThat(_constructorArgs.get(storableInput)).containsExactly(url, useUTF8));
    }

    @Test
    void testCreateStorableInputForString() {
        // given
        String stringStream = "test";

        // when
        de.renew.draw.storables.ontology.StorableInput storableInput =
            _service.createStorableInput(stringStream);

        // then
        assertAll(
            () -> assertThat(_mockedConstructionStorableInput)
                .returns(1, mc -> mc.constructed().size())
                .returns(storableInput, mc -> mc.constructed().get(0)),
            () -> assertThat(_constructorArgs).containsOnlyKeys(storableInput),
            () -> assertThat(_constructorArgs.get(storableInput)).containsExactly(stringStream));
    }

    @Test
    void testCreateStorableOutputForOutputStream() {
        // given
        OutputStream os = mock(OutputStream.class);

        // when
        de.renew.draw.storables.ontology.StorableOutput storableOutput =
            _service.createStorableOutput(os);

        // then
        assertAll(
            () -> assertThat(_mockedConstructionStorableOutput)
                .returns(1, mc -> mc.constructed().size())
                .returns(storableOutput, mc -> mc.constructed().get(0)),
            () -> assertThat(_constructorArgs).containsOnlyKeys(storableOutput),
            () -> assertThat(_constructorArgs.get(storableOutput)).containsExactly(os));
    }

    @Test
    void testCreateStorableOutputForFile() throws FileNotFoundException {
        // given
        File file = new File("Dummy.txt");

        // when
        de.renew.draw.storables.ontology.StorableOutput storableOutput =
            _service.createStorableOutput(file);

        // then
        assertAll(
            () -> assertThat(_mockedConstructionStorableOutput)
                .returns(1, mc -> mc.constructed().size())
                .returns(storableOutput, mc -> mc.constructed().get(0)),
            () -> assertThat(_constructorArgs).containsOnlyKeys(storableOutput),
            () -> assertThat(_constructorArgs.get(storableOutput)).containsExactly(file));
    }

    @Test
    void testCreatePatchingStorableInputForInputStream() {
        // given
        InputStream is = mock(InputStream.class);

        // when
        de.renew.draw.storables.ontology.StorableInput storableInput =
            _service.createPatchingStorableInput(is);

        // then
        assertAll(
            () -> assertThat(_mockedConstructionPatchingStorableInput)
                .returns(1, mc -> mc.constructed().size())
                .returns(storableInput, mc -> mc.constructed().get(0)),
            () -> assertThat(_constructorArgs).containsOnlyKeys(storableInput),
            () -> assertThat(_constructorArgs.get(storableInput)).containsExactly(is, true));
    }

    @Test
    void testCreatePatchingStorableInputForURLAndBoolean() throws IOException {
        // given
        URL url = mock(URL.class);
        boolean useUTF8 = false;


        // when
        de.renew.draw.storables.ontology.StorableInput storableInput =
            _service.createPatchingStorableInput(url, useUTF8);

        // then
        assertAll(
            () -> assertThat(_mockedConstructionPatchingStorableInput)
                .returns(1, mc -> mc.constructed().size())
                .returns(storableInput, mc -> mc.constructed().get(0)),
            () -> assertThat(_constructorArgs).containsOnlyKeys(storableInput),
            () -> assertThat(_constructorArgs.get(storableInput)).containsExactly(url, useUTF8));
    }

    @Test
    void testCloneStorable() throws Exception {
        //given
        Storable dummyDrawing1 = mock(StandardDrawing.class);
        Storable dummyDrawing2 = mock(StandardDrawing.class);

        try (MockedStatic<StorableHelper> mockedStorableHelperStatic =
            Mockito.mockStatic(StorableHelper.class)) {

            mockedStorableHelperStatic.when(() -> StorableHelper.cloneStorable(dummyDrawing1))
                .thenReturn(dummyDrawing2);

            //when
            Storable clonedDrawing = _service.cloneStorable(dummyDrawing1);

            //then
            mockedStorableHelperStatic
                .verify(() -> StorableHelper.cloneStorable(Mockito.any(Storable.class)));
            assertThat(clonedDrawing).isSameAs(dummyDrawing2);
        }

    }

    @Test
    void testCreateDrawingChangeEvent() {
        // given
        Drawing drawing = mock(Drawing.class);
        Rectangle rect = new Rectangle(0, 0, 100, 100);

        // when
        DrawingChangeEvent drawingChangeEvent = _service.createDrawingChangeEvent(drawing, rect);

        // then
        assertAll(
            () -> assertThat(_mockedConstructionDrawingChangeEvent)
                .returns(1, mc -> mc.constructed().size())
                .returns(drawingChangeEvent, mc -> mc.constructed().get(0)),
            () -> assertThat(_constructorArgs).containsOnlyKeys(drawingChangeEvent),
            () -> assertThat(_constructorArgs.get(drawingChangeEvent))
                .containsExactly(drawing, rect));
    }

    @Test
    void testCreateFigureChangeEventWithFigure() {
        // given
        Figure figure = mock(Figure.class);

        // when
        FigureChangeEvent figureChangeEvent = _service.createFigureChangeEvent(figure);

        // then
        assertAll(
            () -> assertThat(_mockedConstructionFigureChangeEvent)
                .returns(1, mc -> mc.constructed().size())
                .returns(figureChangeEvent, mc -> mc.constructed().get(0)),
            () -> assertThat(_constructorArgs).containsOnlyKeys(figureChangeEvent),
            () -> assertThat(_constructorArgs.get(figureChangeEvent)).containsExactly(figure));
    }

    @Test
    void testCreateFigureChangeEventWithFigureAndRectangle() {
        // given
        Figure figure = mock(Figure.class);
        Rectangle rect = new Rectangle();

        // when
        FigureChangeEvent figureChangeEvent = _service.createFigureChangeEvent(figure, rect);

        // then
        assertAll(
            () -> assertThat(_mockedConstructionFigureChangeEvent)
                .returns(1, mc -> mc.constructed().size())
                .returns(figureChangeEvent, mc -> mc.constructed().get(0)),
            () -> assertThat(_constructorArgs).containsOnlyKeys(figureChangeEvent),
            () -> assertThat(_constructorArgs.get(figureChangeEvent))
                .containsExactly(figure, rect));
    }


    @Test
    void testCreateFilteredFigureEnumerator() {
        // given
        FigureEnumeration enumeration = mock(FigureEnumeration.class);
        Predicate<Figure> predicate = figure -> true;

        // when
        FigureEnumeration figureEnumeration =
            _service.createFilteredFigureEnumerator(enumeration, predicate);

        // then
        assertAll(
            () -> assertThat(_mockedConstructionFilteredFigureEnumerator.constructed()).hasSize(1),
            () -> assertThat(figureEnumeration)
                .isSameAs(_mockedConstructionFilteredFigureEnumerator.constructed().get(0)),
            () -> assertThat(_constructorArgs).containsOnlyKeys(figureEnumeration),
            // the predicate cannot be checked as it is mapped in the service.
            () -> assertThat(_constructorArgs.get(figureEnumeration))
                .contains(enumeration, Index.atIndex(0)));
    }

    @Test
    void testCreateMergedFigureEnumerator() {
        // given
        FigureEnumeration enumerationA = mock(FigureEnumeration.class);
        FigureEnumeration enumerationB = mock(FigureEnumeration.class);

        // when
        FigureEnumeration figureEnumeration =
            _service.createMergedFigureEnumerator(enumerationA, enumerationB);

        // then
        assertAll(
            () -> assertThat(_mockedConstructionMergedFigureEnumerator.constructed()).hasSize(1),
            () -> assertThat(figureEnumeration)
                .isSameAs(_mockedConstructionMergedFigureEnumerator.constructed().get(0)),
            () -> assertThat(_constructorArgs).containsOnlyKeys(figureEnumeration),
            () -> assertThat(_constructorArgs.get(figureEnumeration))
                .containsExactly(enumerationA, enumerationB));

    }

    @Test
    void testIsDrawingNullDrawingWithNotNullDrawing() {
        // given
        Drawing drawing = mock(Drawing.class);

        // when
        boolean isNullDrawing = _service.isDrawingNullDrawing(drawing);

        // then
        assertThat(isNullDrawing).isFalse();
    }

    @Test
    void testIsDrawingNullDrawingWithNullDrawing() {
        // given
        Drawing nullDrawing = mock(NullDrawing.class);

        // when
        boolean isNullDrawing = _service.isDrawingNullDrawing(nullDrawing);

        // then
        assertThat(isNullDrawing).isTrue();
    }

    @Test
    void testCreateElbowTextLocator() {
        // given/when
        Locator locator = _service.createElbowTextLocator();
        // then
        assertThat(locator).isNotNull();
        assertThat(locator).isInstanceOf(ElbowTextLocator.class);
    }

    @Test
    void testGetKnownDrawingTypesFileFilter() {
        // given
        try (MockedStatic<IOHelper> mockedStaticIOHelper = mockStatic(IOHelper.class)) {
            IOHelper mockedIOHelper = mock(IOHelper.class);
            mockedStaticIOHelper.when(IOHelper::getInstance).thenReturn(mockedIOHelper);
            MultiExtensionFileFilterImpl expectedFilter = mock(MultiExtensionFileFilterImpl.class);
            when(mockedIOHelper.getFileFilterKnownTypes()).thenReturn(expectedFilter);

            // when
            MultiExtensionFileFilter actualFilter = _service.getKnownDrawingTypesFileFilter();

            // then
            assertThat(actualFilter).isEqualTo(expectedFilter);
            mockedStaticIOHelper.verify(IOHelper::getInstance);
            verify(mockedIOHelper).getFileFilterKnownTypes();
        }
    }

    @Test
    void testGetExtensionFileFilterByDrawingType() {
        // given
        try (MockedStatic<DrawingTypeManager> mockedStaticManager =
            mockStatic(DrawingTypeManager.class)) {
            DrawingTypeManager mockedManager = mock(DrawingTypeManager.class);
            mockedStaticManager.when(DrawingTypeManager::getInstance).thenReturn(mockedManager);
            ExtensionFileFilter expectedFilter = mock(ExtensionFileFilter.class);
            when(mockedManager.getFilter(any())).thenReturn(expectedFilter);

            // when
            ExtensionFileFilter actualFilter =
                _service.getExtensionFileFilterByDrawingType(DUMMY_DRAWING_TYPE);

            // then
            assertThat(actualFilter).isEqualTo(expectedFilter);
            mockedStaticManager.verify(DrawingTypeManager::getInstance);
            verify(mockedManager).getFilter(DUMMY_DRAWING_TYPE);
        }
    }

    @Test
    void testRegisterDrawingType() {
        // given
        try (MockedStatic<DrawingTypeManager> mockedStaticManager =
            mockStatic(DrawingTypeManager.class)) {
            DrawingTypeManager mockedManager = mock(DrawingTypeManager.class);
            mockedStaticManager.when(DrawingTypeManager::getInstance).thenReturn(mockedManager);
            ExtensionFileFilter filter = mock(ExtensionFileFilter.class);

            // when
            _service.registerDrawingType(DUMMY_DRAWING_TYPE, filter);

            // then
            mockedStaticManager.verify(DrawingTypeManager::getInstance);
            verify(mockedManager).register(DUMMY_DRAWING_TYPE, filter);
        }
    }

    @Test
    void testUnregisterDrawingType() {
        // given
        try (MockedStatic<DrawingTypeManager> mockedStaticManager =
            mockStatic(DrawingTypeManager.class)) {
            DrawingTypeManager mockedManager = mock(DrawingTypeManager.class);
            mockedStaticManager.when(DrawingTypeManager::getInstance).thenReturn(mockedManager);

            // when
            _service.unregisterDrawingType(DUMMY_DRAWING_TYPE);

            // then
            mockedStaticManager.verify(DrawingTypeManager::getInstance);
            verify(mockedManager).unregister(DUMMY_DRAWING_TYPE);
        }
    }

    @Test
    void testCreateOffsetLocatorLocator() {
        //given
        Locator locator = mock(Locator.class);
        //when
        Locator testOffsetLocator = _service.createOffsetLocator(locator);
        //then
        assertAll(
            () -> assertThat(_mockedConstructionOffsetLocator.constructed()).hasSize(1),
            () -> assertThat(testOffsetLocator)
                .isEqualTo(_mockedConstructionOffsetLocator.constructed().get(0)),
            () -> assertThat(_constructorArgs.get(testOffsetLocator)).containsExactly(locator));
    }

    @Test
    void testCreateDrawingForDrawingType() {
        // given
        try (MockedStatic<DrawingTypeManager> mockedStaticManager =
            mockStatic(DrawingTypeManager.class)) {
            mockedStaticManager.when(() -> DrawingTypeManager.getDrawingFromName(any()))
                .thenReturn(_mockedDrawing);

            // when
            Drawing actualDrawing = _service.createDrawingForDrawingType(DUMMY_DRAWING_TYPE);

            // then
            assertThat(actualDrawing).isSameAs(_mockedDrawing);
            mockedStaticManager
                .verify(() -> DrawingTypeManager.getDrawingFromName(DUMMY_DRAWING_TYPE));
        }
    }

    @Test
    void testAddExportFormat() {
        // given
        try (MockedStatic<DrawingExportFormatHolder> mockedStaticHolder =
            mockStatic(DrawingExportFormatHolder.class)) {
            DrawingExportFormatHolder mockedHolder = mock(DrawingExportFormatHolder.class);
            mockedStaticHolder.when(DrawingExportFormatHolder::getInstance)
                .thenReturn(mockedHolder);

            // when
            _service.addExportFormat(DUMMY_EXPORT_FORMAT);

            // then
            mockedStaticHolder.verify(DrawingExportFormatHolder::getInstance);
            verify(mockedHolder).addExportFormat(DUMMY_EXPORT_FORMAT);
        }
    }

    @Test
    void testRemoveExportFormat() {
        // given
        try (MockedStatic<DrawingExportFormatHolder> mockedStaticHolder =
            mockStatic(DrawingExportFormatHolder.class)) {
            DrawingExportFormatHolder mockedHolder = mock(DrawingExportFormatHolder.class);
            mockedStaticHolder.when(DrawingExportFormatHolder::getInstance)
                .thenReturn(mockedHolder);

            // when
            _service.removeExportFormat(DUMMY_EXPORT_FORMAT);

            // then
            mockedStaticHolder.verify(DrawingExportFormatHolder::getInstance);
            verify(mockedHolder).removeExportFormat(DUMMY_EXPORT_FORMAT);
        }
    }

    @Test
    void testGetExportFormats() {
        // given
        try (MockedStatic<DrawingExportFormatHolder> mockedStaticHolder =
            mockStatic(DrawingExportFormatHolder.class)) {
            DrawingExportFormatHolder mockedHolder = mock(DrawingExportFormatHolder.class);
            mockedStaticHolder.when(DrawingExportFormatHolder::getInstance)
                .thenReturn(mockedHolder);
            List<ExportFormat<Drawing>> expected = List.of(DUMMY_EXPORT_FORMAT);
            when(mockedHolder.getExportFormats()).thenReturn(expected);

            // when
            List<ExportFormat<Drawing>> actual = _service.getExportFormats();

            // then
            assertThat(actual).isEqualTo(expected);
            mockedStaticHolder.verify(DrawingExportFormatHolder::getInstance);
            verify(mockedHolder).getExportFormats();
        }
    }

    @Test
    void testAddImportFormat() {
        // given
        try (MockedStatic<DrawingImportFormatHolder> mockedStaticHolder =
            mockStatic(DrawingImportFormatHolder.class)) {
            DrawingImportFormatHolder mockedHolder = mock(DrawingImportFormatHolder.class);
            mockedStaticHolder.when(DrawingImportFormatHolder::getInstance)
                .thenReturn(mockedHolder);

            // when
            _service.addImportFormat(DUMMY_IMPORT_FORMAT);

            // then
            mockedStaticHolder.verify(DrawingImportFormatHolder::getInstance);
            verify(mockedHolder).addImportFormat(DUMMY_IMPORT_FORMAT);
        }
    }

    @Test
    void testRemoveImportFormat() {
        // given
        try (MockedStatic<DrawingImportFormatHolder> mockedStaticHolder =
            mockStatic(DrawingImportFormatHolder.class)) {
            DrawingImportFormatHolder mockedHolder = mock(DrawingImportFormatHolder.class);
            mockedStaticHolder.when(DrawingImportFormatHolder::getInstance)
                .thenReturn(mockedHolder);

            // when
            _service.removeImportFormat(DUMMY_IMPORT_FORMAT);

            // then
            mockedStaticHolder.verify(DrawingImportFormatHolder::getInstance);
            verify(mockedHolder).removeImportFormat(DUMMY_IMPORT_FORMAT);
        }
    }

    @Test
    void testGetImportFormats() {
        // given
        try (MockedStatic<DrawingImportFormatHolder> mockedStaticHolder =
            mockStatic(DrawingImportFormatHolder.class)) {
            DrawingImportFormatHolder mockedHolder = mock(DrawingImportFormatHolder.class);
            mockedStaticHolder.when(DrawingImportFormatHolder::getInstance)
                .thenReturn(mockedHolder);
            List<ImportFormat<Drawing>> expected = List.of(DUMMY_IMPORT_FORMAT);
            when(mockedHolder.getImportFormats()).thenReturn(expected);

            // when
            List<ImportFormat<Drawing>> actual = _service.getImportFormats();

            // then
            assertThat(actual).isEqualTo(expected);
            mockedStaticHolder.verify(DrawingImportFormatHolder::getInstance);
            verify(mockedHolder).getImportFormats();
        }
    }

    /**
     * Creates a mocked construction for the given class and saves the constructor arguments of said mock in {@code _constructorArgs} when its constructed.
     * The arguments can then be accessed by using {@code _constructorArgs.get(mock)}.
     * @param clazz The class for which the construction is to be mocked.
     * @return the created mocked construction object for the class
     * @param <T> the type of the class
     */
    private <T> MockedConstruction<T> createMockConstruction(Class<T> clazz) {
        return mockConstruction(
            clazz, (mock, context) -> _constructorArgs
                .put(mock, Arrays.asList(context.arguments().toArray())));
    }

    @ParameterizedTest
    @MethodSource("provideLocatorOffsets")
    void testCreateOffsetLocatorLocatorWithOffset(int offsetX, int offsetY) {
        // given
        Locator locator = mock(Locator.class);
        // when
        Locator testOffsetLocator = _service.createOffsetLocator(locator, offsetX, offsetY);
        // then
        assertAll(
            () -> assertThat(_mockedConstructionOffsetLocator.constructed()).hasSize(1),
            () -> assertThat(testOffsetLocator)
                .isEqualTo(_mockedConstructionOffsetLocator.constructed().get(0)),
            () -> assertThat(_constructorArgs.get(testOffsetLocator))
                .containsExactly(locator, offsetX, offsetY));
    }

    private static Stream<Arguments> provideLocatorOffsets() {
        return Stream.of(Arguments.of(1, 1), Arguments.of(1, -2), Arguments.of(-300, -20));
    }
}