/**
 * PerformanceOptimizer - Optimisations de performance
 * 
 * Responsabilités:
 * - Lazy loading pour documents volumineux
 * - Debouncing pour recherche textuelle
 * - Virtual scrolling pour grandes listes
 * - Optimisation du nombre de re-renders
 * 
 * Validates: Requirements 12.1, 12.2, 12.3, 12.6
 */
class PerformanceOptimizer {
  constructor() {
    this.debounceTimers = {};
    this.throttleTimers = {};
  }

  /**
   * Debounce une fonction
   * Validates: Requirements 12.2
   */
  debounce(fn, delay = 300, key = 'default') {
    return (...args) => {
      if (this.debounceTimers[key]) {
        clearTimeout(this.debounceTimers[key]);
      }
      
      this.debounceTimers[key] = setTimeout(() => {
        fn.apply(this, args);
        delete this.debounceTimers[key];
      }, delay);
    };
  }

  /**
   * Throttle une fonction
   */
  throttle(fn, delay = 100, key = 'default') {
    return (...args) => {
      if (this.throttleTimers[key]) {
        return;
      }
      
      this.throttleTimers[key] = setTimeout(() => {
        fn.apply(this, args);
        delete this.throttleTimers[key];
      }, delay);
    };
  }

  /**
   * Lazy loading pour documents volumineux
   * Validates: Requirements 12.1, 12.3
   */
  async lazyLoadDocument(documentId, apiClient, chunkSize = 100000) {
    const document = await apiClient.getDocument(documentId);
    
    // Si le document est petit, le retourner directement
    if (!document.content || document.content.length < chunkSize) {
      return document;
    }
    
    // Sinon, charger par morceaux
    const chunks = this.splitIntoChunks(document.content, chunkSize);
    
    // Retourner le premier morceau immédiatement
    const firstChunk = {
      ...document,
      content: chunks[0],
      isPartial: true,
      totalChunks: chunks.length,
      currentChunk: 1
    };
    
    // Charger les autres morceaux progressivement
    this.loadRemainingChunks(documentId, chunks, firstChunk);
    
    return firstChunk;
  }

  /**
   * Diviser en morceaux
   */
  splitIntoChunks(content, chunkSize) {
    const chunks = [];
    for (let i = 0; i < content.length; i += chunkSize) {
      chunks.push(content.slice(i, i + chunkSize));
    }
    return chunks;
  }

  /**
   * Charger les morceaux restants progressivement
   */
  async loadRemainingChunks(documentId, chunks, document) {
    for (let i = 1; i < chunks.length; i++) {
      // Attendre un peu entre chaque morceau
      await this.sleep(100);
      
      // Ajouter le morceau au document
      document.content += chunks[i];
      document.currentChunk = i + 1;
      
      // Notifier le changement
      if (window.stateManager) {
        window.stateManager.emit('documentChunkLoaded', {
          documentId,
          chunk: i + 1,
          total: chunks.length
        });
      }
    }
    
    // Marquer comme complet
    document.isPartial = false;
  }

  /**
   * Virtual scrolling pour grandes listes
   * Validates: Requirements 12.6
   */
  createVirtualScroller(container, items, renderItem, itemHeight = 50) {
    const visibleCount = Math.ceil(container.clientHeight / itemHeight);
    const bufferCount = 5;
    
    let scrollTop = 0;
    let startIndex = 0;
    let endIndex = visibleCount + bufferCount;
    
    const render = () => {
      // Calculer les indices visibles
      startIndex = Math.max(0, Math.floor(scrollTop / itemHeight) - bufferCount);
      endIndex = Math.min(items.length, startIndex + visibleCount + bufferCount * 2);
      
      // Créer le contenu
      const content = document.createElement('div');
      content.style.position = 'relative';
      content.style.height = `${items.length * itemHeight}px`;
      
      // Rendre uniquement les éléments visibles
      for (let i = startIndex; i < endIndex; i++) {
        const itemElement = renderItem(items[i], i);
        itemElement.style.position = 'absolute';
        itemElement.style.top = `${i * itemHeight}px`;
        itemElement.style.height = `${itemHeight}px`;
        content.appendChild(itemElement);
      }
      
      // Remplacer le contenu
      container.innerHTML = '';
      container.appendChild(content);
    };
    
    // Écouter le scroll
    container.addEventListener('scroll', this.throttle(() => {
      scrollTop = container.scrollTop;
      render();
    }, 50, 'virtualScroll'));
    
    // Rendre initial
    render();
    
    return {
      update: (newItems) => {
        items = newItems;
        render();
      },
      destroy: () => {
        container.removeEventListener('scroll', render);
      }
    };
  }

  /**
   * Optimiser le nombre de re-renders
   */
  shouldUpdate(oldProps, newProps) {
    // Comparaison superficielle
    const oldKeys = Object.keys(oldProps);
    const newKeys = Object.keys(newProps);
    
    if (oldKeys.length !== newKeys.length) {
      return true;
    }
    
    for (const key of oldKeys) {
      if (oldProps[key] !== newProps[key]) {
        return true;
      }
    }
    
    return false;
  }

  /**
   * Memoization simple
   */
  memoize(fn) {
    const cache = new Map();
    
    return (...args) => {
      const key = JSON.stringify(args);
      
      if (cache.has(key)) {
        return cache.get(key);
      }
      
      const result = fn.apply(this, args);
      cache.set(key, result);
      
      // Limiter la taille du cache
      if (cache.size > 100) {
        const firstKey = cache.keys().next().value;
        cache.delete(firstKey);
      }
      
      return result;
    };
  }

  /**
   * Mesurer les performances
   */
  measurePerformance(name, fn) {
    const start = performance.now();
    const result = fn();
    const end = performance.now();
    
    console.log(`[Performance] ${name}: ${(end - start).toFixed(2)}ms`);
    
    return result;
  }

  /**
   * Mesurer les performances async
   */
  async measurePerformanceAsync(name, fn) {
    const start = performance.now();
    const result = await fn();
    const end = performance.now();
    
    console.log(`[Performance] ${name}: ${(end - start).toFixed(2)}ms`);
    
    return result;
  }

  /**
   * Attendre un délai
   */
  sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  /**
   * RequestAnimationFrame wrapper
   */
  requestFrame(fn) {
    if (typeof requestAnimationFrame !== 'undefined') {
      return requestAnimationFrame(fn);
    }
    return setTimeout(fn, 16);
  }

  /**
   * Batch updates
   */
  batchUpdates(updates) {
    this.requestFrame(() => {
      updates.forEach(update => update());
    });
  }
}

// Export pour utilisation dans d'autres modules
if (typeof module !== 'undefined' && module.exports) {
  module.exports = PerformanceOptimizer;
}
