package de.renew.simulatorontology.shadow;

import java.util.stream.Stream;

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 static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.Mockito.mock;

/**
 * Test class for {@link ShadowArc}.
 * Tests only the relevant (non-trivial) methods.
 */
class ShadowArcTest {
    private static final String DEFAULT_ERROR_MESSAGE = "Must connect place and transition.";

    private ShadowNode _from;
    private ShadowNode _to;

    /**
     * Setup method to initialize this test environment.
     */
    @BeforeEach
    public void setUp() {
        ShadowNet shadowNetMock = mock(ShadowNet.class);

        _from = new ShadowTransition(shadowNetMock);
        _to = new ShadowPlace(shadowNetMock);
    }

    /**
     * Test method for the constructor of {@link ShadowArc}.
     * We test the constructor with a {@link ShadowTransition} as {@code from}
     * and a {@link ShadowPlace} as {@code to}.
     */
    @Test
    public void testShadowArcTransitionPlace() {
        // When
        ShadowArc shadowArc = new ShadowArc(_from, _to, ShadowArc.ORDINARY);

        // Then
        assertThat(shadowArc).isNotNull();
        assertThat(shadowArc.getTransition()).isEqualTo(_from);
        assertThat(shadowArc.getPlace()).isEqualTo(_to);
        assertThat(shadowArc.isPlaceToTransition()).isFalse();
    }

    /**
     * Test method for the constructor of {@link ShadowArc}.
     * We test the constructor with a {@link ShadowPlace} as {@code from}
     * and a {@link ShadowTransition} as {@code to}.
     */
    @Test
    public void testShadowArcPlaceTransition() {
        // When
        // To not declare new variables, we reuse the ones from the setup
        // but switch their order:
        ShadowArc shadowArc2 = new ShadowArc(_to, _from, ShadowArc.ORDINARY);

        // Then
        assertThat(shadowArc2).isNotNull();
        assertThat(shadowArc2.getPlace()).isEqualTo(_to);
        assertThat(shadowArc2.getTransition()).isEqualTo(_from);
        assertThat(shadowArc2.isPlaceToTransition()).isTrue();
    }

    /**
     * Test method for if the constructor throws the right exceptions.
     */
    @ParameterizedTest
    @MethodSource("provideInvalidNodePairs")
    public void testShadowArcThrowsExceptionWhenTwoNodesOfTheSameTypeGetConnected(
        ShadowNode from, ShadowNode to)
    {
        // When & Then
        assertThatExceptionOfType(RuntimeException.class)
            .isThrownBy(() -> new ShadowArc(from, to, 0)).withMessage(DEFAULT_ERROR_MESSAGE);
    }

    private static Stream<Arguments> provideInvalidNodePairs() {
        // Given
        ShadowNet shadowNetMock = mock(ShadowNet.class);
        ShadowTransition transition = new ShadowTransition(shadowNetMock);
        ShadowPlace place = new ShadowPlace(shadowNetMock);

        return Stream.of(Arguments.of(transition, transition), Arguments.of(place, place));
    }
}
