/**
 * 💾 CacheService - Service de cache optimisé avec LRU et métriques
 * Remplace le cache basique du GlobalService
 */

import { Injectable, Inject, Optional } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { 
  CacheEntry, 
  CacheConfig, 
  CacheStats, 
  CacheStrategy, 
  CacheOptions 
} from '../interfaces/cache.interface';

export const CACHE_CONFIG = 'CACHE_CONFIG';

@Injectable({
  providedIn: 'root'
})
export class CacheService {
  private readonly cache = new Map<string, CacheEntry>();
  private readonly accessOrder: string[] = [];
  private readonly config: CacheConfig;
  private readonly stats = new BehaviorSubject<CacheStats>(this.initStats());
  private cleanupTimer?: number;

  constructor(
    @Optional() @Inject(CACHE_CONFIG) customConfig?: Partial<CacheConfig>
  ) {
    this.config = {
      defaultTtl: 30000, // 30 secondes (même que GlobalService)
      maxSize: 100,      // Augmenté de 50 à 100
      cleanupInterval: 60000, // 1 minute
      enableMetrics: true,
      ...customConfig
    };

    this.startCleanupTimer();
  }

  /**
   * Récupère une valeur du cache
   */
  get<T>(key: string): T | null {
    const entry = this.cache.get(key);
    
    if (!entry) {
      this.updateStats(stats => stats.missCount++);
      return null;
    }

    // Vérifier l'expiration
    if (this.isExpired(entry)) {
      this.cache.delete(key);
      this.removeFromAccessOrder(key);
      this.updateStats(stats => {
        stats.missCount++;
        stats.evictionCount++;
        stats.totalEntries--;
      });
      return null;
    }

    // Mettre à jour les statistiques d'accès
    entry.accessCount++;
    entry.lastAccessed = Date.now();
    this.updateAccessOrder(key);
    this.updateStats(stats => stats.hitCount++);

    return entry.data as T;
  }

  /**
   * Met une valeur en cache
   */
  set<T>(key: string, value: T, options: CacheOptions = {}): void {
    const now = Date.now();
    const ttl = options.ttl ?? this.config.defaultTtl;
    
    const entry: CacheEntry<T> = {
      data: value,
      timestamp: now,
      ttl,
      accessCount: 0,
      lastAccessed: now,
      key
    };

    // Vérifier si on dépasse la taille max
    if (!this.cache.has(key) && this.cache.size >= this.config.maxSize) {
      this.evictLeastRecentlyUsed();
    }

    const isUpdate = this.cache.has(key);
    this.cache.set(key, entry);
    
    if (!isUpdate) {
      this.accessOrder.push(key);
      this.updateStats(stats => stats.totalEntries++);
    }
    
    this.updateAccessOrder(key);
  }

  /**
   * Supprime une entrée du cache
   */
  delete(key: string): boolean {
    const existed = this.cache.delete(key);
    if (existed) {
      this.removeFromAccessOrder(key);
      this.updateStats(stats => stats.totalEntries--);
    }
    return existed;
  }

  /**
   * Vide complètement le cache
   */
  clear(): void {
    const count = this.cache.size;
    this.cache.clear();
    this.accessOrder.length = 0;
    this.updateStats(stats => {
      stats.totalEntries = 0;
      stats.evictionCount += count;
    });
  }

  /**
   * Invalide toutes les entrées correspondant à un pattern
   */
  invalidatePattern(pattern: string | RegExp): number {
    const regex = typeof pattern === 'string' ? new RegExp(pattern) : pattern;
    const keysToDelete: string[] = [];
    
    for (const key of this.cache.keys()) {
      if (regex.test(key)) {
        keysToDelete.push(key);
      }
    }
    
    keysToDelete.forEach(key => this.delete(key));
    return keysToDelete.length;
  }

  /**
   * Invalide les entrées avec des tags spécifiques
   */
  invalidateByTags(tags: string[]): number {
    // À implémenter si on ajoute le support des tags
    return 0;
  }

  /**
   * Retourne les statistiques du cache
   */
  getStats(): Observable<CacheStats> {
    return this.stats.asObservable();
  }

  /**
   * Retourne un snapshot des statistiques actuelles
   */
  getCurrentStats(): CacheStats {
    return { ...this.stats.value };
  }

  /**
   * Force un nettoyage du cache (expire + LRU)
   */
  cleanup(): number {
    let cleaned = 0;
    
    // Nettoyer les entrées expirées
    for (const [key, entry] of this.cache.entries()) {
      if (this.isExpired(entry)) {
        this.cache.delete(key);
        this.removeFromAccessOrder(key);
        cleaned++;
      }
    }
    
    this.updateStats(stats => {
      stats.totalEntries = this.cache.size;
      stats.evictionCount += cleaned;
    });
    
    return cleaned;
  }

  /**
   * Génère une clé de cache standardisée
   */
  generateKey(...parts: (string | number | object)[]): string {
    return parts
      .map(part => {
        if (typeof part === 'object') {
          return JSON.stringify(part);
        }
        return String(part);
      })
      .join('::');
  }

  /**
   * Vérifie si une entrée est expirée
   */
  private isExpired(entry: CacheEntry): boolean {
    return Date.now() > (entry.timestamp + entry.ttl);
  }

  /**
   * Supprime l'entrée la moins récemment utilisée
   */
  private evictLeastRecentlyUsed(): void {
    if (this.accessOrder.length === 0) return;
    
    const lruKey = this.accessOrder[0];
    this.cache.delete(lruKey);
    this.accessOrder.shift();
    this.updateStats(stats => {
      stats.totalEntries--;
      stats.evictionCount++;
    });
  }

  /**
   * Met à jour l'ordre d'accès pour LRU
   */
  private updateAccessOrder(key: string): void {
    const index = this.accessOrder.indexOf(key);
    if (index > -1) {
      this.accessOrder.splice(index, 1);
    }
    this.accessOrder.push(key);
  }

  /**
   * Supprime une clé de l'ordre d'accès
   */
  private removeFromAccessOrder(key: string): void {
    const index = this.accessOrder.indexOf(key);
    if (index > -1) {
      this.accessOrder.splice(index, 1);
    }
  }

  /**
   * Lance le timer de nettoyage automatique
   */
  private startCleanupTimer(): void {
    if (!this.config.enableMetrics) return;
    
    this.cleanupTimer = window.setInterval(() => {
      this.cleanup();
    }, this.config.cleanupInterval);
  }

  /**
   * Initialise les statistiques
   */
  private initStats(): CacheStats {
    return {
      totalEntries: 0,
      hitCount: 0,
      missCount: 0,
      evictionCount: 0,
      hitRatio: 0,
      memoryUsage: 0
    };
  }

  /**
   * Met à jour les statistiques
   */
  private updateStats(updater: (stats: CacheStats) => void): void {
    if (!this.config.enableMetrics) return;
    
    const currentStats = { ...this.stats.value };
    updater(currentStats);
    
    // Calculer le ratio de hit
    const totalRequests = currentStats.hitCount + currentStats.missCount;
    currentStats.hitRatio = totalRequests > 0 
      ? Math.round((currentStats.hitCount / totalRequests) * 100) / 100 
      : 0;
    
    // Estimer l'usage mémoire (approximatif)
    currentStats.memoryUsage = this.cache.size * 1024; // Estimation basique
    
    this.stats.next(currentStats);
  }

  /**
   * Nettoyage lors de la destruction du service
   */
  ngOnDestroy(): void {
    if (this.cleanupTimer) {
      clearInterval(this.cleanupTimer);
    }
    this.clear();
    this.stats.complete();
  }
}