import { regEx } from "../../../regex.js";
import { logger } from "../../../../logger/index.js";
import { parseUrl } from "../../../url.js";
import { compressToBase64, decompressFromBase64 } from "../../../compress.js";
import { PackageCacheBase } from "./base.js";
import { DateTime } from "luxon";
import { createClient, createCluster } from "@redis/client";
//#region lib/util/cache/package/impl/redis.ts
function normalizeRedisUrl(url) {
	return url.replace(regEx(/^(rediss?)\+cluster:\/\//), "$1://");
}
var PackageCacheRedis = class PackageCacheRedis extends PackageCacheBase {
	static async create(url, prefix) {
		const rprefix = prefix ?? "";
		logger.debug("Redis cache init");
		const rewrittenUrl = normalizeRedisUrl(url);
		const clusteredMode = rewrittenUrl !== url;
		const config = {
			url: rewrittenUrl,
			socket: { reconnectStrategy: (retries) => Math.min(retries * 100, 3e3) },
			pingInterval: 3e4
		};
		let client;
		if (clusteredMode) {
			const clusterConfig = { rootNodes: [config] };
			const parsedUrl = parseUrl(rewrittenUrl);
			if (parsedUrl?.username) clusterConfig.defaults = { username: parsedUrl.username };
			if (parsedUrl?.password) {
				clusterConfig.defaults ??= {};
				clusterConfig.defaults.password = parsedUrl.password;
			}
			client = createCluster(clusterConfig);
		} else client = createClient(config);
		await client.connect();
		logger.debug("Redis cache connected");
		return new PackageCacheRedis(client, rprefix);
	}
	client;
	rprefix;
	constructor(client, rprefix) {
		super();
		this.client = client;
		this.rprefix = rprefix;
	}
	getKey(namespace, key) {
		return `${this.rprefix}${namespace}-${key}`;
	}
	async get(namespace, key) {
		logger.trace(`cache.get(${namespace}, ${key})`);
		try {
			const raw = await this.client.get(this.getKey(namespace, key));
			if (!raw) return;
			const cached = JSON.parse(raw);
			const expiry = DateTime.fromISO(cached.expiry);
			if (!expiry.isValid || DateTime.local() >= expiry) {
				await this.rm(namespace, key);
				return;
			}
			logger.trace({
				rprefix: this.rprefix,
				namespace,
				key
			}, "Returning cached value");
			if (!cached.compress) return cached.value;
			const json = await decompressFromBase64(cached.value);
			return JSON.parse(json);
		} catch {
			logger.trace({
				rprefix: this.rprefix,
				namespace,
				key
			}, "Cache miss");
		}
	}
	async set(namespace, key, value, hardTtlMinutes) {
		logger.trace({
			rprefix: this.rprefix,
			namespace,
			key,
			hardTtlMinutes
		}, "Saving cached value");
		const ttlSeconds = Math.floor(hardTtlMinutes * 60);
		try {
			if (ttlSeconds <= 0) {
				await this.rm(namespace, key);
				return;
			}
			const compressedValue = await compressToBase64(JSON.stringify(value));
			const expiry = DateTime.local().plus({ minutes: hardTtlMinutes });
			const payload = JSON.stringify({
				compress: true,
				value: compressedValue,
				expiry
			});
			await this.client.set(this.getKey(namespace, key), payload, { EX: ttlSeconds });
		} catch (err) {
			logger.once.warn({ err }, "Error while setting Redis cache value");
		}
	}
	destroy() {
		try {
			this.client.destroy();
		} catch (err) {
			logger.warn({ err }, "Redis cache destroy failed");
		}
		return Promise.resolve();
	}
	async rm(namespace, key) {
		logger.trace({
			rprefix: this.rprefix,
			namespace,
			key
		}, "Removing cache entry");
		await this.client.del(this.getKey(namespace, key));
	}
};
//#endregion
export { PackageCacheRedis };

//# sourceMappingURL=redis.js.map