/**
 * Inter-Agent Announcements Module
 *
 * Provides a structured communication mechanism between agents
 * using a queue-based announcement system.
 */
import { EventEmitter } from 'events';
/**
 * Default configuration
 */
export const DEFAULT_ANNOUNCEMENT_CONFIG = {
    maxPerTarget: 100,
    ttlMs: 5 * 60 * 1000, // 5 minutes
    cleanupIntervalMs: 60 * 1000, // 1 minute
};
// ============================================================================
// Announcement Queue
// ============================================================================
/**
 * Announcement queue for inter-agent communication
 */
export class AnnouncementQueue extends EventEmitter {
    config;
    queues = new Map();
    idCounter = 0;
    cleanupTimer = null;
    constructor(config = {}) {
        super();
        this.config = { ...DEFAULT_ANNOUNCEMENT_CONFIG, ...config };
        if (this.config.cleanupIntervalMs > 0) {
            this.startCleanup();
        }
    }
    /**
     * Create and enqueue an announcement
     */
    announce(source, target, type, payload, options = {}) {
        const announcement = {
            id: `ann-${++this.idCounter}-${Date.now()}`,
            source,
            target,
            type,
            priority: options.priority ?? 'normal',
            payload,
            createdAt: Date.now(),
            consumed: false,
            correlationId: options.correlationId,
            metadata: options.metadata,
        };
        // Get or create queue for target
        let queue = this.queues.get(target);
        if (!queue) {
            queue = [];
            this.queues.set(target, queue);
        }
        // Check queue size limit
        if (this.config.maxPerTarget > 0 && queue.length >= this.config.maxPerTarget) {
            // Remove oldest non-urgent announcement
            const removeIndex = queue.findIndex(a => a.priority !== 'urgent' && !a.consumed);
            if (removeIndex !== -1) {
                const removed = queue.splice(removeIndex, 1)[0];
                this.emit('expire', removed);
            }
        }
        // Insert based on priority
        const insertIndex = this.findInsertIndex(queue, announcement.priority);
        queue.splice(insertIndex, 0, announcement);
        this.emit('announce', announcement);
        this.notifyTarget(target);
        return announcement;
    }
    /**
     * Find insertion index based on priority
     */
    findInsertIndex(queue, priority) {
        const priorityOrder = {
            urgent: 0,
            high: 1,
            normal: 2,
            low: 3,
        };
        const targetPriority = priorityOrder[priority];
        for (let i = 0; i < queue.length; i++) {
            if (priorityOrder[queue[i].priority] > targetPriority) {
                return i;
            }
        }
        return queue.length;
    }
    /**
     * Notify a target agent of pending announcements
     */
    notifyTarget(target) {
        // This would typically integrate with the agent system
        // For now, we just emit an event that can be listened to
        this.emit(`notify:${target}`);
    }
    /**
     * Consume announcements for a target
     */
    consume(target, filter) {
        const queue = this.queues.get(target);
        if (!queue || queue.length === 0) {
            return [];
        }
        const consumed = [];
        const remaining = [];
        for (const announcement of queue) {
            if (announcement.consumed && !filter?.includeConsumed) {
                remaining.push(announcement);
                continue;
            }
            if (this.matchesFilter(announcement, filter)) {
                announcement.consumed = true;
                announcement.consumedAt = Date.now();
                consumed.push(announcement);
                this.emit('consume', announcement);
            }
            else {
                remaining.push(announcement);
            }
        }
        this.queues.set(target, remaining);
        return consumed;
    }
    /**
     * Peek at announcements without consuming
     */
    peek(target, filter) {
        const queue = this.queues.get(target);
        if (!queue) {
            return [];
        }
        return queue.filter(a => this.matchesFilter(a, filter));
    }
    /**
     * Check if announcement matches filter
     */
    matchesFilter(announcement, filter) {
        if (!filter)
            return true;
        if (filter.source && announcement.source !== filter.source) {
            return false;
        }
        if (filter.type) {
            const types = Array.isArray(filter.type) ? filter.type : [filter.type];
            if (!types.includes(announcement.type)) {
                return false;
            }
        }
        if (filter.priority) {
            const priorities = Array.isArray(filter.priority) ? filter.priority : [filter.priority];
            if (!priorities.includes(announcement.priority)) {
                return false;
            }
        }
        if (filter.correlationId && announcement.correlationId !== filter.correlationId) {
            return false;
        }
        if (!filter.includeConsumed && announcement.consumed) {
            return false;
        }
        return true;
    }
    /**
     * Get pending count for a target
     */
    getPendingCount(target) {
        const queue = this.queues.get(target);
        if (!queue)
            return 0;
        return queue.filter(a => !a.consumed).length;
    }
    /**
     * Check if target has pending announcements
     */
    hasPending(target, filter) {
        const queue = this.queues.get(target);
        if (!queue)
            return false;
        return queue.some(a => !a.consumed && this.matchesFilter(a, filter));
    }
    /**
     * Wait for an announcement (with timeout)
     */
    async waitFor(target, filter, timeoutMs = 30000) {
        // Check if already available
        const existing = this.peek(target, filter);
        if (existing.length > 0) {
            return this.consume(target, filter)[0] || null;
        }
        // Wait for new announcement
        return new Promise((resolve) => {
            const timeout = setTimeout(() => {
                this.off(`notify:${target}`, handler);
                resolve(null);
            }, timeoutMs);
            const handler = () => {
                const announcements = this.consume(target, filter);
                if (announcements.length > 0) {
                    clearTimeout(timeout);
                    this.off(`notify:${target}`, handler);
                    resolve(announcements[0]);
                }
            };
            this.on(`notify:${target}`, handler);
        });
    }
    /**
     * Start cleanup timer
     */
    startCleanup() {
        this.cleanupTimer = setInterval(() => {
            this.cleanup();
        }, this.config.cleanupIntervalMs);
    }
    /**
     * Cleanup expired announcements
     */
    cleanup() {
        if (this.config.ttlMs === 0)
            return;
        const now = Date.now();
        for (const [target, queue] of this.queues) {
            const remaining = queue.filter(a => {
                const age = now - a.createdAt;
                if (age > this.config.ttlMs) {
                    this.emit('expire', a);
                    return false;
                }
                return true;
            });
            if (remaining.length === 0) {
                this.queues.delete(target);
            }
            else {
                this.queues.set(target, remaining);
            }
        }
    }
    /**
     * Stop the queue
     */
    stop() {
        if (this.cleanupTimer) {
            clearInterval(this.cleanupTimer);
            this.cleanupTimer = null;
        }
    }
    /**
     * Clear all announcements
     */
    clear() {
        this.queues.clear();
    }
    /**
     * Get statistics
     */
    getStats() {
        let totalPending = 0;
        let totalConsumed = 0;
        const byTarget = {};
        for (const [target, queue] of this.queues) {
            const pending = queue.filter(a => !a.consumed).length;
            const consumed = queue.filter(a => a.consumed).length;
            totalPending += pending;
            totalConsumed += consumed;
            byTarget[target] = { pending, consumed };
        }
        return {
            totalTargets: this.queues.size,
            totalPending,
            totalConsumed,
            byTarget,
        };
    }
}
// ============================================================================
// Singleton
// ============================================================================
let announcementQueueInstance = null;
/**
 * Get the singleton announcement queue
 */
export function getAnnouncementQueue(config) {
    if (!announcementQueueInstance) {
        announcementQueueInstance = new AnnouncementQueue(config);
    }
    return announcementQueueInstance;
}
/**
 * Reset the singleton announcement queue
 */
export function resetAnnouncementQueue() {
    if (announcementQueueInstance) {
        announcementQueueInstance.stop();
        announcementQueueInstance.clear();
        announcementQueueInstance = null;
    }
}
// ============================================================================
// Convenience Functions
// ============================================================================
/**
 * Send a result announcement
 */
export function announceResult(source, target, result, correlationId) {
    return getAnnouncementQueue().announce(source, target, 'result', result, { correlationId });
}
/**
 * Send an error announcement
 */
export function announceError(source, target, error, correlationId) {
    const payload = {
        message: error instanceof Error ? error.message : error,
        stack: error instanceof Error ? error.stack : undefined,
    };
    return getAnnouncementQueue().announce(source, target, 'error', payload, {
        correlationId,
        priority: 'high',
    });
}
/**
 * Send a progress announcement
 */
export function announceProgress(source, target, progress, correlationId) {
    return getAnnouncementQueue().announce(source, target, 'progress', progress, {
        correlationId,
        priority: 'low',
    });
}
/**
 * Send a request announcement
 */
export function announceRequest(source, target, request, correlationId) {
    return getAnnouncementQueue().announce(source, target, 'request', request, {
        correlationId,
        priority: 'normal',
    });
}
/**
 * Wait for a response to a request
 */
export async function requestAndWait(source, target, request, timeoutMs = 30000) {
    const correlationId = `req-${Date.now()}-${Math.random().toString(36).slice(2)}`;
    // Send the request
    announceRequest(source, target, request, correlationId);
    // Wait for the response
    const response = await getAnnouncementQueue().waitFor(source, { source: target, type: 'result', correlationId }, timeoutMs);
    return response?.payload ?? null;
}
//# sourceMappingURL=announcements.js.map