import log from 'electron-log';

interface CacheEntry<T> {
  data: T;
  timestamp: number;
}

interface CacheConfig {
  ttl: number; // Time-to-live in milliseconds
  maxSize?: number; // Maximum number of entries (optional)
}

/**
 * Generic cache manager with TTL (time-to-live) and automatic invalidation.
 * Provides type-safe caching with automatic expiration and size limits.
 */
export class CacheManager<K extends string, V> {
  private cache: Map<K, CacheEntry<V>>;
  private config: CacheConfig;

  constructor(config: CacheConfig) {
    this.cache = new Map();
    this.config = config;
  }

  /**
   * Set a value in the cache with current timestamp
   */
  set(key: K, value: V): void {
    // Enforce max size if configured
    if (this.config.maxSize && this.cache.size >= this.config.maxSize) {
      // Remove oldest entry
      const oldestKey = this.getOldestKey();
      if (oldestKey) {
        this.cache.delete(oldestKey);
        log.debug(`Cache size limit reached, removed oldest entry: ${oldestKey}`);
      }
    }

    this.cache.set(key, {
      data: value,
      timestamp: Date.now()
    });
  }

  /**
   * Get a value from the cache if it exists and hasn't expired
   * Returns undefined if the entry doesn't exist or has expired
   */
  get(key: K): V | undefined {
    const entry = this.cache.get(key);

    if (!entry) {
      return undefined;
    }

    // Check if entry has expired
    const age = Date.now() - entry.timestamp;
    if (age > this.config.ttl) {
      log.debug(`Cache entry expired for key: ${key} (age: ${age}ms, ttl: ${this.config.ttl}ms)`);
      this.cache.delete(key);
      return undefined;
    }

    return entry.data;
  }

  /**
   * Check if a key exists in the cache and hasn't expired
   */
  has(key: K): boolean {
    return this.get(key) !== undefined;
  }

  /**
   * Invalidate (remove) a specific cache entry
   */
  invalidate(key: K): void {
    this.cache.delete(key);
    log.debug(`Cache invalidated for key: ${key}`);
  }

  /**
   * Invalidate all cache entries
   */
  invalidateAll(): void {
    const size = this.cache.size;
    this.cache.clear();
    log.debug(`All cache entries invalidated (${size} entries cleared)`);
  }

  /**
   * Invalidate all expired entries
   */
  invalidateExpired(): void {
    const now = Date.now();
    let expiredCount = 0;

    for (const [key, entry] of this.cache.entries()) {
      const age = now - entry.timestamp;
      if (age > this.config.ttl) {
        this.cache.delete(key);
        expiredCount++;
      }
    }

    if (expiredCount > 0) {
      log.debug(`Invalidated ${expiredCount} expired cache entries`);
    }
  }

  /**
   * Get the age of a cache entry in milliseconds
   * Returns undefined if entry doesn't exist
   */
  getAge(key: K): number | undefined {
    const entry = this.cache.get(key);
    if (!entry) {
      return undefined;
    }
    return Date.now() - entry.timestamp;
  }

  /**
   * Get the oldest key in the cache
   */
  private getOldestKey(): K | undefined {
    let oldestKey: K | undefined;
    let oldestTimestamp = Infinity;

    for (const [key, entry] of this.cache.entries()) {
      if (entry.timestamp < oldestTimestamp) {
        oldestTimestamp = entry.timestamp;
        oldestKey = key;
      }
    }

    return oldestKey;
  }

  /**
   * Get cache statistics
   */
  getStats(): {
    size: number;
    maxSize: number | undefined;
    ttl: number;
    oldestEntryAge: number | undefined;
  } {
    const oldestKey = this.getOldestKey();
    const oldestEntryAge = oldestKey ? this.getAge(oldestKey) : undefined;

    return {
      size: this.cache.size,
      maxSize: this.config.maxSize,
      ttl: this.config.ttl,
      oldestEntryAge
    };
  }
}

/**
 * Setup Wizard status cache keys
 */
export type SetupWizardCacheKey =
  | 'dockerStatus'
  | 'homebrewStatus'
  | 'frpcStatus'
  | 'gitConfigStatus'
  | 'sshKeyStatus'
  | 'artifactoryStatus'
  | 'instCliStatus';

/**
 * Create a cache manager for setup wizard status data
 * Default TTL: 5 minutes (300000ms)
 */
export function createSetupWizardCache(ttl: number = 300000): CacheManager<SetupWizardCacheKey, any> {
  return new CacheManager<SetupWizardCacheKey, any>({
    ttl,
    maxSize: 10 // Limit to 10 entries
  });
}
