"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DatabaseSubscriber = void 0;
const common_1 = require("@nestjs/common");
const common_2 = require("./common");
class DatabaseSubscriber {
    constructor(mqMessageService, mqMessageQueueService, poller) {
        this.mqMessageService = mqMessageService;
        this.mqMessageQueueService = mqMessageQueueService;
        this.poller = poller;
        this.serviceRole = process.env.QUEUES_SERVICE_ROLE || 'both';
        if (!process.env.QUEUES_SERVICE_ROLE) {
            this.logger.debug('QUEUES_SERVICE_ROLE is not defined. Defaulting DatabaseSubscriber service role to "both".');
        }
    }
    get loggerContext() {
        return this.constructor.name;
    }
    get logger() {
        if (!this._loggerInstance) {
            this._loggerInstance = new common_1.Logger(this.loggerContext);
        }
        return this._loggerInstance;
    }
    async processNext(queueName) {
        const job = await this.mqMessageService.lockNextPendingMessage(queueName);
        if (!job) {
            return;
        }
        const messageContentString = job.input.toString();
        let message = null;
        try {
            message = JSON.parse(messageContentString);
            if (!message.retryCount)
                message.retryCount = 0;
            if (!message.retryInterval)
                message.retryInterval = 1000;
            if (!message.currentRetry)
                message.currentRetry = 0;
            await this.processMessage(message);
        }
        catch (error) {
            this.logger.error(`Error processing message: ${error.message}`);
            if (message) {
                if (message.currentRetry < message.retryCount) {
                    await this.updateStatusInDatabase('retrying', message);
                    message.currentRetry++;
                    this.logger.warn(`Retrying message (${message.currentRetry}/${message.retryCount}) after ${message.retryInterval}ms`);
                    setTimeout(() => {
                        this.retryMessage(message);
                    }, message.retryInterval);
                }
                else {
                    await this.updateStatusInDatabase('failed', message, error.message, '');
                    this.logger.error(`Message failed after ${message.retryCount} attempts: ${error.message}`);
                }
            }
        }
    }
    async onModuleInit() {
        const defaultBroker = process.env.QUEUES_DEFAULT_BROKER || 'database';
        const solidCliRunning = process.env.SOLID_CLI_RUNNING || "false";
        const queueNameRegex = (process.env.QUEUES_QUEUE_NAME_REGEX_TO_ENABLE || '').trim();
        const roleAllowed = ['both', 'subscriber'].includes(this.serviceRole);
        if (!roleAllowed) {
            this.logger.log(`DatabaseSubscriber is disabled because QUEUES_SERVICE_ROLE is "${this.serviceRole}". Expected "both" or "subscriber".`);
            return;
        }
        if (defaultBroker === 'database' && solidCliRunning === "false") {
            const options = this.options();
            const queueName = options.queueName;
            if (queueNameRegex && queueNameRegex !== "all") {
                try {
                    const regex = new RegExp(queueNameRegex);
                    if (!regex.test(queueName)) {
                        this.logger.log(`DatabaseSubscriber for queue ${queueName} is disabled because it does not match QUEUES_QUEUE_NAME_REGEX_TO_ENABLE=${queueNameRegex}`);
                        return;
                    }
                }
                catch (error) {
                    this.logger.error(`Invalid QUEUES_QUEUE_NAME_REGEX_TO_ENABLE regex "${queueNameRegex}". Subscriber for queue ${queueName} will not start.`);
                    return;
                }
            }
            const namespacedQueueName = (0, common_2.buildNamespacedQueueName)(queueName);
            this.poller.start(namespacedQueueName, (q) => this.processNext(q), {
                baseDelayMs: 1000,
                maxDelayMs: 30_000,
                timeoutPerIterationMs: 5 * 60_000,
                jitter: true,
            });
            this.logger.log(`DatabaseSubscriber ready to consume messages: ${JSON.stringify(this.options())}`);
        }
    }
    onModuleDestroy() {
        const options = this.options();
        const queueName = options.queueName;
        const namespacedQueueName = (0, common_2.buildNamespacedQueueName)(queueName);
        this.poller.stop(namespacedQueueName);
    }
    async processMessage(message) {
        await this.updateStatusInDatabase('started', message);
        const result = await this.subscribe(message);
        await this.updateStatusInDatabase('succeeded', message, '', result ? JSON.stringify(result, null, 2) : '');
    }
    async retryMessage(message) {
        try {
            await this.processMessage(message);
        }
        catch (error) {
            if (message.currentRetry < message.retryCount) {
                await this.updateStatusInDatabase('retrying', message);
                message.currentRetry++;
                this.logger.warn(`Retrying message (${message.currentRetry}/${message.retryCount}) after ${message.retryInterval}ms: ${error.message}`);
                setTimeout(() => {
                    this.retryMessage(message);
                }, message.retryInterval);
            }
            else {
                this.logger.error(`Message failed after ${message.retryCount} attempts: ${error.message}`);
                await this.updateStatusInDatabase('failed', message, error.message, '');
            }
        }
    }
    async updateStatusInDatabase(stage, message, error = '', result = '') {
        try {
            this.logger.debug(`Updating message status in database: ${stage} for messageId: ${message.messageId}`);
            const mqMessage = await this.mqMessageService.repo.findOne({
                where: {
                    messageId: message.messageId,
                }
            });
            if (mqMessage) {
                this.logger.debug(`Found message in database: ${JSON.stringify(mqMessage.messageId)}`);
                this.logger.debug(`Updating message status in database: ${stage} for messageId: ${mqMessage.id}`);
                const updatedFields = {
                    stage: stage
                };
                if (stage === 'failed' || stage === 'succeeded') {
                    updatedFields['finishedAt'] = new Date();
                    updatedFields['elapsedMillis'] = updatedFields['finishedAt'].getTime() - mqMessage.startedAt.getTime();
                }
                if (stage === 'succeeded') {
                    updatedFields['output'] = result;
                }
                if (stage === 'failed') {
                    updatedFields['error'] = error;
                }
                await this.mqMessageService.repo.update(mqMessage.id, updatedFields);
                this.logger.debug(`Message status updated to ${stage} for messageId: ${mqMessage.id}`);
            }
        }
        catch (error) {
            this.logger.error(error.message, error.stack);
        }
    }
}
exports.DatabaseSubscriber = DatabaseSubscriber;
//# sourceMappingURL=database-subscriber.service.js.map