package de.renew.plugin.di;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * Test class that test the methods of the Container class.
 *
 * @author Philipp Schult
 */
public class ContainerTest {

    private static Container _testContainer;
    private static DummyClass _dummy;
    private static Class<?> _dummyClass;

    @BeforeEach
    public void setUp() {
        _testContainer = new Container();
        _dummy = new DummyClass();
        _dummyClass = DummyClass.class;
    }

    @Test
    public void testHasWithoutAddingDefinition() {
        //given
        Definition<?> definition = new SingletonDefinition<>(_dummyClass, _dummy);
        //when/then
        assertFalse(_testContainer.has(_dummyClass));
    }

    @Test
    public void testHasWithoutSettingContainer() {
        //given (in setup)
        //when/then
        assertFalse(_testContainer.has(_dummyClass));
    }

    @Test
    public void testAddDefinition() {
        //given
        Definition<?> definition = new SingletonDefinition<>(_dummyClass, _dummy);
        //when
        _testContainer.addDefinition(definition);
        //then
        assertTrue(_testContainer.has(_dummyClass));
    }

    @Test
    public void testSet() {
        //given (in setup)
        //when
        _testContainer.set(_dummyClass, _dummy);
        //then
        assertTrue(_testContainer.has(_dummyClass));
    }

    @Test
    public void testHasContainsSingularDefinition() {
        //given
        _testContainer.set(_dummyClass, _dummy);
        //when
        boolean result = _testContainer.has(_dummyClass);
        //then
        assertTrue(result);
    }

    @Test
    public void testHasContainsSameDefinitionMultipleTimes() {
        //given
        _testContainer.set(_dummyClass, _dummy);
        _testContainer.set(_dummyClass, _dummy);
        _testContainer.set(_dummyClass, new DummyClass());
        //when
        boolean result = _testContainer.has(_dummyClass);
        //then
        assertTrue(result);
    }

    @Test
    public void testHasContainsMultipleDefinitions() {
        //given
        _testContainer.set(_dummyClass, _dummy);
        _testContainer.set(AnotherClass.class, new AnotherClass());
        //when
        boolean firstResult = _testContainer.has(_dummyClass);
        boolean secondResult = _testContainer.has(_dummyClass);
        //then
        assertTrue(firstResult);
        assertTrue(secondResult);
    }

    @Test
    public void testHasDoesNotContainDefinition() {
        //given
        _testContainer.set(AnotherClass.class, new AnotherClass());
        //when
        boolean result = _testContainer.has(_dummyClass);
        //then
        assertFalse(result);
    }

    @Test
    public void testGetContainsDefinition() throws MissingDependencyException {
        //given
        _testContainer.set(_dummyClass, _dummy);
        //when
        Object result = _testContainer.get(_dummyClass);
        //then
        assertSame(_dummy, result);
    }

    @Test
    public void testGetDoesNotContainDefinitionThrowsMissingDependencyException() {
        //given
        _testContainer.set(AnotherClass.class, new AnotherClass());
        //when/then
        assertThrows(MissingDependencyException.class, () -> _testContainer.get(_dummyClass));
    }

    public static class DummyClass {
        //this is an empty class to make this test independent of other classes
    }

    public static class AnotherClass {
        //another empty class to test if Container can handle multiple classes
    }
}
