/**
 * Generic Valkey (Redis-compatible) cache.
 *
 * Reuses the same ioredis singleton from presignCache.ts but exposes a
 * simple typed get/set/invalidate API for arbitrary JSON values.
 *
 * Usage:
 *   const books = await cache.get<Book[]>('books:all');
 *   await cache.set('books:all', books, 5 * 60);
 *   await cache.invalidate('books:all');
 */

import Redis from 'ioredis';

let _client: Redis | null = null;

function client(): Redis {
	if (!_client) {
		const url = process.env.VALKEY_URL ?? 'redis://valkey:6379';
		_client = new Redis(url, {
			lazyConnect: false,
			enableOfflineQueue: true,
			maxRetriesPerRequest: 1,
			connectTimeout: 1500
		});
		_client.on('error', (err: Error) => {
			console.error('[cache] Valkey error:', err.message);
		});
	}
	return _client;
}

/** Return the cached value for key, or null if absent / expired / error. */
export async function get<T>(key: string): Promise<T | null> {
	try {
		const raw = await client().get(key);
		if (!raw) return null;
		return JSON.parse(raw) as T;
	} catch {
		return null;
	}
}

/**
 * Store a value under key for ttlSeconds seconds.
 * Silently no-ops on Valkey errors so callers never crash.
 */
export async function set<T>(key: string, value: T, ttlSeconds: number): Promise<void> {
	try {
		await client().set(key, JSON.stringify(value), 'EX', ttlSeconds);
	} catch {
		// non-fatal
	}
}

/** Delete a key immediately (e.g. after a write that invalidates it). */
export async function invalidate(key: string): Promise<void> {
	try {
		await client().del(key);
	} catch {
		// non-fatal
	}
}

/** Invalidate all keys matching a glob pattern (e.g. 'books:*'). */
export async function invalidatePattern(pattern: string): Promise<void> {
	try {
		const keys = await client().keys(pattern);
		if (keys.length > 0) await client().del(...keys);
	} catch {
		// non-fatal
	}
}
