// 前端性能優化工具模組

/**
 * 防抖函數 - 防止過度頻繁的 API 調用
 */
export function debounce<T extends (...args: any[]) => any>(
  func: T,
  delay: number
): (...args: Parameters<T>) => void {
  let timeoutId: NodeJS.Timeout;

  return (...args: Parameters<T>) => {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => func.apply(null, args), delay);
  };
}

/**
 * 節流函數 - 限制函數執行頻率
 */
export function throttle<T extends (...args: any[]) => any>(
  func: T,
  limit: number
): (...args: Parameters<T>) => void {
  let inThrottle: boolean;

  return (...args: Parameters<T>) => {
    if (!inThrottle) {
      func.apply(null, args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

/**
 * 記憶化函數 - 快取函數執行結果
 */
export function memoize<T extends (...args: any[]) => any>(func: T): T {
  const cache = new Map();

  return ((...args: any[]) => {
    const key = JSON.stringify(args);
    if (cache.has(key)) {
      return cache.get(key);
    }

    const result = func.apply(null, args);
    cache.set(key, result);
    return result;
  }) as T;
}

/**
 * 簡單的本地快取實作
 */
export class LocalCache {
  private static instance: LocalCache;
  private cache = new Map<string, { data: any; expiry: number }>();

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

  set(key: string, value: any, ttlMs: number = 300000): void { // 預設5分鐘
    const expiry = Date.now() + ttlMs;
    this.cache.set(key, { data: value, expiry });
  }

  get<T>(key: string): T | null {
    const item = this.cache.get(key);

    if (!item) {
      return null;
    }

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

    return item.data as T;
  }

  has(key: string): boolean {
    const item = this.cache.get(key);
    if (!item) return false;

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

    return true;
  }

  clear(): void {
    this.cache.clear();
  }

  // 清理過期項目
  cleanup(): void {
    const now = Date.now();
    for (const [key, item] of this.cache) {
      if (now > item.expiry) {
        this.cache.delete(key);
      }
    }
  }
}

/**
 * API 請求快取包裝器
 */
export async function cachedApiCall<T>(
  key: string,
  apiCall: () => Promise<T>,
  ttlMs: number = 300000
): Promise<T> {
  const cache = LocalCache.getInstance();

  // 檢查快取
  const cached = cache.get<T>(key);
  if (cached) {
    console.log(`Cache hit for key: ${key}`);
    return cached;
  }

  // 執行 API 調用
  console.log(`Cache miss for key: ${key}, making API call`);
  const result = await apiCall();

  // 存入快取
  cache.set(key, result, ttlMs);

  return result;
}

/**
 * 生成快取鍵
 */
export function generateCacheKey(prefix: string, ...params: any[]): string {
  const paramString = params.map(p =>
    typeof p === 'object' ? JSON.stringify(p) : String(p)
  ).join('_');

  return `${prefix}_${paramString}`;
}

/**
 * 性能監控工具
 */
export class PerformanceMonitor {
  private static timers = new Map<string, number>();

  static start(label: string): void {
    this.timers.set(label, performance.now());
  }

  static end(label: string): number {
    const startTime = this.timers.get(label);
    if (!startTime) {
      console.warn(`No timer found for label: ${label}`);
      return 0;
    }

    const duration = performance.now() - startTime;
    this.timers.delete(label);

    console.log(`⏱️ ${label}: ${duration.toFixed(2)}ms`);
    return duration;
  }

  static measure<T>(label: string, fn: () => T): T {
    this.start(label);
    const result = fn();
    this.end(label);
    return result;
  }

  static async measureAsync<T>(label: string, fn: () => Promise<T>): Promise<T> {
    this.start(label);
    const result = await fn();
    this.end(label);
    return result;
  }
}

/**
 * 圖片懶加載 Hook 替代方案
 */
export function createIntersectionObserver(
  callback: (entry: IntersectionObserverEntry) => void,
  options?: IntersectionObserverInit
): IntersectionObserver {
  const defaultOptions: IntersectionObserverInit = {
    root: null,
    rootMargin: '50px',
    threshold: 0.1,
    ...options
  };

  return new IntersectionObserver((entries) => {
    entries.forEach(callback);
  }, defaultOptions);
}