import { Logger } from '../config/logger.js';
function getOrCreateSet(store, event) {
    const existing = store.get(event);
    if (existing !== undefined)
        return existing;
    const created = new Set();
    store.set(event, created);
    return created;
}
function safeFireAndForget(listener, payload, event) {
    const result = listener(payload);
    if (result instanceof Promise) {
        void result.catch((error) => {
            Logger.error('Unhandled async event listener error', { event, error });
        });
    }
}
export const EventDispatcher = Object.freeze({
    create() {
        const listeners = new Map();
        const dispatcher = {
            on(event, listener) {
                const set = getOrCreateSet(listeners, event);
                set.add(listener);
                // Capture dispatcher reference in closure instead of using 'this'
                return () => {
                    dispatcher.off(event, listener);
                };
            },
            once(event, listener) {
                const wrapped = async (payload) => {
                    dispatcher.off(event, wrapped);
                    return listener(payload);
                };
                const set = getOrCreateSet(listeners, event);
                set.add(wrapped);
                return () => {
                    dispatcher.off(event, wrapped);
                };
            },
            off(event, listener) {
                const set = listeners.get(event);
                if (set === undefined)
                    return;
                set.delete(listener);
                if (set.size === 0) {
                    listeners.delete(event);
                }
            },
            emit(event, payload) {
                const set = listeners.get(event);
                if (set === undefined)
                    return;
                // Snapshot to avoid mutation during iteration affecting dispatch.
                const snapshot = Array.from(set);
                for (const listener of snapshot) {
                    safeFireAndForget(listener, payload, event);
                }
            },
            async emitAsync(event, payload) {
                const set = listeners.get(event);
                if (set === undefined)
                    return;
                const snapshot = Array.from(set);
                const results = await Promise.allSettled(snapshot.map(async (listener) => {
                    await listener(payload);
                }));
                const errors = results
                    .filter((r) => r.status === 'rejected')
                    .map((r) => r.reason);
                if (errors.length === 1) {
                    throw errors[0];
                }
                if (errors.length > 1) {
                    throw new AggregateError(errors, `Multiple event listeners failed for "${event}"`);
                }
            },
            listenerCount(event) {
                return listeners.get(event)?.size ?? 0;
            },
            clear(event) {
                if (event !== undefined) {
                    listeners.delete(event);
                    return;
                }
                listeners.clear();
            },
        };
        return dispatcher;
    },
});
export default EventDispatcher;
