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 { RedisDb, RedisHostname, RedisIPFamily, RedisPassword, RedisPort, RedisTlsCa, RedisTlsCert, RedisTlsKey, RedisTlsSentinelMode, RedisUsername, ShouldRedisTlsEnable, } from "../EnvironmentConfig";
import logger from "../Utils/Logger";
import Sleep from "../../Types/Sleep";
import { Redis as RedisClient } from "ioredis";
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
import GracefulShutdown, { ShutdownPriority } from "../Utils/GracefulShutdown";
class Redis {
    static isConnected() {
        if (!this.client) {
            return false;
        }
        return this.client.status === "ready";
    }
    static getClient() {
        return this.client;
    }
    static getRedisOptions() {
        const redisOptions = {
            host: RedisHostname,
            port: RedisPort.toNumber(),
            username: RedisUsername,
            password: RedisPassword,
            db: RedisDb,
            enableTLSForSentinelMode: RedisTlsSentinelMode,
            family: RedisIPFamily,
            lazyConnect: true,
        };
        if (ShouldRedisTlsEnable) {
            redisOptions.tls = {
                ca: RedisTlsCa || undefined,
                cert: RedisTlsCert || undefined,
                key: RedisTlsKey || undefined,
            };
        }
        return redisOptions;
    }
    static async connect() {
        let retry = 0;
        try {
            this.client = new RedisClient(this.getRedisOptions());
            // Listen to 'error' events to the Redis connection
            this.client.on("error", (error) => {
                if (error.code === "ECONNRESET") {
                    logger.error("Connection to Redis Session Store timed out.");
                }
                else if (error.code === "ECONNREFUSED") {
                    logger.error("Connection to Redis Session Store refused!");
                }
                else {
                    logger.error(error);
                }
            });
            // Listen to 'reconnecting' event to Redis
            this.client.on("reconnecting", () => {
                var _a;
                if (((_a = this.client) === null || _a === void 0 ? void 0 : _a.status) === "reconnecting") {
                    logger.error("Reconnecting to Redis Session Store...");
                }
                else {
                    logger.error("Error reconnecting to Redis Session Store.");
                }
            });
            // Listen to the 'connect' event to Redis
            this.client.on("connect", (err) => {
                if (!err) {
                    logger.debug("Connected to Redis Session Store!");
                }
            });
            const connectToDatabase = async (client) => {
                try {
                    await client.connect();
                }
                catch (err) {
                    if (retry < 3) {
                        logger.debug("Cannot connect to Redis. Retrying again in 5 seconds");
                        // sleep for 5 seconds.
                        await Sleep.sleep(5000);
                        retry++;
                        return await connectToDatabase(client);
                    }
                    throw err;
                }
            };
            await connectToDatabase(this.client);
            logger.debug(`Redis connected on ${RedisHostname}:${RedisPort.toNumber()}`);
            // Close the Redis connection on shutdown.
            GracefulShutdown.registerHandler("Redis", ShutdownPriority.DataStores, () => {
                return this.disconnect();
            });
            return this.client;
        }
        catch (err) {
            logger.error("Redis Connection Failed");
            logger.error(err);
            throw err;
        }
    }
    static disconnect() {
        var _a;
        if (this.isConnected()) {
            (_a = this.client) === null || _a === void 0 ? void 0 : _a.disconnect();
            this.client = null;
        }
        return Promise.resolve();
    }
    static async checkConnnectionStatus() {
        var _a;
        // Ping redis to check if the connection is still alive
        try {
            const result = await ((_a = this.client) === null || _a === void 0 ? void 0 : _a.ping());
            if (result !== "PONG") {
                return false;
            }
            return true;
        }
        catch (err) {
            logger.error("Redis Connection Lost");
            logger.error(err);
            return false;
        }
    }
}
Redis.client = null;
export default Redis;
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Boolean)
], Redis, "isConnected", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Object)
], Redis, "getClient", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Object)
], Redis, "getRedisOptions", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], Redis, "connect", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], Redis, "disconnect", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], Redis, "checkConnnectionStatus", null);
//# sourceMappingURL=Redis.js.map