import { getConfig } from "../../config/index.js";
import { union } from "@virtualstate/union";
import { isAsyncIterable, isIterable, isPromise, ok } from "../../is.js";
import { v4 } from "uuid";
export const VIRTUAL_FUNCTIONS = new Set();
export function virtual(fn) {
    return createVirtualFunction(fn);
}
export function createVirtualFunction(fn) {
    VIRTUAL_FUNCTIONS.add(fn);
    return () => {
        VIRTUAL_FUNCTIONS.delete(fn);
    };
}
export async function* generateVirtualEvents() {
    // Ensure all virtual functions are loaded
    await import("../../virtual.js");
    const { events } = getConfig();
    const fnsSnapshot = [...VIRTUAL_FUNCTIONS];
    let sources = fnsSnapshot;
    if (events) {
        sources = [
            events,
            fnsSnapshot
        ];
    }
    const seen = new WeakSet();
    for await (const events of generate(sources)) {
        const yieldingEvents = events.filter(event => !seen.has(event));
        if (!yieldingEvents.length)
            continue;
        for (const event of yieldingEvents) {
            if (!event.durableEventId) {
                // Provide it or have it mutated...
                event.durableEventId = v4();
            }
            if (!event.virtual) {
                // Provide it or have it mutated...
                event.virtual = true;
            }
        }
        yield yieldingEvents;
        for (const event of yieldingEvents) {
            seen.add(event);
        }
    }
    function generate(events) {
        if (isVirtualEventFn(events)) {
            return functionSource(events);
        }
        if (Array.isArray(events)) {
            return arraySource(events);
        }
        return arraySource(Object.values(events));
    }
    function isVirtualEventFn(value) {
        return typeof value === "function";
    }
    async function* asyncIterableSource(source) {
        for await (const values of source) {
            if (isDurableEventDataIterable(values)) {
                yield [...values];
            }
            else {
                yield [values];
            }
        }
    }
    async function* functionSource(fn) {
        let result = fn();
        if (isPromise(result)) {
            result = await result;
        }
        if (!result) {
            return;
        }
        if (isDurableEventDataArray(result)) {
            return yield result;
        }
        if (isDurableEventDataIterable(result)) {
            return yield [...result];
        }
        if (isAsyncIterable(result)) {
            return yield* asyncIterableSource(result);
        }
        ok(!Array.isArray(result));
        yield [result];
    }
    function isDurableEventDataArray(value) {
        return Array.isArray(value);
    }
    function isDurableEventDataIterable(value) {
        return isIterable(value);
    }
    async function* arraySource(events) {
        for await (const allEvents of union([...new Set(events)].map(generate))) {
            const yieldingEvents = allEvents
                .filter(Boolean)
                .flatMap(events => events.filter(event => !seen.has(event)));
            if (yieldingEvents.length) {
                yield yieldingEvents;
            }
        }
    }
}
//# sourceMappingURL=virtual.js.map