import { uuidv7 } from 'uuidv7';
import { EventSchemaMap, } from './schemas.js';
const MAX_LISTENERS = 50;
const MAX_DEDUP_CACHE = 1000;
export class EventBus {
    listeners = new Map();
    totalListenerCount = 0;
    dedupCache = new Set();
    dedupOrder = [];
    lastSyncErrors = [];
    on(eventType, listener) {
        if (this.totalListenerCount >= MAX_LISTENERS) {
            throw new Error(`Maximum listener limit (${MAX_LISTENERS}) reached`);
        }
        const list = this.listeners.get(eventType) || [];
        list.push({ fn: listener });
        this.listeners.set(eventType, list);
        this.totalListenerCount++;
    }
    off(eventType, listener) {
        const list = this.listeners.get(eventType);
        if (!list)
            return;
        const idx = list.findIndex((e) => e.fn === listener);
        if (idx !== -1) {
            list.splice(idx, 1);
            this.totalListenerCount--;
        }
    }
    emit(eventType, payload) {
        const schema = EventSchemaMap[eventType];
        const validated = schema.parse(payload);
        const enriched = {
            ...validated,
            correlationId: validated.correlationId || uuidv7(),
            timestamp: validated.timestamp || new Date().toISOString(),
        };
        if (eventType !== 'error') {
            this.lastSyncErrors = [];
        }
        this.dispatch(eventType, enriched);
        return enriched;
    }
    emitIdempotent(eventId, eventType, payload) {
        if (this.dedupCache.has(eventId)) {
            return null;
        }
        const result = this.emit(eventType, payload);
        if (this.lastSyncErrors.length === 0) {
            this.trackDedup(eventId);
        }
        return result;
    }
    getListenerCount(eventType) {
        if (eventType) {
            return (this.listeners.get(eventType) || []).length;
        }
        return this.totalListenerCount;
    }
    getLastSyncErrors() {
        return this.lastSyncErrors;
    }
    removeAllListeners(eventType) {
        if (eventType) {
            const list = this.listeners.get(eventType);
            if (list) {
                this.totalListenerCount -= list.length;
                this.listeners.delete(eventType);
            }
        }
        else {
            this.listeners.clear();
            this.totalListenerCount = 0;
        }
    }
    dispatch(eventType, event) {
        const list = this.listeners.get(eventType) || [];
        for (let i = 0; i < list.length; i++) {
            try {
                const result = list[i].fn(event);
                if (result && typeof result.catch === 'function') {
                    result.catch((err) => {
                        this.handleListenerError(eventType, i, err);
                    });
                }
            }
            catch (err) {
                this.lastSyncErrors.push(err instanceof Error ? err : new Error(String(err)));
                this.handleListenerError(eventType, i, err);
            }
        }
    }
    handleListenerError(eventType, listenerIndex, err) {
        if (eventType === 'error') {
            process.stderr.write(`[EventBus] Error in error listener: ${err instanceof Error ? err.message : String(err)}\n`);
            return;
        }
        try {
            this.emit('error', {
                originalEventType: eventType,
                listenerIndex,
                error: err instanceof Error ? err.message : String(err),
                stack: err instanceof Error ? err.stack : undefined,
            });
        }
        catch {
            process.stderr.write(`[EventBus] Failed to emit error event: ${err instanceof Error ? err.message : String(err)}\n`);
        }
    }
    trackDedup(eventId) {
        this.dedupCache.add(eventId);
        this.dedupOrder.push(eventId);
        if (this.dedupOrder.length > MAX_DEDUP_CACHE) {
            const oldest = this.dedupOrder.shift();
            this.dedupCache.delete(oldest);
        }
    }
}
//# sourceMappingURL=EventBus.js.map