/**
 * Cache Management for Analytics
 * Provides caching for expensive analytics calculations
 */

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

class AnalyticsCache {
  private cache: Map<string, CacheEntry<any>> = new Map();
  private defaultTTL: number = 5 * 60 * 1000; // 5 minutes

  /**
   * Get cached data
   */
  get<T>(key: string): T | null {
    const entry = this.cache.get(key);
    if (!entry) return null;

    if (Date.now() > entry.expiresAt) {
      this.cache.delete(key);
      return null;
    }

    return entry.data as T;
  }

  /**
   * Set cached data
   */
  set<T>(key: string, data: T, ttlMs: number = this.defaultTTL): void {
    const now = Date.now();
    this.cache.set(key, {
      data,
      timestamp: now,
      expiresAt: now + ttlMs,
    });
  }

  /**
   * Check if key exists and is valid
   */
  has(key: string): boolean {
    const entry = this.cache.get(key);
    if (!entry) return false;

    if (Date.now() > entry.expiresAt) {
      this.cache.delete(key);
      return false;
    }

    return true;
  }

  /**
   * Delete a specific key
   */
  delete(key: string): boolean {
    return this.cache.delete(key);
  }

  /**
   * Clear all cached data
   */
  clear(): void {
    this.cache.clear();
  }

  /**
   * Clear expired entries
   */
  cleanup(): number {
    const now = Date.now();
    let cleaned = 0;

    for (const [key, entry] of this.cache.entries()) {
      if (now > entry.expiresAt) {
        this.cache.delete(key);
        cleaned++;
      }
    }

    return cleaned;
  }

  /**
   * Get cache statistics
   */
  getStats(): {
    size: number;
    validEntries: number;
    expiredEntries: number;
  } {
    const now = Date.now();
    let valid = 0;
    let expired = 0;

    for (const entry of this.cache.values()) {
      if (now > entry.expiresAt) {
        expired++;
      } else {
        valid++;
      }
    }

    return {
      size: this.cache.size,
      validEntries: valid,
      expiredEntries: expired,
    };
  }

  /**
   * Generate cache key from object
   */
  static generateKey(prefix: string, params: Record<string, any>): string {
    const sortedParams = Object.keys(params)
      .sort()
      .map((key) => `${key}:${JSON.stringify(params[key])}`)
      .join('|');
    return `${prefix}:${sortedParams}`;
  }
}

// Singleton instance
export const analyticsCache = new AnalyticsCache();

/**
 * Cache decorator for async functions
 */
export function cached<T>(
  keyGenerator: (...args: any[]) => string,
  ttlMs: number = 5 * 60 * 1000
) {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    const originalMethod = descriptor.value;

    descriptor.value = async function (...args: any[]) {
      const cacheKey = keyGenerator(...args);
      const cached = analyticsCache.get<T>(cacheKey);

      if (cached !== null) {
        return cached;
      }

      const result = await originalMethod.apply(this, args);
      analyticsCache.set(cacheKey, result, ttlMs);
      return result;
    };

    return descriptor;
  };
}

/**
 * Higher-order function for caching async functions
 */
export function withCache<T, A extends any[]>(
  fn: (...args: A) => Promise<T>,
  keyGenerator: (...args: A) => string,
  ttlMs: number = 5 * 60 * 1000
): (...args: A) => Promise<T> {
  return async (...args: A): Promise<T> => {
    const cacheKey = keyGenerator(...args);
    const cached = analyticsCache.get<T>(cacheKey);

    if (cached !== null) {
      return cached;
    }

    const result = await fn(...args);
    analyticsCache.set(cacheKey, result, ttlMs);
    return result;
  };
}

// Schedule periodic cleanup
if (typeof setInterval !== 'undefined') {
  setInterval(() => {
    analyticsCache.cleanup();
  }, 60 * 1000); // Run cleanup every minute
}
