package de.accso.flexinale.common.shared_kernel;

import de.accso.flexinale.common.api.event.AbstractEvent;
import de.accso.flexinale.common.api.event.Event;
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 java.util.stream.Stream;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

public class EventClassHelperTest {
    public static final Versionable.Version VERSION = Versionable.Version.of(42);

    private static Stream<Arguments> allEventClassesToTest() {
        return Stream.of(
                  Arguments.of(MyEvent.class)
                , Arguments.of(MyEventWithHierarchyClass.class)
                , Arguments.of(MyEventExtendsAbstractClassButDoesNotImplementInterface.class)
                , Arguments.of(MyEventWithHierarchySuperClass.class)
        );
    }

    @ParameterizedTest
    @MethodSource("allEventClassesToTest")
    void testGetClassForEventClass(Class<?> eventClass) {
        // arrange
        String clazzName = eventClass.getCanonicalName();

        // act
        Class<?> classForClazzName = EventClassHelper.getClassFor(clazzName);

        // assert
        assertThat(classForClazzName).isEqualTo(eventClass);
    }

    @Test
    void testGetClassForNonEventClass() {
        // arrange
        String clazzName = String.class.getCanonicalName();

        // act
        Class<?> classForClazzName = EventClassHelper.getClassFor(clazzName);

        // assert
        assertThat(classForClazzName).isEqualTo(String.class);
    }

    @ParameterizedTest
    @MethodSource("allEventClassesToTest")
    void testGetClassNameForEventClass(Class<?> eventClass) {
        // arrange
        String clazzName = eventClass.getCanonicalName();

        // act
        String eventClazzName = EventClassHelper.getEventClazzName(clazzName);

        // assert
        assertThat(eventClazzName).isEqualTo(clazzName);
    }


    @Test
    void testGetClassNameForNonEventClassThrowsException() {
        // arrange
        String clazzName = Long.class.getCanonicalName();

        assertThatThrownBy(() -> {
            // act
            EventClassHelper.getEventClazzName(clazzName);

        // assert
        }).isInstanceOf(FlexinaleIllegalArgumentException.class)
          .hasMessageContaining("wrong clazz");
    }

    @ParameterizedTest
    @MethodSource("allEventClassesToTest")
    void testGetVersionForEventClass(Class<?> eventClass) {
        // arrange
        String clazzName = eventClass.getCanonicalName();

        // act
        Versionable.Version eventClazzVersion = EventClassHelper.getEventClazzVersion(clazzName);

        // assert
        assertThat(eventClazzVersion).isEqualTo(VERSION);
    }

    @Test
    void testGetVersionForNonEventClassThrowsException() {
        // arrange
        String clazzName = Integer.class.getCanonicalName();

        assertThatThrownBy(() -> {
            // act
            EventClassHelper.getEventClazzVersion(clazzName);

        // assert
        }).isInstanceOf(FlexinaleIllegalArgumentException.class)
          .hasMessageContaining("need to use Event class");
    }
}


class MyEvent extends AbstractEvent implements Event {
    static final Version version = EventClassHelperTest.VERSION;

    @Override
    public Version version() {
        return version;
    }
}

class MyEventExtendsAbstractClassButDoesNotImplementInterface extends AbstractEvent {
    static final Version version = EventClassHelperTest.VERSION;

    @Override
    public Version version() {
        return version;
    }
}

class MyEventWithHierarchySuperClass extends AbstractEvent implements Event {
    static final Version version = EventClassHelperTest.VERSION;

    @Override
    public Version version() {
        return version;
    }
}
class MyEventWithHierarchyClass extends MyEventWithHierarchySuperClass {
    static final Version version = EventClassHelperTest.VERSION;

    @Override
    public Version version() {
        return version;
    }
}