/**
 * Priority Queue Manager
 * BullMQ priority levels with datacenter affinity
 * Sealed namespace for immutability
 */
import { ErrorFactory, Logger } from '../../../src/index.js';
// Priority mappings
const PRIORITY_VALUES = {
    critical: 10,
    high: 5,
    normal: 1,
    low: 0,
};
let queueRedisModule;
let hasWarnedMissingQueueRedis = false;
const loadQueueRedisModule = async () => {
    if (queueRedisModule)
        return queueRedisModule;
    try {
        queueRedisModule = (await import('../../queue-redis/src/index.js'));
        return queueRedisModule;
    }
    catch (error) {
        if (!hasWarnedMissingQueueRedis) {
            hasWarnedMissingQueueRedis = true;
            Logger.warn('Optional package "@zintrust/queue-redis" is not installed. PriorityQueue features are disabled until it is installed.', error);
        }
        return undefined;
    }
};
/**
 * Helper: Get or create queue via shared driver
 */
const getQueue = async (queueName) => {
    const queueRedis = await loadQueueRedisModule();
    if (!queueRedis) {
        throw ErrorFactory.createWorkerError('Optional package "@zintrust/queue-redis" is required for PriorityQueue. Install it to use queue features.');
    }
    return queueRedis.BullMQRedisQueue.getQueue(queueName);
};
/**
 * Helper: Build job options with priority
 */
const buildJobOptions = (options) => {
    const jobOptions = {
        priority: PRIORITY_VALUES[options.priority],
    };
    if (options.delay !== undefined) {
        jobOptions['delay'] = options.delay;
    }
    if (options.attempts !== undefined) {
        jobOptions['attempts'] = options.attempts;
    }
    if (options.backoff) {
        jobOptions['backoff'] = options.backoff;
    }
    if (options.removeOnComplete !== undefined) {
        jobOptions['removeOnComplete'] = options.removeOnComplete;
    }
    if (options.removeOnFail !== undefined) {
        jobOptions['removeOnFail'] = options.removeOnFail;
    }
    // Store datacenter affinity in job data (workers can read this)
    if (options.datacenter) {
        jobOptions['datacenter'] = options.datacenter;
    }
    return jobOptions;
};
/**
 * Helper: Match datacenter affinity
 */
const matchesDatacenterAffinity = (jobDatacenter, workerRegion) => {
    if (!jobDatacenter) {
        return true; // No affinity, can be processed anywhere
    }
    // Check preferred datacenters first
    if (jobDatacenter.preferred.includes(workerRegion)) {
        return true;
    }
    // Check fallback datacenters
    if (jobDatacenter.fallback.includes(workerRegion)) {
        return true;
    }
    return false;
};
/**
 * Priority Queue Manager - Sealed namespace
 */
export const PriorityQueue = Object.freeze({
    /**
     * Initialize with Redis configuration
     */
    initialize(_config) {
        Logger.debug('PriorityQueue.initialize() called - auto-initialized via BullMQRedisQueue');
    },
    /**
     * Add a job to the queue with priority
     */
    async addJob(queueName, jobName, data, options) {
        const queue = await getQueue(queueName);
        const jobOptions = buildJobOptions(options);
        try {
            const job = await queue.add(jobName, data, jobOptions);
            Logger.debug(`Added job "${jobName}" to queue "${queueName}"`, {
                jobId: job.id,
                priority: options.priority,
                datacenter: options.datacenter,
            });
            if (job.id === undefined) {
                throw ErrorFactory.createWorkerError(`Failed to add job "${jobName}" to queue "${queueName}": missing job id`);
            }
            return job.id;
        }
        catch (error) {
            Logger.error(`Failed to add job "${jobName}" to queue "${queueName}"`, error);
            throw error;
        }
    },
    /**
     * Add multiple jobs in bulk
     */
    async addBulk(queueName, jobs) {
        const queue = await getQueue(queueName);
        try {
            const bulkJobs = jobs.map((job) => ({
                name: job.name,
                data: job.data,
                opts: buildJobOptions(job.options),
            }));
            const addedJobs = await queue.addBulk(bulkJobs);
            Logger.info(`Added ${addedJobs.length} jobs to queue "${queueName}"`);
            return addedJobs.map((job) => {
                if (job.id === undefined) {
                    throw ErrorFactory.createWorkerError(`Failed to add job to queue "${queueName}": missing job id`);
                }
                return job.id;
            });
        }
        catch (error) {
            Logger.error(`Failed to add bulk jobs to queue "${queueName}"`, error);
            throw error;
        }
    },
    /**
     * Get job by ID
     */
    async getJob(queueName, jobId) {
        const queue = await getQueue(queueName);
        return queue.getJob(jobId);
    },
    /**
     * Remove a job
     */
    async removeJob(queueName, jobId) {
        const queue = await getQueue(queueName);
        const job = await queue.getJob(jobId);
        if (job) {
            await job.remove();
            Logger.debug(`Removed job ${jobId} from queue "${queueName}"`);
        }
    },
    /**
     * Pause a queue
     */
    async pause(queueName) {
        const queue = await getQueue(queueName);
        await queue.pause();
        Logger.info(`Paused queue "${queueName}"`);
    },
    /**
     * Resume a queue
     */
    async resume(queueName) {
        const queue = await getQueue(queueName);
        await queue.resume();
        Logger.info(`Resumed queue "${queueName}"`);
    },
    /**
     * Get queue information
     */
    async getQueueInfo(queueName) {
        const queue = await getQueue(queueName);
        const isPaused = await queue.isPaused();
        const jobCounts = await queue.getJobCounts();
        return {
            name: queueName,
            isPaused,
            jobCounts: {
                active: jobCounts['active'] || 0,
                waiting: jobCounts['waiting'] || 0,
                completed: jobCounts['completed'] || 0,
                failed: jobCounts['failed'] || 0,
                delayed: jobCounts['delayed'] || 0,
                paused: jobCounts['paused'] || 0,
            },
        };
    },
    /**
     * Get all queue names
     */
    async getQueueNames() {
        const queueRedis = await loadQueueRedisModule();
        if (!queueRedis)
            return [];
        return queueRedis.BullMQRedisQueue.getQueueNames();
    },
    /**
     * Drain queue (remove all jobs)
     */
    async drain(queueName, delayed = false) {
        const queue = await getQueue(queueName);
        await queue.drain(delayed);
        Logger.info(`Drained queue "${queueName}"`, { delayed });
    },
    /**
     * Clean old jobs from queue
     */
    async clean(queueName, grace, limit, type = 'completed') {
        const queue = await getQueue(queueName);
        const jobs = await queue.clean(grace, limit, type);
        Logger.info(`Cleaned ${jobs.length} ${type} jobs from queue "${queueName}"`);
        return jobs;
    },
    /**
     * Obliterate queue (remove all data including queue itself)
     */
    async obliterate(queueName, force = false) {
        const queue = await getQueue(queueName);
        await queue.obliterate({ force });
        const queueRedis = await loadQueueRedisModule();
        if (queueRedis) {
            await queueRedis.BullMQRedisQueue.closeQueue(queueName);
        }
        Logger.warn(`Obliterated queue "${queueName}"`);
    },
    /**
     * Get priority value for level
     */
    getPriorityValue(level) {
        return PRIORITY_VALUES[level];
    },
    /**
     * Check if job matches datacenter affinity
     */
    matchesDatacenter(jobDatacenter, workerRegion) {
        return matchesDatacenterAffinity(jobDatacenter, workerRegion);
    },
    /**
     * Get queue instance (internal use)
     */
    getQueueInstance(queueName) {
        return getQueue(queueName);
    },
    /**
     * Close a queue
     */
    async closeQueue(queueName) {
        const queueRedis = await loadQueueRedisModule();
        if (!queueRedis)
            return;
        await queueRedis.BullMQRedisQueue.closeQueue(queueName);
        Logger.info(`Closed queue "${queueName}"`);
    },
    /**
     * Shutdown and close all queues
     */
    async shutdown() {
        Logger.info('PriorityQueue shutting down via BullMQRedisQueue...');
        const queueRedis = await loadQueueRedisModule();
        if (!queueRedis)
            return;
        await queueRedis.BullMQRedisQueue.shutdown();
        Logger.info('PriorityQueue shutdown complete');
    },
});
// Graceful shutdown handled by WorkerShutdown
