/**
 * Message queue service for agent sessions.
 *
 * Each agent session maintains a FIFO queue of pending messages.
 * When Claude is busy processing a message, new messages are queued.
 * Once the current message finishes, the next one in the queue is processed.
 *
 * Queue limit: MAX_QUEUE_PER_PROJECT (50) per session.
 */
import { MAX_QUEUE_PER_PROJECT } from "@pocketcoder/shared/constants";
import { logger } from "../utils/logger.js";
const sessionQueues = new Map();
/**
 * Initialize a queue for a session with its process function.
 * Must be called before enqueueMessage for a given session.
 */
export function initQueue(agentSessionId, processFn) {
    if (!sessionQueues.has(agentSessionId)) {
        sessionQueues.set(agentSessionId, {
            messages: [],
            processing: false,
            processFn,
        });
    }
}
/**
 * Enqueue a message for a session.
 *
 * @returns "started" if the message will be processed immediately,
 *          "queued" if it was added to the queue behind a running process,
 *          "full" if the queue has reached the limit (reject with daemon_busy).
 */
export function enqueueMessage(msg) {
    const q = sessionQueues.get(msg.agentSessionId);
    if (!q) {
        // Auto-init shouldn't happen, but handle gracefully
        logger.warn(`Queue not initialized for session ${msg.agentSessionId}`);
        return "full";
    }
    if (q.messages.length >= MAX_QUEUE_PER_PROJECT) {
        logger.warn(`Queue full for session ${msg.agentSessionId} (${q.messages.length}/${MAX_QUEUE_PER_PROJECT})`);
        return "full";
    }
    q.messages.push(msg);
    if (!q.processing) {
        // Not currently busy — will start immediately on startDrain()
        return "started";
    }
    logger.info(`Message queued for session ${msg.agentSessionId} (${q.messages.length} pending)`);
    return "queued";
}
/**
 * Start draining the queue for a session.
 * Call this AFTER sending the response to the client to avoid race conditions
 * where events fire before the response is sent.
 */
export function startDrain(agentSessionId) {
    const q = sessionQueues.get(agentSessionId);
    if (q && !q.processing && q.messages.length > 0) {
        void drainQueue(agentSessionId);
    }
}
/**
 * Drain the queue: process messages one at a time in FIFO order.
 */
async function drainQueue(agentSessionId) {
    const q = sessionQueues.get(agentSessionId);
    if (!q)
        return;
    q.processing = true;
    while (q.messages.length > 0) {
        const msg = q.messages.shift();
        try {
            await q.processFn(msg);
        }
        catch (err) {
            logger.error(`Error processing queued message for ${agentSessionId}: ${err instanceof Error ? err.message : String(err)}`);
        }
    }
    q.processing = false;
}
/**
 * Check if a session is currently processing a message.
 */
export function isProcessing(agentSessionId) {
    const q = sessionQueues.get(agentSessionId);
    return q !== undefined && q.processing;
}
/**
 * Get the number of pending messages in the queue.
 */
export function getQueueSize(agentSessionId) {
    const q = sessionQueues.get(agentSessionId);
    return q ? q.messages.length : 0;
}
/**
 * Clear the queue for a session (e.g. on cancel).
 * Does NOT stop the currently processing message.
 */
export function clearQueue(agentSessionId) {
    const q = sessionQueues.get(agentSessionId);
    if (q) {
        q.messages = [];
    }
}
/**
 * Remove all state for a session.
 */
export function removeSession(agentSessionId) {
    sessionQueues.delete(agentSessionId);
}
/**
 * Clear all queues (used during shutdown).
 */
export function clearAllQueues() {
    sessionQueues.clear();
}
//# sourceMappingURL=message-queue.js.map