import { createCache, type CacheProvider, type CacheStats } from '@stock-bot/cache';
import type { RedisConfig } from './types';
import { getServiceConfig } from './service-registry';

/**
 * Service-aware cache that uses the service's Redis DB
 * Automatically prefixes keys with the service's cache namespace
 */
export class ServiceCache implements CacheProvider {
  private cache: CacheProvider;
  private prefix: string;

  constructor(
    serviceName: string,
    redisConfig: RedisConfig,
    isGlobalCache: boolean = false,
    logger?: any
  ) {
    // Get service configuration
    const serviceConfig = getServiceConfig(serviceName);
    if (!serviceConfig && !isGlobalCache) {
      throw new Error(`Unknown service: ${serviceName}`);
    }

    // Determine Redis DB and prefix
    let db: number;
    let prefix: string;

    if (isGlobalCache) {
      // Global cache uses db:0
      db = 0;
      prefix = 'stock-bot:shared';
    } else {
      // Service cache uses service's DB
      db = serviceConfig!.db;
      prefix = serviceConfig!.cachePrefix;
    }

    // Create underlying cache with correct DB
    const cacheConfig = {
      redisConfig: {
        ...redisConfig,
        db,
      },
      keyPrefix: prefix + ':',
      logger,
    };

    this.cache = createCache(cacheConfig);
    this.prefix = prefix;
  }

  // Implement CacheProvider interface
  async get<T = any>(key: string): Promise<T | null> {
    return this.cache.get<T>(key);
  }

  async set<T = any>(
    key: string,
    value: T,
    options?:
      | number
      | {
          ttl?: number;
          preserveTTL?: boolean;
          onlyIfExists?: boolean;
          onlyIfNotExists?: boolean;
          getOldValue?: boolean;
        }
  ): Promise<T | null> {
    return this.cache.set(key, value, options);
  }

  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(): CacheStats {
    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();
  }

  // Enhanced cache methods (delegate to underlying cache if available)
  async update<T = any>(key: string, value: T): Promise<T | null> {
    if (this.cache.update) {
      return this.cache.update(key, value);
    }
    // Fallback implementation
    return this.cache.set(key, value, { preserveTTL: true });
  }

  async setIfExists<T = any>(key: string, value: T, ttl?: number): Promise<boolean> {
    if (this.cache.setIfExists) {
      return this.cache.setIfExists(key, value, ttl);
    }
    // Fallback implementation
    const result = await this.cache.set(key, value, { onlyIfExists: true, ttl });
    return result !== null;
  }

  async setIfNotExists<T = any>(key: string, value: T, ttl?: number): Promise<boolean> {
    if (this.cache.setIfNotExists) {
      return this.cache.setIfNotExists(key, value, ttl);
    }
    // Fallback implementation
    const result = await this.cache.set(key, value, { onlyIfNotExists: true, ttl });
    return result !== null;
  }

  async replace<T = any>(key: string, value: T, ttl?: number): Promise<T | null> {
    if (this.cache.replace) {
      return this.cache.replace(key, value, ttl);
    }
    // Fallback implementation
    return this.cache.set(key, value, ttl);
  }

  async updateField<T = any>(key: string, updater: (current: T | null) => T, ttl?: number): Promise<T | null> {
    if (this.cache.updateField) {
      return this.cache.updateField(key, updater, ttl);
    }
    // Fallback implementation
    const current = await this.cache.get<T>(key);
    const updated = updater(current);
    return this.cache.set(key, updated, ttl);
  }

  /**
   * Get the actual Redis key with prefix
   */
  getKey(key: string): string {
    return `${this.prefix}:${key}`;
  }
}


/**
 * Factory function to create service cache
 */
export function createServiceCache(
  serviceName: string,
  redisConfig: RedisConfig,
  options: { global?: boolean; logger?: any } = {}
): ServiceCache {
  return new ServiceCache(serviceName, redisConfig, options.global, options.logger);
}