var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import { QueueName } from "./Queue";
import TimeoutException from "../../Types/Exception/TimeoutException";
import { Worker } from "bullmq";
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
import AppMetrics from "../Utils/Telemetry/AppMetrics";
import TelemetryContext from "../Utils/Telemetry/TelemetryContext";
import Telemetry, { SpanStatusCode, } from "../Utils/Telemetry";
import Redis from "./Redis";
import GracefulShutdown, { ShutdownPriority } from "../Utils/GracefulShutdown";
export default class QueueWorker {
    static getWorker(queueName, onJobInQueue, options) {
        const instrumentedJobHandler = async (job) => {
            const startNs = process.hrtime.bigint();
            const baseAttributes = {
                "messaging.system": "bullmq",
                "messaging.destination.name": queueName,
                "messaging.operation.name": job.name || "unknown",
            };
            AppMetrics.getWorkerJobsInFlight().add(1, baseAttributes);
            let outcome = "success";
            try {
                /*
                 * Seed a telemetry-context scope for this job so every span and log it
                 * produces inherits the queue/job name plus any tenant identifiers
                 * carried in the job payload (projectId, monitorId, incidentId, ...).
                 */
                await TelemetryContext.runWithContext(Object.assign({ queueName: queueName, jobName: job.name || "unknown" }, TelemetryContext.pickKnownAttributes(job.data)), () => {
                    /*
                     * Wrap the job in an explicit root span so every background job has
                     * a consistent, named trace root that carries the seeded context —
                     * the @CaptureSpan service calls it makes become children of this.
                     */
                    return Telemetry.startActiveSpan({
                        name: `worker.job ${queueName}/${job.name || "unknown"}`,
                        fn: async (span) => {
                            try {
                                await onJobInQueue(job);
                                span.setStatus({ code: SpanStatusCode.OK });
                            }
                            catch (err) {
                                span.recordException(err);
                                span.setStatus({ code: SpanStatusCode.ERROR });
                                throw err;
                            }
                            finally {
                                span.end();
                            }
                        },
                    });
                });
            }
            catch (err) {
                outcome =
                    err instanceof TimeoutException ||
                        (err === null || err === void 0 ? void 0 : err.name) === "TimeoutException"
                        ? "timeout"
                        : "failure";
                throw err;
            }
            finally {
                const elapsedNs = process.hrtime.bigint() - startNs;
                const durationMs = Number(elapsedNs) / 1e6;
                const attributes = Object.assign(Object.assign({}, baseAttributes), { outcome });
                AppMetrics.getWorkerJobCounter().add(1, attributes);
                AppMetrics.getWorkerJobDuration().record(durationMs, attributes);
                AppMetrics.getWorkerJobsInFlight().add(-1, baseAttributes);
            }
        };
        const worker = new Worker(queueName, instrumentedJobHandler, Object.assign(Object.assign({ connection: Redis.getRedisOptions(), concurrency: options.concurrency }, (options.lockDuration ? { lockDuration: options.lockDuration } : {})), (options.maxStalledCount !== undefined
            ? { maxStalledCount: options.maxStalledCount }
            : {})));
        /*
         * Stop pulling new jobs and let in-flight ones finish on shutdown. Runs in
         * the Workers tier — before datastores are drained — so jobs mid-flight can
         * still reach Postgres / Redis. Replaces a SIGINT-only handler that never
         * fired in containers (Kubernetes / docker stop send SIGTERM).
         */
        GracefulShutdown.registerHandler(`QueueWorker:${queueName}`, ShutdownPriority.Workers, () => {
            return worker.close();
        });
        return worker;
    }
    static async runJobWithTimeout(timeoutInMS, jobCallback) {
        const timeoutPromise = (ms) => {
            return new Promise((_resolve, reject) => {
                setTimeout(() => {
                    return reject(new TimeoutException("Job Timeout"));
                }, ms);
            });
        };
        return await Promise.race([timeoutPromise(timeoutInMS), jobCallback()]);
    }
}
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [String, Function, Object]),
    __metadata("design:returntype", Worker)
], QueueWorker, "getWorker", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Number, Function]),
    __metadata("design:returntype", Promise)
], QueueWorker, "runJobWithTimeout", null);
//# sourceMappingURL=QueueWorker.js.map