"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
const events_1 = __importDefault(require("events"));
const ioredis_1 = __importDefault(require("ioredis"));
class KeyvRedis extends events_1.default {
    constructor(uri, options) {
        super();
        this.ttlSupport = true;
        this._getKeyName = (key) => {
            if (!this.opts.useRedisSets) {
                return `sets:${this._getNamespace()}:${key}`;
            }
            return key;
        };
        this.opts = {};
        this.opts.useRedisSets = true;
        this.opts.dialect = 'redis';
        if (typeof uri !== 'string' && uri.options && ('family' in uri.options || uri.isCluster)) {
            this.redis = uri;
        }
        else {
            options = { ...(typeof uri === 'string' ? { uri } : uri), ...options };
            // @ts-expect-error - uri is a string or RedisOptions
            this.redis = new ioredis_1.default(options.uri, options);
        }
        if (options !== undefined && options.useRedisSets === false) {
            this.opts.useRedisSets = false;
        }
        this.redis.on('error', (error) => this.emit('error', error));
    }
    _getNamespace() {
        return `namespace:${this.namespace}`;
    }
    async get(key) {
        key = this._getKeyName(key);
        const value = await this.redis.get(key);
        if (value === null) {
            return undefined;
        }
        return value;
    }
    async getMany(keys) {
        keys = keys.map(this._getKeyName);
        return this.redis.mget(keys);
    }
    async set(key, value, ttl) {
        if (value === undefined) {
            return undefined;
        }
        key = this._getKeyName(key);
        const set = async (redis) => {
            if (typeof ttl === 'number') {
                await redis.set(key, value, 'PX', ttl);
            }
            else {
                await redis.set(key, value);
            }
        };
        if (this.opts.useRedisSets) {
            const trx = await this.redis.multi();
            await set(trx);
            await trx.sadd(this._getNamespace(), key);
            await trx.exec();
        }
        else {
            await set(this.redis);
        }
    }
    async delete(key) {
        key = this._getKeyName(key);
        let items = 0;
        const unlink = async (redis) => redis.unlink(key);
        if (this.opts.useRedisSets) {
            const trx = this.redis.multi();
            await unlink(trx);
            await trx.srem(this._getNamespace(), key);
            const r = await trx.exec();
            items = r[0][1];
        }
        else {
            items = await unlink(this.redis);
        }
        return items > 0;
    }
    async deleteMany(keys) {
        const deletePromises = keys.map(async (key) => this.delete(key));
        const results = await Promise.allSettled(deletePromises);
        // @ts-expect-error - results is an array of objects with status and value
        return results.every(result => result.value);
    }
    async clear() {
        if (this.opts.useRedisSets) {
            const keys = await this.redis.smembers(this._getNamespace());
            if (keys.length > 0) {
                await Promise.all([
                    this.redis.unlink([...keys]),
                    this.redis.srem(this._getNamespace(), [...keys]),
                ]);
            }
        }
        else {
            const pattern = `sets:${this._getNamespace()}:*`;
            const keys = await this.redis.keys(pattern);
            if (keys.length > 0) {
                await this.redis.unlink(keys);
            }
        }
    }
    async *iterator(namespace) {
        const scan = this.redis.scan.bind(this.redis);
        const get = this.redis.mget.bind(this.redis);
        let cursor = '0';
        do {
            // eslint-disable-next-line no-await-in-loop
            const [curs, keys] = await scan(cursor, 'MATCH', `${namespace}:*`);
            cursor = curs;
            if (keys.length > 0) {
                // eslint-disable-next-line no-await-in-loop
                const values = await get(keys);
                for (const [i] of keys.entries()) {
                    const key = keys[i];
                    const value = values[i];
                    yield [key, value];
                }
            }
        } while (cursor !== '0');
    }
    async has(key) {
        const value = await this.redis.exists(key);
        return value !== 0;
    }
    async disconnect() {
        return this.redis.disconnect();
    }
}
module.exports = KeyvRedis;
//# sourceMappingURL=index.js.map