/**
 * Утилиты производительности
 */

/**
 * Debounce - откладывает выполнение функции до тех пор, 
 * пока не пройдет delay мс с последнего вызова
 */
export function debounce(func, delay, immediate = false) {
  let timeoutId;
  
  return function executedFunction(...args) {
    const callNow = immediate && !timeoutId;
    
    clearTimeout(timeoutId);
    
    timeoutId = setTimeout(() => {
      timeoutId = null;
      if (!immediate) func.apply(this, args);
    }, delay);
    
    if (callNow) func.apply(this, args);
  };
}

/**
 * Throttle - ограничивает выполнение функции одним разом в delay мс
 */
export function throttle(func, delay) {
  let timeoutId;
  let lastExecTime = 0;
  
  return function executedFunction(...args) {
    const currentTime = Date.now();
    
    if (currentTime - lastExecTime > delay) {
      func.apply(this, args);
      lastExecTime = currentTime;
    } else {
      clearTimeout(timeoutId);
      timeoutId = setTimeout(() => {
        func.apply(this, args);
        lastExecTime = Date.now();
      }, delay - (currentTime - lastExecTime));
    }
  };
}

/**
 * RequestAnimationFrame throttle
 */
export function rafThrottle(func) {
  let rafId;
  
  return function executedFunction(...args) {
    if (rafId) return;
    
    rafId = requestAnimationFrame(() => {
      func.apply(this, args);
      rafId = null;
    });
  };
}

/**
 * Мемоизация функции
 */
export function memoize(func, keyGenerator = (...args) => JSON.stringify(args)) {
  const cache = new Map();
  
  return function memoizedFunction(...args) {
    const key = keyGenerator(...args);
    
    if (cache.has(key)) {
      return cache.get(key);
    }
    
    const result = func.apply(this, args);
    cache.set(key, result);
    
    return result;
  };
}

/**
 * Ленивая загрузка функции
 */
export function lazy(importFunction) {
  let modulePromise = null;
  
  return function lazyFunction(...args) {
    if (!modulePromise) {
      modulePromise = importFunction();
    }
    
    return modulePromise.then(module => {
      const func = module.default || module;
      return func(...args);
    });
  };
}

/**
 * Пул воркеров для выполнения тяжелых вычислений
 */
export class WorkerPool {
  constructor(workerScript, poolSize = navigator.hardwareConcurrency || 4) {
    this.workerScript = workerScript;
    this.poolSize = poolSize;
    this.workers = [];
    this.queue = [];
    this.activeJobs = 0;
    
    this.initWorkers();
  }
  
  initWorkers() {
    for (let i = 0; i < this.poolSize; i++) {
      const worker = new Worker(this.workerScript);
      worker.isAvailable = true;
      this.workers.push(worker);
    }
  }
  
  execute(data) {
    return new Promise((resolve, reject) => {
      const job = { data, resolve, reject };
      
      const availableWorker = this.workers.find(w => w.isAvailable);
      
      if (availableWorker) {
        this.runJob(availableWorker, job);
      } else {
        this.queue.push(job);
      }
    });
  }
  
  runJob(worker, job) {
    worker.isAvailable = false;
    this.activeJobs++;
    
    const handleMessage = (event) => {
      worker.removeEventListener('message', handleMessage);
      worker.removeEventListener('error', handleError);
      
      worker.isAvailable = true;
      this.activeJobs--;
      
      job.resolve(event.data);
      this.processQueue();
    };
    
    const handleError = (error) => {
      worker.removeEventListener('message', handleMessage);
      worker.removeEventListener('error', handleError);
      
      worker.isAvailable = true;
      this.activeJobs--;
      
      job.reject(error);
      this.processQueue();
    };
    
    worker.addEventListener('message', handleMessage);
    worker.addEventListener('error', handleError);
    worker.postMessage(job.data);
  }
  
  processQueue() {
    if (this.queue.length === 0) return;
    
    const availableWorker = this.workers.find(w => w.isAvailable);
    if (availableWorker) {
      const job = this.queue.shift();
      this.runJob(availableWorker, job);
    }
  }
  
  terminate() {
    this.workers.forEach(worker => worker.terminate());
    this.workers = [];
    this.queue = [];
  }
}

/**
 * Измерение производительности
 */
export class PerformanceMonitor {
  constructor() {
    this.marks = new Map();
    this.measures = new Map();
  }
  
  mark(name) {
    const timestamp = performance.now();
    this.marks.set(name, timestamp);
    
    if (performance.mark) {
      performance.mark(name);
    }
    
    return timestamp;
  }
  
  measure(name, startMark, endMark = null) {
    const startTime = this.marks.get(startMark);
    const endTime = endMark ? this.marks.get(endMark) : performance.now();
    
    if (startTime === undefined) {
      console.warn(`Performance mark "${startMark}" not found`);
      return null;
    }
    
    if (endMark && endTime === undefined) {
      console.warn(`Performance mark "${endMark}" not found`);
      return null;
    }
    
    const duration = endTime - startTime;
    this.measures.set(name, duration);
    
    // Безопасная попытка создать браузерную метрику
    try {
      if (performance.measure) {
        if (endMark) {
          performance.measure(name, startMark, endMark);
        } else {
          performance.measure(name, startMark);
        }
      }
    } catch (error) {
      // Игнорируем ошибки браузерных метрик
      console.warn(`Failed to create browser performance measure: ${error.message}`);
    }
    
    return duration;
  }
  
  getMeasure(name) {
    return this.measures.get(name);
  }
  
  getAllMeasures() {
    return new Map(this.measures);
  }
  
  clear() {
    this.marks.clear();
    this.measures.clear();
    
    if (performance.clearMarks) {
      performance.clearMarks();
    }
    
    if (performance.clearMeasures) {
      performance.clearMeasures();
    }
  }
  
  report() {
    console.group('Performance Report');
    this.measures.forEach((duration, name) => {
      console.log(`${name}: ${duration.toFixed(2)}ms`);
    });
    console.groupEnd();
  }
}

/**
 * Глобальный экземпляр монитора производительности
 */
export const performanceMonitor = new PerformanceMonitor();

/**
 * Декоратор для измерения времени выполнения функции
 */
export function measureTime(name) {
  return function(target, propertyName, descriptor) {
    const method = descriptor.value;
    
    descriptor.value = function(...args) {
      const startTime = performance.now();
      const result = method.apply(this, args);
      const endTime = performance.now();
      
      console.log(`${name || propertyName}: ${(endTime - startTime).toFixed(2)}ms`);
      
      return result;
    };
    
    return descriptor;
  };
}

/**
 * Батчинг операций DOM
 */
export class DOMBatcher {
  constructor() {
    this.readOperations = [];
    this.writeOperations = [];
    this.isScheduled = false;
  }
  
  read(callback) {
    this.readOperations.push(callback);
    this.schedule();
  }
  
  write(callback) {
    this.writeOperations.push(callback);
    this.schedule();
  }
  
  schedule() {
    if (this.isScheduled) return;
    
    this.isScheduled = true;
    requestAnimationFrame(() => {
      this.flush();
    });
  }
  
  flush() {
    // Сначала выполняем все read операции
    this.readOperations.forEach(callback => callback());
    this.readOperations = [];
    
    // Затем все write операции
    this.writeOperations.forEach(callback => callback());
    this.writeOperations = [];
    
    this.isScheduled = false;
  }
}

/**
 * Глобальный батчер DOM операций
 */
export const domBatcher = new DOMBatcher();

/**
 * Intersection Observer для lazy loading
 */
export class LazyLoader {
  constructor(options = {}) {
    this.options = {
      rootMargin: '50px',
      threshold: 0.1,
      ...options
    };
    
    this.observer = new IntersectionObserver(
      this.handleIntersection.bind(this),
      this.options
    );
    
    this.callbacks = new Map();
  }
  
  observe(element, callback) {
    this.callbacks.set(element, callback);
    this.observer.observe(element);
  }
  
  unobserve(element) {
    this.callbacks.delete(element);
    this.observer.unobserve(element);
  }
  
  handleIntersection(entries) {
    entries.forEach(entry => {
      if (entry.isIntersecting) {
        const callback = this.callbacks.get(entry.target);
        if (callback) {
          callback(entry.target);
          this.unobserve(entry.target);
        }
      }
    });
  }
  
  disconnect() {
    this.observer.disconnect();
    this.callbacks.clear();
  }
}

/**
 * Создание Web Worker из функции
 */
export function createWorkerFromFunction(func) {
  const blob = new Blob([`(${func})()`], { type: 'application/javascript' });
  return new Worker(URL.createObjectURL(blob));
}