/**
 * Priority Queue Implementation
 *
 * Extends the basic queue with priority-based ordering.
 * Items with higher priority are processed first.
 */
import { Queue } from './queue';
const PRIORITY_VALUES = {
    low: 0,
    normal: 1,
    high: 2,
    critical: 3,
};
const DEFAULT_PRIORITY_OPTIONS = {
    maxSize: 1000,
    maxRetries: 3,
    retryDelay: 1000,
    autoProcess: false,
    concurrency: 1,
    defaultPriority: 'normal',
    fairScheduling: false,
    maxWaitTime: 60000, // 1 minute
};
/**
 * Priority Queue
 */
export class PriorityQueue extends Queue {
    items = [];
    priorityOptions;
    constructor(options = {}) {
        super(options);
        this.priorityOptions = { ...DEFAULT_PRIORITY_OPTIONS, ...options };
    }
    /**
     * Add an item with priority
     */
    enqueuePriority(data, priority = this.priorityOptions.defaultPriority, metadata) {
        if (this.items.length >= this.options.maxSize) {
            this.emit('full');
            return null;
        }
        const item = {
            id: this.generateId(),
            data,
            enqueuedAt: new Date(),
            attempts: 0,
            priority,
            priorityValue: PRIORITY_VALUES[priority],
            metadata,
        };
        this.insertByPriority(item);
        this.emit('enqueue', item);
        if (this.options.autoProcess && this.processor) {
            this.processQueue();
        }
        return item;
    }
    /**
     * Override enqueue to use priority
     */
    enqueue(data, metadata) {
        return this.enqueuePriority(data, this.priorityOptions.defaultPriority, metadata);
    }
    /**
     * Insert item in correct position based on priority
     */
    insertByPriority(item) {
        // Apply fair scheduling if enabled (boost priority of waiting items)
        if (this.priorityOptions.fairScheduling) {
            this.applyFairScheduling();
        }
        // Find insertion point (maintain stability within same priority)
        let insertIndex = this.items.length;
        for (let i = 0; i < this.items.length; i++) {
            if (item.priorityValue > this.items[i].priorityValue) {
                insertIndex = i;
                break;
            }
        }
        this.items.splice(insertIndex, 0, item);
    }
    /**
     * Apply fair scheduling to prevent starvation
     */
    applyFairScheduling() {
        const now = Date.now();
        for (const item of this.items) {
            const waitTime = now - item.enqueuedAt.getTime();
            if (waitTime > this.priorityOptions.maxWaitTime) {
                // Boost priority for items that have been waiting too long
                const boostLevels = Math.floor(waitTime / this.priorityOptions.maxWaitTime);
                item.priorityValue = Math.min(PRIORITY_VALUES['critical'], item.priorityValue + boostLevels);
            }
        }
        // Re-sort after boosting
        this.items.sort((a, b) => b.priorityValue - a.priorityValue);
    }
    /**
     * Get items by priority level
     */
    getByPriority(priority) {
        return this.items.filter(item => item.priority === priority);
    }
    /**
     * Count items by priority level
     */
    countByPriority() {
        const counts = {
            low: 0,
            normal: 0,
            high: 0,
            critical: 0,
        };
        for (const item of this.items) {
            counts[item.priority]++;
        }
        return counts;
    }
    /**
     * Update item priority
     */
    updatePriority(id, priority) {
        const item = this.items.find(i => i.id === id);
        if (!item) {
            return false;
        }
        // Remove item
        const index = this.items.indexOf(item);
        this.items.splice(index, 1);
        // Update priority
        item.priority = priority;
        item.priorityValue = PRIORITY_VALUES[priority];
        // Re-insert in correct position
        this.insertByPriority(item);
        return true;
    }
    /**
     * Escalate item to higher priority
     */
    escalate(id) {
        const item = this.items.find(i => i.id === id);
        if (!item) {
            return false;
        }
        const priorities = ['low', 'normal', 'high', 'critical'];
        const currentIndex = priorities.indexOf(item.priority);
        if (currentIndex < priorities.length - 1) {
            return this.updatePriority(id, priorities[currentIndex + 1]);
        }
        return false; // Already at highest priority
    }
    /**
     * De-escalate item to lower priority
     */
    deescalate(id) {
        const item = this.items.find(i => i.id === id);
        if (!item) {
            return false;
        }
        const priorities = ['low', 'normal', 'high', 'critical'];
        const currentIndex = priorities.indexOf(item.priority);
        if (currentIndex > 0) {
            return this.updatePriority(id, priorities[currentIndex - 1]);
        }
        return false; // Already at lowest priority
    }
    /**
     * Get highest priority item
     */
    peekHighest() {
        return this.items[0];
    }
    /**
     * Get lowest priority item
     */
    peekLowest() {
        return this.items[this.items.length - 1];
    }
    /**
     * Dequeue only items with specific priority or higher
     */
    dequeueWithMinPriority(minPriority) {
        const minValue = PRIORITY_VALUES[minPriority];
        const item = this.items.find(i => i.priorityValue >= minValue);
        if (item) {
            const index = this.items.indexOf(item);
            this.items.splice(index, 1);
            this.emit('dequeue', item);
            if (this.items.length === 0) {
                this.emit('empty');
            }
            return item;
        }
        return undefined;
    }
    /**
     * Clear all items of a specific priority
     */
    clearPriority(priority) {
        const initialLength = this.items.length;
        this.items = this.items.filter(item => item.priority !== priority);
        const removed = initialLength - this.items.length;
        if (this.items.length === 0) {
            this.emit('empty');
        }
        return removed;
    }
    /**
     * Get all items sorted by priority (highest first)
     */
    getAll() {
        return [...this.items];
    }
    /**
     * Get priority queue specific stats
     */
    getPriorityStats() {
        const counts = this.countByPriority();
        let highest = null;
        let lowest = null;
        if (this.items.length > 0) {
            highest = this.items[0].priority;
            lowest = this.items[this.items.length - 1].priority;
        }
        return {
            size: this.items.length,
            byPriority: counts,
            highestPriority: highest,
            lowestPriority: lowest,
        };
    }
    /**
     * Format priority queue status for display
     */
    formatStatus() {
        const stats = this.getStats();
        const priorityStats = this.getPriorityStats();
        return [
            '┌────────────────────────────────────┐',
            '│       PRIORITY QUEUE STATUS        │',
            '├────────────────────────────────────┤',
            `│ Size:        ${stats.size.toString().padStart(6)} / ${stats.maxSize.toString().padEnd(6)} │`,
            '├────────────────────────────────────┤',
            `│ Critical:    ${priorityStats.byPriority.critical.toString().padStart(13)} │`,
            `│ High:        ${priorityStats.byPriority.high.toString().padStart(13)} │`,
            `│ Normal:      ${priorityStats.byPriority.normal.toString().padStart(13)} │`,
            `│ Low:         ${priorityStats.byPriority.low.toString().padStart(13)} │`,
            '├────────────────────────────────────┤',
            `│ Processed:   ${stats.processed.toString().padStart(13)} │`,
            `│ Failed:      ${stats.failed.toString().padStart(13)} │`,
            '└────────────────────────────────────┘',
        ].join('\n');
    }
}
/**
 * Create a priority queue with custom settings
 */
export function createPriorityQueue(options) {
    return new PriorityQueue(options);
}
export default PriorityQueue;
//# sourceMappingURL=priority-queue.js.map