import type { CacheProvider, ICache } from './types';

/**
 * A cache wrapper that automatically prefixes all keys with a namespace
 * Used to provide isolated cache spaces for different services
 */
export class NamespacedCache implements CacheProvider {
  private readonly prefix: string;

  constructor(
    private readonly cache: CacheProvider,
    private readonly namespace: string
  ) {
    this.prefix = `${namespace}:`;
  }

  async get<T = unknown>(key: string): Promise<T | null> {
    return this.cache.get(`${this.prefix}${key}`);
  }

  async set<T>(key: string, value: T, options?: number | { ttl?: number }): Promise<T | null> {
    return this.cache.set(`${this.prefix}${key}`, value, options);
  }

  async del(key: string): Promise<void> {
    return this.cache.del(`${this.prefix}${key}`);
  }

  async exists(key: string): Promise<boolean> {
    return this.cache.exists(`${this.prefix}${key}`);
  }

  async keys(pattern: string = '*'): Promise<string[]> {
    const fullPattern = `${this.prefix}${pattern}`;
    const keys = await this.cache.keys(fullPattern);
    // Remove the prefix from returned keys for cleaner API
    return keys.filter(k => k.startsWith(this.prefix)).map(k => k.substring(this.prefix.length));
  }

  async clear(): Promise<void> {
    // Clear only keys with this namespace prefix
    const keys = await this.cache.keys(`${this.prefix}*`);
    if (keys.length > 0) {
      await Promise.all(keys.map(key => this.cache.del(key.substring(this.prefix.length))));
    }
  }

  getNamespace(): string {
    return this.namespace;
  }

  getFullPrefix(): string {
    return this.prefix;
  }

  // CacheProvider methods
  getStats() {
    return this.cache.getStats();
  }

  async health(): Promise<boolean> {
    return this.cache.health();
  }

  async waitForReady(timeout?: number): Promise<void> {
    return this.cache.waitForReady(timeout);
  }

  isReady(): boolean {
    return this.cache.isReady();
  }
}

/**
 * Adapter to convert ICache to CacheProvider
 */
export class CacheAdapter implements CacheProvider {
  constructor(private readonly cache: ICache) {}

  async get<T = unknown>(key: string): Promise<T | null> {
    return this.cache.get(key);
  }

  async set<T>(key: string, value: T, options?: number | { ttl?: number }): Promise<T | null> {
    const ttl = typeof options === 'number' ? options : options?.ttl;
    await this.cache.set(key, value, ttl);
    return null;
  }

  async del(key: string): Promise<void> {
    return this.cache.del(key);
  }

  async exists(key: string): Promise<boolean> {
    return this.cache.exists(key);
  }

  async clear(): Promise<void> {
    return this.cache.clear();
  }

  async keys(pattern: string): Promise<string[]> {
    return this.cache.keys(pattern);
  }

  getStats() {
    return {
      hits: 0,
      misses: 0,
      errors: 0,
      hitRate: 0,
      total: 0,
      uptime: process.uptime(),
    };
  }

  async health(): Promise<boolean> {
    return this.cache.ping();
  }

  async waitForReady(): Promise<void> {
    // ICache doesn't have waitForReady, so just check connection
    if (!this.cache.isConnected()) {
      throw new Error('Cache not connected');
    }
  }

  isReady(): boolean {
    return this.cache.isConnected();
  }
}
