package de.accso.flexinale.backoffice.api_contract.event;

import com.fasterxml.jackson.core.JsonProcessingException;
import de.accso.flexinale.common.api.event.Event;
import de.accso.flexinale.common.infrastructure.eventbus.EventSerializationHelper;
import de.accso.flexinale.common.shared_kernel.EventClassHelper;
import de.accso.flexinale.common.shared_kernel.Identifiable;
import de.accso.flexinale.common.shared_kernel.Versionable;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.time.LocalDateTime;
import java.util.stream.Stream;

import static de.accso.flexinale.backoffice.api_contract.event.ReflectionHelper.setField;
import static org.assertj.core.api.Assertions.*;

class EventStructureTest {
    private static Stream<Arguments> allEventTypes() {
        return AllBackofficeEvents.eventTypes.stream().map(Arguments::of);
    }

    // check for all Event subclasses if no exception is thrown for (de)serialization and reflection access
    // a) default constructor is needed
    // b) field 'version' is needed

    @ParameterizedTest
    @MethodSource("allEventTypes")
    <E extends Event> void testCheckEventStructure(final Class<E> eventType)
            throws InstantiationException, IllegalAccessException, JsonProcessingException, NoSuchMethodException, InvocationTargetException {
        // arrange
        Constructor<E> constructor = eventType.getDeclaredConstructor();
        constructor.setAccessible(true);
        Event event = constructor.newInstance();

        // set id, timestamp and version explicitely, so that (de)serialization is somewhat more realistic
        setField(eventType, event, "id",        Identifiable.Id.of(), false);
        setField(eventType, event, "timestamp", LocalDateTime.now(), false);

        // act - check serialization and deserialization
        String jsonString = EventSerializationHelper.serializeEvent2JsonString(event);
        EventSerializationHelper.deserializeJsonString2Event(jsonString, eventType);

        // act - check instantiation and version attribute
        assertThatCode(() -> {
            Versionable.Version eventClazzVersion = EventClassHelper.getEventClazzVersion(eventType);
            assertThat(eventClazzVersion).isEqualTo(event.version());
        })
        .doesNotThrowAnyException();
    }
}