package de.renew.net.inscription.transition;

import java.util.Arrays;
import java.util.Collection;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;

import de.renew.engine.searcher.Binder;
import de.renew.engine.searcher.Executable;
import de.renew.engine.searcher.Searcher;
import de.renew.engine.searcher.VariableMapperCopier;
import de.renew.engine.thread.SimulationThreadPool;
import de.renew.net.INetInstantiator;
import de.renew.net.Net;
import de.renew.net.NetInstance;
import de.renew.net.TransitionInstance;
import de.renew.unify.Copier;
import de.renew.unify.Impossible;
import de.renew.unify.Unify;
import de.renew.unify.Variable;

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

/**
 * Test class for {@link CreationOccurrence}.
 * Tests only the relevant (non-trivial) methods.
 */
@ExtendWith(MockitoExtension.class)
class CreationOccurrenceTest {
    private MockedStatic<SimulationThreadPool> _mockedPool;

    private Searcher _searcher;

    private VariableMapperCopier _copier;

    private Variable _variable;

    private CreationOccurrence _occurrence;

    @Mock
    private TransitionInstance _mockTransitionInstance;

    @Mock
    private Net _mockNet;

    @Mock
    private NetInstance _mockNetInstance;

    @Mock
    private INetInstantiator _mockINetInstantiator;

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

        when(_mockINetInstantiator.makeInstance()).thenReturn(_mockNetInstance);
        when(_mockNet.getInstantiator()).thenReturn(_mockINetInstantiator);
        _searcher = new Searcher();
        _copier = new VariableMapperCopier(new Copier());
        _variable = new Variable();
        _occurrence = new CreationOccurrence(_variable, _mockNet, _mockTransitionInstance);
    }

    /**
     * Setup method to close this test environment.
     */
    @AfterEach
    void tearDown() {
        _mockedPool.close();
    }

    /**
     * Test method for {@link CreationOccurrence#makeBinders(Searcher)}.
     *
     * @throws Impossible when the binders cannot be set up, implying
     *  that the occurrence cannot occur.
     */
    @Test
    public void testMakeBinders() throws Impossible {
        //given
        try (MockedStatic<Unify> mockedUnify = mockStatic(Unify.class)) {
            //when
            Collection<Binder> binders = _occurrence.makeBinders(_searcher);

            //then
            mockedUnify.verify(
                () -> Unify.unify(_variable, _mockNetInstance, _searcher.getStateRecorder()));
            assertThat(binders).isEmpty();
        }
    }

    /**
     * Test method for {@link CreationOccurrence#makeExecutables(VariableMapperCopier)}.
     */
    @Test
    public void testMakeExecutables() throws Impossible {
        //when
        _occurrence.makeBinders(_searcher);
        Collection<Executable> executables = _occurrence.makeExecutables(_copier);

        //then
        assertThat(executables).isNotEmpty();
        assertThat(executables).hasSize(2);
        assertThat(executables).usingRecursiveComparison().isEqualTo(
            Arrays
                .asList(new EarlyConfirmer(_mockNetInstance), new LateConfirmer(_mockNetInstance)));
    }
}