/**
 * Cache Service for storing API responses in localStorage
 * Provides automatic expiration and efficient data management
 */
import { isLocalStorageEnabled } from './storagePolicy';

interface CacheItem<T> {
  data: T;
  timestamp: number;
  ttl: number; // Time to live in milliseconds
}

interface CacheOptions {
  ttl?: number; // Default 5 minutes
  maxItems?: number; // Maximum items in cache
}

class CacheService {
  private static instance: CacheService;
  private readonly defaultTTL = 5 * 60 * 1000; // 5 minutes
  private readonly maxItems = 100;
  private readonly prefix = 'cache_';

  private constructor() {}

  static getInstance(): CacheService {
    if (!CacheService.instance) {
      CacheService.instance = new CacheService();
    }
    return CacheService.instance;
  }

  /**
   * Store data in cache with TTL
   */
  set<T>(key: string, data: T, options: CacheOptions = {}): void {
    try {
      if (!isLocalStorageEnabled()) {
        return; // global disabled
      }
      // Skip only if persistence disabled OR policy forbids type
      if (key.startsWith('products') || key.startsWith('product_') || key.startsWith('categories') || key.startsWith('home_page_data') || key.startsWith('featured_')) {
        // When enabled we still allow caching these; comment out skip.
        // (If you want to block even when enabled, re-add return.)
      }
      const ttl = options.ttl || this.defaultTTL;
      const cacheItem: CacheItem<T> = {
        data,
        timestamp: Date.now(),
        ttl,
      };

      localStorage.setItem(this.prefix + key, JSON.stringify(cacheItem));
      this.cleanupOldEntries();
    } catch (error) {
      console.warn('Failed to store in cache:', error);
    }
  }

  /**
   * Retrieve data from cache if still valid
   */
  get<T>(key: string): T | null {
    try {
      if (!isLocalStorageEnabled()) {
        return null; // disabled globally
      }
      // Skip retrieval if persistence disabled OR policy forbids type
      if (key.startsWith('products') || key.startsWith('product_') || key.startsWith('categories') || key.startsWith('home_page_data') || key.startsWith('featured_')) {
        return null; // never retrieve since we didn't store
      }
      const item = localStorage.getItem(this.prefix + key);
      if (!item) return null;

      const cacheItem: CacheItem<T> = JSON.parse(item);
      const now = Date.now();

      // Check if item has expired
      if (now - cacheItem.timestamp > cacheItem.ttl) {
        this.delete(key);
        return null;
      }

      return cacheItem.data;
    } catch (error) {
      console.warn('Failed to retrieve from cache:', error);
      return null;
    }
  }

  /**
   * Delete specific cache entry
   */
  delete(key: string): void {
    try {
      localStorage.removeItem(this.prefix + key);
    } catch (error) {
      console.warn('Failed to delete from cache:', error);
    }
  }

  /**
   * Clear all cache entries
   */
  clear(): void {
    try {
      const keys = Object.keys(localStorage);
      keys.forEach(key => {
        if (key.startsWith(this.prefix)) {
          localStorage.removeItem(key);
        }
      });
    } catch (error) {
      console.warn('Failed to clear cache:', error);
    }
  }

  /**
   * Check if data exists and is valid in cache
   */
  has(key: string): boolean {
    return this.get(key) !== null;
  }

  /**
   * Get cache statistics
   */
  getStats(): { totalItems: number; totalSize: number } {
    let totalItems = 0;
    let totalSize = 0;

    try {
      const keys = Object.keys(localStorage);
      keys.forEach(key => {
        if (key.startsWith(this.prefix)) {
          totalItems++;
          totalSize += localStorage.getItem(key)?.length || 0;
        }
      });
    } catch (error) {
      console.warn('Failed to get cache stats:', error);
    }

    return { totalItems, totalSize };
  }

  /**
   * Remove expired entries and enforce max items limit
   */
  private cleanupOldEntries(): void {
    try {
      const keys = Object.keys(localStorage);
      const cacheKeys = keys.filter(key => key.startsWith(this.prefix));
      const now = Date.now();
      const validEntries: { key: string; timestamp: number }[] = [];

      // Remove expired entries and collect valid ones
      cacheKeys.forEach(key => {
        try {
          const item = localStorage.getItem(key);
          if (item) {
            const cacheItem: CacheItem<unknown> = JSON.parse(item);
            if (now - cacheItem.timestamp > cacheItem.ttl) {
              localStorage.removeItem(key);
            } else {
              validEntries.push({ key, timestamp: cacheItem.timestamp });
            }
          }
        } catch {
          localStorage.removeItem(key);
        }
      });

      // If we still have too many items, remove oldest ones
      if (validEntries.length > this.maxItems) {
        validEntries
          .sort((a, b) => a.timestamp - b.timestamp)
          .slice(0, validEntries.length - this.maxItems)
          .forEach(entry => localStorage.removeItem(entry.key));
      }
    } catch (error) {
      console.warn('Failed to cleanup cache:', error);
    }
  }

  /**
   * Get or set pattern - useful for hooks
   */
  async getOrSet<T>(
    key: string, 
    fetcher: () => Promise<T>, 
    options: CacheOptions = {}
  ): Promise<T> {
    const cached = this.get<T>(key);
    if (cached !== null) {
      return cached;
    }

    const data = await fetcher();
    this.set(key, data, options);
    return data;
  }
}

export const cache = CacheService.getInstance();

// Cache key generators
export const cacheKeys = {
  products: (params?: { 
    search?: string; 
    category_id?: number; 
    page?: number;
    sort_by?: string;
    sort_order?: string;
  }) => {
    const queryParams = new URLSearchParams();
    if (params?.search) queryParams.append('search', params.search);
    if (params?.category_id) queryParams.append('category_id', params.category_id.toString());
    if (params?.page) queryParams.append('page', params.page.toString());
    if (params?.sort_by) queryParams.append('sort_by', params.sort_by);
    if (params?.sort_order) queryParams.append('sort_order', params.sort_order);
    return `products_${queryParams.toString()}`;
  },
  categories: () => 'categories',
  product: (slug: string) => `product_${slug}`,
  homePageData: () => 'home_page_data',
  featuredProducts: (categoryId?: number) => `featured_${categoryId || 'all'}`,
};

// Cache TTL constants
export const cacheTTL = {
  products: 5 * 60 * 1000, // 5 minutes
  categories: 15 * 60 * 1000, // 15 minutes - categories change less frequently
  homePageData: 3 * 60 * 1000, // 3 minutes for home page
  product: 10 * 60 * 1000, // 10 minutes for individual products
};
