package de.renew.shadowcompiler;

import java.util.Collections;

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

import de.renew.engine.thread.SimulationThreadPool;
import de.renew.net.Net;
import de.renew.net.Place;
import de.renew.net.Transition;
import de.renew.simulatorontology.shadow.ShadowNet;
import de.renew.simulatorontology.shadow.ShadowPlace;
import de.renew.simulatorontology.shadow.ShadowTransition;

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

/**
 * Test class for {@link ShadowLookup}.
 * Tests only the relevant (non-trivial) methods.
 */
class ShadowLookupTest {
    private static final String NET_NAME = "testNet";
    private static final String PLACE_NAME = "testPlace";
    private static final String TRANSITION_NAME = "testTransition";

    private ShadowLookup _shadowLookup;
    private Net _net;
    private ShadowNet _shadowNetMock;

    private MockedStatic<SimulationThreadPool> _pool;

    /**
     * Setup method to initialize this test environment.
     */
    @BeforeEach
    public void setUp() {
        _pool = mockStatic(SimulationThreadPool.class);
        when(SimulationThreadPool.isSimulationThread()).thenReturn(true);

        _shadowLookup = new ShadowLookup();
        _net = new Net();

        _shadowNetMock = mock(ShadowNet.class);
        when(_shadowNetMock.getName()).thenReturn(PLACE_NAME);
    }

    /**
     * Teardown method to clean up after each test.
     */
    @AfterEach
    public void tearDown() {
        _pool.close();
    }

    /**
     * Test method for the constructor of {@link ShadowLookup}.
     */
    @Test
    public void testShadowLookup() {
        // Then
        assertThat(_shadowLookup).isNotNull();
        assertThat(Collections.list(_shadowLookup.allNetNames())).isNotNull();
        assertThat(Collections.list(_shadowLookup.allPlaces())).isNotNull();
        assertThat(Collections.list(_shadowLookup.allNetNames()).isEmpty()).isTrue();
        assertThat(Collections.list(_shadowLookup.allPlaces()).isEmpty()).isTrue();
    }

    /**
     * Test method for {@link ShadowLookup#setNet};
     */
    @Test
    public void testSetNet() {
        // When
        _shadowLookup.setNet(NET_NAME, _net);

        // Then
        assertThat(_shadowLookup.getNet(NET_NAME)).isEqualTo(_net);
    }

    /**
     * Test method for {@link ShadowLookup#set(ShadowPlace, Place)}
     * and {@link ShadowLookup#set(ShadowTransition, Transition)}.
     * Tests first implementation with ShadowPlace and Place objects.
     */
    @Test
    public void testSetPlaces() {
        // Given
        ShadowPlace shadowPlace = new ShadowPlace(_shadowNetMock);
        Place place = new Place(_net, PLACE_NAME, null);

        // When
        _shadowLookup.set(shadowPlace, place);

        // Then
        assertThat(_shadowLookup.get(shadowPlace)).isEqualTo(place);
    }

    /**
     * Test method for {@link ShadowLookup#set(ShadowPlace, Place)}
     * and {@link ShadowLookup#set(ShadowTransition, Transition)}.
     * Tests second implementation with ShadowTransition and Transition objects.
     */
    @Test
    public void testSetTransitions() {
        // Given
        ShadowTransition shadowTransition = new ShadowTransition(_shadowNetMock);
        Transition transition = new Transition(_net, TRANSITION_NAME, null);

        // When
        _shadowLookup.set(shadowTransition, transition);

        // Then
        assertThat(_shadowLookup.get(shadowTransition)).isEqualTo(transition);
    }
}