import { randomUUID } from "node:crypto";
/**
 * Server-side prompt queue for a single session.
 * Steered messages sort before non-steered, stable within each group.
 */
export class PromptQueue {
    queue = [];
    /** Create a queue, optionally restoring from persisted data. */
    constructor(initial) {
        if (initial) {
            this.queue = [...initial];
        }
    }
    /** Add a message to the end of the queue. Returns the queued message. */
    enqueue(text, opts) {
        const msg = {
            id: randomUUID(),
            text,
            isSteered: opts?.isSteered ?? false,
            createdAt: Date.now(),
        };
        if (opts?.images?.length)
            msg.images = opts.images;
        if (opts?.attachments?.length)
            msg.attachments = opts.attachments;
        this.queue.push(msg);
        if (msg.isSteered)
            this.reorder();
        return msg;
    }
    /**
     * Mark a message as steered and reorder.
     * Steered messages sort before non-steered, stable within each group.
     * Returns true if the message was found and updated.
     */
    steer(messageId) {
        const msg = this.queue.find(m => m.id === messageId);
        if (!msg)
            return false;
        if (msg.isSteered)
            return true; // already steered
        msg.isSteered = true;
        this.reorder();
        return true;
    }
    /** Remove a message from the queue. Returns true if found and removed. */
    remove(messageId) {
        const idx = this.queue.findIndex(m => m.id === messageId);
        if (idx === -1)
            return false;
        this.queue.splice(idx, 1);
        return true;
    }
    /** Pop the next message from the front of the queue. Returns undefined if empty. */
    dequeue() {
        return this.queue.shift();
    }
    /** Pop all consecutive steered messages from the front. Returns empty array if front is not steered. */
    dequeueAllSteered() {
        const result = [];
        while (this.queue.length > 0 && this.queue[0].isSteered) {
            result.push(this.queue.shift());
        }
        return result;
    }
    /**
     * Insert a message at the front of the queue. Used by reconciliation paths
     * (e.g. RPC failure rollback) that need to put a row back at index 0.
     * Calls reorder() so the steered group still sorts first.
     */
    enqueueAtFront(text, opts) {
        const msg = {
            id: randomUUID(),
            text,
            isSteered: opts?.isSteered ?? false,
            createdAt: Date.now(),
        };
        if (opts?.images?.length)
            msg.images = opts.images;
        if (opts?.attachments?.length)
            msg.attachments = opts.attachments;
        this.queue.unshift(msg);
        this.reorder();
        return msg;
    }
    /** Peek at the front of the queue without removing. */
    peek() {
        return this.queue[0];
    }
    /** Get the full queue as an array (for broadcasting). */
    toArray() {
        return [...this.queue];
    }
    /** Number of messages in the queue. */
    get length() {
        return this.queue.length;
    }
    /** Whether the queue is empty. */
    get isEmpty() {
        return this.queue.length === 0;
    }
    /** Reorder queue to match the given ID list. Unknown IDs ignored. Unlisted items appended at end. */
    reorderByIds(messageIds) {
        const byId = new Map(this.queue.map(m => [m.id, m]));
        const reordered = [];
        const seen = new Set();
        for (const id of messageIds) {
            const msg = byId.get(id);
            if (msg) {
                reordered.push(msg);
                seen.add(id);
            }
        }
        for (const msg of this.queue) {
            if (!seen.has(msg.id))
                reordered.push(msg);
        }
        this.queue = reordered;
    }
    /**
     * Stable reorder: steered messages first, non-steered second.
     * Within each group, original insertion order is preserved.
     */
    reorder() {
        const steered = this.queue.filter(m => m.isSteered);
        const normal = this.queue.filter(m => !m.isSteered);
        this.queue = [...steered, ...normal];
    }
}
//# sourceMappingURL=prompt-queue.js.map