// Path: grooves-api/src/main/java/com/github/rahulsom/grooves/queries/internal/Utils.java
// @NotNull
// public static <EventT extends BaseEvent> String ids(
//         @NotNull List<EventT> events) {
//     return events.stream()
//             .map(i -> String.valueOf(i.getId()))
//             .collect(JOIN_EVENT_IDS);
// }
// 
// Path: grooves-api/src/main/java/com/github/rahulsom/grooves/queries/internal/Utils.java
// @NotNull
// public static <EventT extends BaseEvent> String stringify(
//         @NotNull List<EventT> events) {
//     return events.stream()
//             .map(EventT::toString)
//             .collect(JOIN_EVENTS);
// }


import com.github.rahulsom.grooves.api.EventApplyOutcome;
import com.github.rahulsom.grooves.api.GroovesException;
import com.github.rahulsom.grooves.api.events.BaseEvent;
import com.github.rahulsom.grooves.api.events.DeprecatedBy;
import com.github.rahulsom.grooves.api.events.Deprecates;
import com.github.rahulsom.grooves.api.events.RevertEvent;
import com.github.rahulsom.grooves.api.snapshots.internal.BaseSnapshot;
import io.reactivex.Flowable;
import org.jetbrains.annotations.NotNull;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import static com.github.rahulsom.grooves.api.EventApplyOutcome.RETURN;
import static com.github.rahulsom.grooves.queries.internal.Utils.ids;
import static com.github.rahulsom.grooves.queries.internal.Utils.stringify;
import static io.reactivex.Flowable.*;
                            (DeprecatedBy<AggregateT, EventIdT, EventT>) event;
                    return applyDeprecatedBy(deprecatedByEvent, snapshot);
                } else {
                    String methodName = "apply" + event.getClass().getSimpleName();
                    return callMethod((QueryT) query, methodName, snapshot, event)
                            .flatMap(retval -> handleMethodResponse(
                                    stopApplyingEvents, snapshot, methodName, retval));
                }
            }
        })).toFlowable().flatMap(it -> it);

    }

    /**
     * Decides how to proceed after inspecting the response of a method that returns an
     * EventApplyOutcome.
     *
     * @param stopApplyingEvents Whether a previous decision has been made to stop applying new
     *                           events
     * @param snapshot           The snapshot on which events are being added
     * @param methodName         The name of the method that was called
     * @param retval             The outcome of calling the method
     * @return The snapshot after deciding what to do with the EventApplyOutcome
     */
    private Flowable<? extends SnapshotT> handleMethodResponse(
            AtomicBoolean stopApplyingEvents, SnapshotT snapshot, String methodName,
            EventApplyOutcome retval) {
        switch (retval) {
            case RETURN:
                stopApplyingEvents.set(true);
// Path: grooves-api/src/main/java/com/github/rahulsom/grooves/queries/internal/Utils.java
// @NotNull
// public static <EventT extends BaseEvent> String ids(
//         @NotNull List<EventT> events) {
//     return events.stream()
//             .map(i -> String.valueOf(i.getId()))
//             .collect(JOIN_EVENT_IDS);
// }
// 
// Path: grooves-api/src/main/java/com/github/rahulsom/grooves/queries/internal/Utils.java
// @NotNull
// public static <EventT extends BaseEvent> String stringify(
//         @NotNull List<EventT> events) {
//     return events.stream()
//             .map(EventT::toString)
//             .collect(JOIN_EVENTS);
// }


// Path: grooves-api/src/main/java/com/github/rahulsom/grooves/queries/internal/QueryExecutor.java
import com.github.rahulsom.grooves.api.EventApplyOutcome;
import com.github.rahulsom.grooves.api.GroovesException;
import com.github.rahulsom.grooves.api.events.BaseEvent;
import com.github.rahulsom.grooves.api.events.DeprecatedBy;
import com.github.rahulsom.grooves.api.events.Deprecates;
import com.github.rahulsom.grooves.api.events.RevertEvent;
import com.github.rahulsom.grooves.api.snapshots.internal.BaseSnapshot;
import io.reactivex.Flowable;
import org.jetbrains.annotations.NotNull;
import org.reactivestreams.Publisher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import static com.github.rahulsom.grooves.api.EventApplyOutcome.RETURN;
import static com.github.rahulsom.grooves.queries.internal.Utils.ids;
import static com.github.rahulsom.grooves.queries.internal.Utils.stringify;
import static io.reactivex.Flowable.*;

                            (DeprecatedBy<AggregateT, EventIdT, EventT>) event;
                    return applyDeprecatedBy(deprecatedByEvent, snapshot);
                } else {
                    String methodName = "apply" + event.getClass().getSimpleName();
                    return callMethod((QueryT) query, methodName, snapshot, event)
                            .flatMap(retval -> handleMethodResponse(
                                    stopApplyingEvents, snapshot, methodName, retval));
                }
            }
        })).toFlowable().flatMap(it -> it);

    }

    /**
     * Decides how to proceed after inspecting the response of a method that returns an
     * EventApplyOutcome.
     *
     * @param stopApplyingEvents Whether a previous decision has been made to stop applying new
     *                           events
     * @param snapshot           The snapshot on which events are being added
     * @param methodName         The name of the method that was called
     * @param retval             The outcome of calling the method
     * @return The snapshot after deciding what to do with the EventApplyOutcome
     */
    private Flowable<? extends SnapshotT> handleMethodResponse(
            AtomicBoolean stopApplyingEvents, SnapshotT snapshot, String methodName,
            EventApplyOutcome retval) {
        switch (retval) {
            case RETURN:
                stopApplyingEvents.set(true);
                return just(snapshot);