package de.renew.net;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.MockitoAnnotations;

import de.renew.engine.thread.SimulationThreadPool;
import de.renew.plugin.ServiceLookupInfrastructure;
import de.renew.simulator.api.ISimulationManager;
import de.renew.simulatorontology.simulation.Simulator;
import de.renew.simulatorontology.simulation.StepIdentifier;

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

public class NetInstantiatorTest {
    private static final String TEST_NET_NAME = "TestNet";
    private static final NetElementID PLACE_ID = new NetElementID(123, 123);
    private static final NetElementID TRANSITION_ID = new NetElementID(234, 234);

    @Mock
    private Simulator _mockSimulator;

    @Mock
    private StepIdentifier _mockStepIdentifier;

    @Mock
    private Place _mockPlace;

    @Mock
    private Transition _mockTransition;

    private Net _net;

    private NetInstantiator _instantiator;

    private MockedStatic<SimulationThreadPool> _simulationThreadPoolMockedStatic;
    private MockedStatic<ServiceLookupInfrastructure> _sliStaticMock;

    private AutoCloseable _closeable;

    @BeforeEach
    public void setUp() throws Exception {
        _closeable = MockitoAnnotations.openMocks(this);

        ISimulationManager simulationManager = mock(ISimulationManager.class);
        ServiceLookupInfrastructure sliMock = mock(ServiceLookupInfrastructure.class);
        when(sliMock.getFirstServiceProvider(ISimulationManager.class))
            .thenReturn(simulationManager);

        _sliStaticMock = mockStatic(ServiceLookupInfrastructure.class);
        _sliStaticMock.when(ServiceLookupInfrastructure::getInstance).thenReturn(sliMock);

        when(_mockSimulator.currentStepIdentifier()).thenReturn(_mockStepIdentifier);
        when(simulationManager.getCurrentSimulator()).thenReturn(_mockSimulator);

        when(_mockPlace.getID()).thenReturn(PLACE_ID);
        when(_mockTransition.getID()).thenReturn(TRANSITION_ID);

        _simulationThreadPoolMockedStatic = mockStatic(SimulationThreadPool.class);
        _simulationThreadPoolMockedStatic.when(SimulationThreadPool::isSimulationThread)
            .thenReturn(true);

        _net = new Net(TEST_NET_NAME);
        _instantiator = new NetInstantiator(_net);

    }

    @AfterEach
    public void tearDown() throws Exception {
        _closeable.close();
        _simulationThreadPoolMockedStatic.close();
        _sliStaticMock.close();
    }

    @Test
    public void testBuildInstanceWithStepIdentifier() {
        //when
        NetInstance instance = _net.getInstantiator().buildInstance(_mockStepIdentifier);

        //then
        assertThat(instance).isNotNull();
        assertThat(instance.getNet()).isEqualTo(_net);

    }

    @Test
    public void testBuildInstance() {
        //when
        NetInstance instance = _instantiator.buildInstance();

        //then
        assertThat(instance.getNet()).isEqualTo(_net);
        verify(_mockSimulator).currentStepIdentifier();

    }

    @Test
    public void testMakeInstance() throws Exception {
        //when
        NetInstance instance = _instantiator.makeInstance();

        //then
        assertThat(instance).isNotNull();
        assertThat(instance.getNet()).isEqualTo(_net);
    }

    @Test
    public void testMakeInstanceWithPlacesAndTransitions() throws Exception {
        //given
        PlaceInstance mockPlaceInstance = mock(PlaceInstance.class);
        when(_mockPlace.makeInstance(any(), any(Boolean.class))).thenReturn(mockPlaceInstance);

        //when
        _net.add(_mockPlace);
        _net.add(_mockTransition);
        NetInstance instance = _instantiator.makeInstance();

        //then
        assertThat(instance).isNotNull();
        assertThat(instance.getNet()).isEqualTo(_net);
        assertThat(instance.getNet().places()).containsExactly(_mockPlace);
        assertThat(instance.getNet().transitions()).containsExactly(_mockTransition);
    }

}
