package de.accso.flexinale.common.api.eventbus;

import de.accso.flexinale.common.api.event.Event;
import de.accso.flexinale.common.api.event.EventTestdata.MyMixedEvent;
import de.accso.flexinale.common.api.event.EventTestdata.MyNumberEvent;
import de.accso.flexinale.common.api.event.EventTestdata.MyTextEvent;
import de.accso.flexinale.common.shared_kernel.FlexinaleIllegalStateException;

import static de.accso.flexinale.common.shared_kernel.Identifiable.Id.uuidString;

@SuppressWarnings("unused")
public final class PublisherAndSubscriberTestdata {
    public static class MyNumberEventSubscriber implements EventSubscriber<MyNumberEvent> {
        MyNumberEvent lastReceivedEvent = null;
        int counterOfRetrievedEvents = 0;

        final boolean checkForAscendingNumbersInEvents;

        MyNumberEventSubscriber() {
            this(false);
        }

        MyNumberEventSubscriber(boolean checkForAscendingNumbersInEvents) {
            this.checkForAscendingNumbersInEvents = checkForAscendingNumbersInEvents;
        }

        @Override
        public String getName() {
            return MyNumberEventSubscriber.class.getName();
        }

        @Override
        public String getGroupName() {
            return getName();
        }

        @Override
        public void receive(final MyNumberEvent event) {
            if (checkForAscendingNumbersInEvents && lastReceivedEvent != null) {
                if (   this.lastReceivedEvent.value1 >= event.value1
                    || this.lastReceivedEvent.value2 >= event.value2) {
                    throw new FlexinaleIllegalStateException(
                            ("no ascending value when receiving next MyNumberEvent " +
                             "(last received event = %s, next received event = %s")
                             .formatted(lastReceivedEvent, event));
                }
            }
            this.lastReceivedEvent = event;
            counterOfRetrievedEvents++;
        }
    }

    public static class MyEventSubscriberEachInstanceInItsOwnGroup extends MyNumberEventSubscriber {
        // not static, as otherwise all instances of MyDifferentEventSubscriber would be in the same group,
        // but we want them in different groups
        private final String RANDOM_SUFFIX = uuidString();

        MyEventSubscriberEachInstanceInItsOwnGroup() {
            this(false);
        }

        MyEventSubscriberEachInstanceInItsOwnGroup(boolean checkForAscendingNumbersInEvents) {
            super(checkForAscendingNumbersInEvents);
        }

        @Override
        public String getGroupName() {
            // don't use anything random here, as called in each subscribe() method (so group name would change)
            return getName() + RANDOM_SUFFIX;
        }
    }

    @SuppressWarnings("unused")
    public static final class MyTextEventSubscriber implements EventSubscriber<MyTextEvent> {
        MyTextEvent lastReceivedEvent = null;
        int counterOfRetrievedEvents = 0;

        @Override
        public String getName() {
            return MyTextEventSubscriber.class.getName();
        }

        @Override
        public String getGroupName() {
            return getName();
        }

        @Override
        public void receive(final MyTextEvent event) {
            this.lastReceivedEvent = event;
            counterOfRetrievedEvents++;
        }
    }

    // -----------------------------------------------------------------------------------------------------------

    @SuppressWarnings("unused")
    public static final class MyNumberEventThrowingRuntimeExceptionSubscriber
            implements EventSubscriber.EventSubscriber2<MyTextEvent, MyNumberEvent> {
        MyNumberEvent lastReceivedEvent = null;
        int counterOfRetrievedEvents = 0;

        @Override
        public String getName() {
            return MyNumberEventThrowingRuntimeExceptionSubscriber.class.getName();
        }

        @Override
        public String getGroupName() {
            return getName();
        }

        @Override
        public void receive(final MyTextEvent event) {
            // do nothing
        }

        @Override
        public void receive2(final MyNumberEvent event) {
            this.lastReceivedEvent = event;
            counterOfRetrievedEvents++;
            throw new MyNumberEventRuntimeException("RuntimeException for event with Id=" + event.id());
        }
    }

    public static class MyNumberEventRuntimeException extends RuntimeException {
        public MyNumberEventRuntimeException(String message) {
            super(message);
        }
    }

    // -----------------------------------------------------------------------------------------------------------

    @SuppressWarnings("unused")
    public static final class MyMixedEventSubscriber implements EventSubscriber<MyMixedEvent> {
        MyMixedEvent lastReceivedEvent = null;
        int counterOfRetrievedEvents = 0;

        @Override
        public String getName() {
            return MyMixedEventSubscriber.class.getName();
        }

        @Override
        public String getGroupName() {
            return getName();
        }

        @Override
        public void receive(final MyMixedEvent event) {
            this.lastReceivedEvent = event;
            counterOfRetrievedEvents++;
        }
    }

    @SuppressWarnings("unused")
    public static final class MyNumberAndMixedEventSubscriber implements
            EventSubscriber.EventSubscriber2<MyNumberEvent, MyMixedEvent> {
        MyNumberEvent lastReceivedNumberEvent = null;
        MyMixedEvent  lastReceivedMixedEvent  = null;
        int counterOfRetrievedEvents = 0;

        @Override
        public String getName() {
            return MyNumberAndMixedEventSubscriber.class.getName();
        }

        @Override
        public String getGroupName() {
            return getName();
        }

        @Override
        public void receive(final MyNumberEvent event) {
            this.lastReceivedNumberEvent = event;
            counterOfRetrievedEvents++;
        }

        @Override
        public void receive2(final MyMixedEvent event) {
            this.lastReceivedMixedEvent = event;
            counterOfRetrievedEvents++;
        }
    }

    @SuppressWarnings("unused")
    public static final class MyNumberAndTextAndMixedEventSubscriber implements
            EventSubscriber.EventSubscriber3<MyNumberEvent, MyTextEvent, MyMixedEvent> {
        MyNumberEvent lastReceivedNumberEvent = null;
        MyTextEvent   lastReceivedTextEvent   = null;
        MyMixedEvent  lastReceivedMixedEvent  = null;
        int counterOfRetrievedEvents = 0;

        @Override
        public String getName() {
            return MyNumberAndTextAndMixedEventSubscriber.class.getName();
        }

        @Override
        public String getGroupName() {
            return getName();
        }

        @Override
        public void receive(final MyNumberEvent event) {
            this.lastReceivedNumberEvent = event;
            counterOfRetrievedEvents++;
        }

        @Override
        public void receive2(final MyTextEvent event) {
            this.lastReceivedTextEvent = event;
            counterOfRetrievedEvents++;
        }

        @Override
        public void receive3(final MyMixedEvent event) {
            this.lastReceivedMixedEvent = event;
            counterOfRetrievedEvents++;
        }
    }

    @SuppressWarnings("unused")
    public static final class GenericEventSubscriber implements EventSubscriber<Event> {
        Event lastReceivedEvent = null;
        int counterOfRetrievedEvents = 0;

        @Override
        public String getName() {
            return GenericEventSubscriber.class.getName();
        }

        @Override
        public String getGroupName() {
            return getName();
        }

        @Override
        public void receive(final Event event) {
            this.lastReceivedEvent = event;
            counterOfRetrievedEvents++;
        }
    }

    // does not work, see expected exception in test
    @SuppressWarnings("unused")
    public static final class GenericEventSubscriberWithExtraSubscription
            implements EventSubscriber.EventSubscriber2<Event, MyTextEvent> { // Event has to be the first generic type otherwise does not compile
        Event lastReceivedEvent = null;
        int counterOfRetrievedEvents = 0;

        @Override
        public String getName() {
            return GenericEventSubscriberWithExtraSubscription.class.getName();
        }

        @Override
        public String getGroupName() {
            return getName();
        }

        @Override
        public void receive(final Event event) {
            this.lastReceivedEvent = event;
            counterOfRetrievedEvents++;
        }

        @Override
        public void receive2(final MyTextEvent event) {
            this.lastReceivedEvent = event;
            counterOfRetrievedEvents++;
        }
    }

    // -----------------------------------------------------------------------------------------------------------

    public static final class MyNumberEventPublisher implements EventPublisher<MyNumberEvent> {
        private final EventBus<MyNumberEvent> bus;

        public MyNumberEventPublisher(EventBus<MyNumberEvent> bus) {
            this.bus = bus;
        }

        @Override
        public String getName() {
            return MyNumberEventPublisher.class.getName();
        }

        @Override
        public void post(final Class<MyNumberEvent> eventType, final MyNumberEvent event) {
            bus.publish(MyNumberEvent.class, event);
        }
    }

    public static final class MyTextEventPublisher implements EventPublisher<MyTextEvent> {
        private final EventBus<MyTextEvent> bus;

        public MyTextEventPublisher(EventBus<MyTextEvent> bus) {
            this.bus = bus;
        }

        @Override
        public String getName() {
            return MyTextEventPublisher.class.getName();
        }

        @Override
        public void post(final Class<MyTextEvent> eventType, final MyTextEvent event) {
            bus.publish(MyTextEvent.class, event);
        }
    }

    public static final class MyMixedEventPublisher implements EventPublisher<MyMixedEvent> {
        private final EventBus<MyMixedEvent> bus;

        MyMixedEventPublisher(EventBus<MyMixedEvent> bus) {
            this.bus = bus;
        }

        @Override
        public String getName() {
            return MyMixedEventPublisher.class.getName();
        }

        @Override
        public void post(final Class<MyMixedEvent> eventType, final MyMixedEvent event) {
            bus.publish(MyMixedEvent.class, event);
        }
    }

    public static final class MyNumberAndMixedEventPublisher implements
            EventPublisher.EventPublisher2<MyNumberEvent, MyMixedEvent> {
        private final EventBus<MyNumberEvent> numberBus;
        private final EventBus<MyMixedEvent>  mixedBus;

        public MyNumberAndMixedEventPublisher(final EventBus<MyNumberEvent> numberBus,
                                              final EventBus<MyMixedEvent> mixedBus) {
            this.numberBus = numberBus;
            this.mixedBus = mixedBus;
        }

        @Override
        public String getName() {
            return MyNumberAndMixedEventPublisher.class.getName();
        }

        @Override
        public void post(final Class<MyNumberEvent> eventType, final MyNumberEvent event) {
            numberBus.publish(MyNumberEvent.class, event);
        }

        @Override
        public void post2(final Class<MyMixedEvent> eventType, final MyMixedEvent event) {
            mixedBus.publish(MyMixedEvent.class, event);
        }
    }

    // -----------------------------------------------------------------------------------------------------------

    @SuppressWarnings("unused")
    public static final class MyNumberEventSubscriberAndMyTextEventPublisher implements
            EventSubscriber<MyNumberEvent>,
            EventPublisher<MyTextEvent> {

        private final EventBus<MyTextEvent> textBus;

        public MyNumberEventSubscriberAndMyTextEventPublisher(final EventBus<MyTextEvent> textBus) {
            this.textBus = textBus;
        }

        MyNumberEvent lastReceivedEvent = null;
        MyTextEvent       lastSentEvent = null;

        @Override
        public String getName() {
            return MyNumberEventSubscriberAndMyTextEventPublisher.class.getName();
        }

        @Override
        public String getGroupName() {
            return getName();
        }

        @Override
        public void receive(final MyNumberEvent event) {
            this.lastReceivedEvent = event;

            // create and post new Event using the received event as the predecessor, implicitely using propagating its correlationId
            this.lastSentEvent = new MyTextEvent(event, ""+event.value1, ""+ event.value2);
            post(MyTextEvent.class, lastSentEvent);
        }

        @Override
        public void post(final Class<MyTextEvent> eventType, final MyTextEvent event) {
            textBus.publish(MyTextEvent.class, event);
        }
    }
}
