"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.RedisCache = void 0;
const tslib_1 = require("tslib");
const logger_1 = require("../../utils/logger");
const config_1 = tslib_1.__importDefault(require("../../config/config"));
const Sentry = tslib_1.__importStar(require("@sentry/node"));
const checkCacheKeyForAnomalies = (cacheKey, maxKeyLength = 512) => {
    const anomalies = [
        {
            condition: () => cacheKey.includes('[object Object]'),
            message: 'contains [object Object]',
        },
        {
            condition: () => !cacheKey || cacheKey.trim() === '',
            message: 'key is empty or null',
        },
        {
            condition: () => /[\t\n\r]/.test(cacheKey),
            message: 'contains special characters',
        },
        {
            condition: () => cacheKey.length > maxKeyLength,
            message: `key is too long`,
        },
    ];
    anomalies.forEach((anomaly) => {
        if (anomaly.condition()) {
            logger_1.logger.error({ cacheKey }, `Anomaly detected in Redis cache key: ${anomaly.message}`);
            try {
                Sentry.captureEvent({
                    message: 'Anomaly detected in Redis cache key',
                    extra: { cacheKey, anomalyMessage: anomaly.message },
                });
            }
            catch (error) {
                logger_1.logger.error({ error: error.message }, 'Error capturing event in Sentry');
            }
        }
    });
};
const RedisCache = (redisClient, prometheus, options) => {
    return async (req, res, next) => {
        if (!config_1.default.redis.cacheEnabled) {
            logger_1.logger.debug({ time: Date.now() }, `Redis cache not enabled for ${req.url}`);
            return next();
        }
        res.setHeader('Cache-Control', `public, max-age=${options.expireSec}, s-maxage=${options.expireSec}`);
        const appName = config_1.default.appName;
        const cacheKey = `${appName}:${options.keyName(req)}`;
        checkCacheKeyForAnomalies(cacheKey);
        try {
            const cachedData = await redisClient.get(cacheKey);
            if (cachedData) {
                logger_1.logger.debug({ cacheKey, time: Date.now() }, `Retrieved from Redis Cache ${req.url}`);
                res.setHeader('X-Cache-Status', 'HIT');
                return res.json(JSON.parse(cachedData));
            }
            const originalSend = res.send.bind(res);
            res.send = (body) => {
                const isCacheableResponse = (res.statusCode >= 200 && res.statusCode < 300) || res.statusCode === 404;
                if (isCacheableResponse) {
                    redisClient
                        .set(cacheKey, body, { EX: options.expireSec })
                        .then(() => {
                        logger_1.logger.debug({ cacheKey, expireSec: options.expireSec, time: Date.now(), byteSize: Buffer.byteLength(body) }, 'Set in Redis Cache');
                        prometheus.cacheBytesWrite.inc({ key: req.url.split('/').slice(0, 2).join('/') }, Buffer.byteLength(body));
                    })
                        .catch((error) => {
                        logger_1.logger.error({ error: error.message, time: Date.now() }, 'Error setting Redis cache');
                    });
                }
                res.setHeader('X-Cache-Status', 'MISS');
                return originalSend(body);
            };
            next();
        }
        catch (error) {
            logger_1.logger.error({ error: error.message }, `Error in Redis cache middleware for ${req.url}`);
            next();
        }
    };
};
exports.RedisCache = RedisCache;
//# sourceMappingURL=redis-cache.js.map