/**
 * @file Canvas 对象池
 * @description 提供 Canvas 元素的复用机制，减少内存分配和 GC 压力
 * @module utils/canvas-pool
 */

/**
 * Canvas 池条目
 */
interface CanvasPoolItem {
  /** Canvas 元素 */
  canvas: HTMLCanvasElement;
  /** Canvas 2D 上下文 */
  context: CanvasRenderingContext2D;
  /** 是否正在使用 */
  inUse: boolean;
  /** 最近使用时间戳 */
  lastUsed: number;
  /** Canvas 尺寸标识 */
  sizeKey: string;
}

/**
 * Canvas 对象池
 *
 * 复用 Canvas 元素，避免频繁创建和销毁导致的内存抖动
 *
 * @example
 * ```typescript
 * const pool = CanvasPool.getInstance();
 * const { canvas, context } = pool.acquire(100, 200);
 * // 使用 canvas 进行绘制...
 * pool.release(canvas);
 * ```
 */
export class CanvasPool {
  /** 单例实例 */
  private static instance: CanvasPool | null = null;

  /** Canvas 池存储 */
  private pool: Map<string, CanvasPoolItem[]> = new Map();

  /** 已借出的 Canvas */
  private borrowed: Map<HTMLCanvasElement, CanvasPoolItem> = new Map();

  /** 最大池大小（每个尺寸） */
  private maxPoolSize: number = 4;

  /** Canvas 尺寸容差（允许一定范围的尺寸复用） */
  private sizeTolerance: number = 10;

  /**
   * 获取单例实例
   */
  public static getInstance(): CanvasPool {
    if (!CanvasPool.instance) {
      CanvasPool.instance = new CanvasPool();
    }
    return CanvasPool.instance;
  }

  /**
   * 重置单例实例（主要用于测试）
   */
  public static resetInstance(): void {
    if (CanvasPool.instance) {
      CanvasPool.instance.dispose();
      CanvasPool.instance = null;
    }
  }

  /**
   * 私有构造函数
   */
  private constructor() {
    // 页面卸载前清理
    if (typeof window !== 'undefined') {
      window.addEventListener('beforeunload', () => this.dispose());
    }
  }

  /**
   * 生成尺寸键
   * @param width 宽度
   * @param height 高度
   */
  private getSizeKey(width: number, height: number): string {
    return `${width}x${height}`;
  }

  /**
   * 查找匹配的尺寸键（考虑容差）
   * @param width 宽度
   * @param height 高度
   */
  private findMatchingSizeKey(width: number, height: number): string | null {
    for (const [key, items] of this.pool.entries()) {
      const [w, h] = key.split('x').map(Number);
      if (Math.abs(w - width) <= this.sizeTolerance &&
          Math.abs(h - height) <= this.sizeTolerance) {
        // 找到可用的
        const available = items.filter(item => !item.inUse);
        if (available.length > 0) {
          return key;
        }
      }
    }
    return null;
  }

  /**
   * 从池中获取 Canvas
   *
   * @param width  宽度
   * @param height 高度
   * @returns Canvas 和其上下文
   */
  public acquire(width: number, height: number): {
    canvas: HTMLCanvasElement;
    context: CanvasRenderingContext2D;
  } {
    // 先尝试精确匹配
    let sizeKey = this.getSizeKey(width, height);
    let items = this.pool.get(sizeKey);

    // 如果没有精确匹配，尝试模糊匹配
    if (!items || items.every(item => item.inUse)) {
      const matchedKey = this.findMatchingSizeKey(width, height);
      if (matchedKey) {
        sizeKey = matchedKey;
        items = this.pool.get(sizeKey);
      }
    }

    // 如果没有可用的，创建一个新的
    if (!items || items.every(item => item.inUse)) {
      const canvas = document.createElement('canvas');
      canvas.width = width;
      canvas.height = height;
      const context = canvas.getContext('2d')!;

      const item: CanvasPoolItem = {
        canvas,
        context,
        inUse: true,
        lastUsed: Date.now(),
        sizeKey: this.getSizeKey(width, height)
      };

      // 如果池已满，移除最老的
      if (!items) {
        items = [];
        this.pool.set(sizeKey, items);
      } else if (items.length >= this.maxPoolSize) {
        // 找到最老的未使用项并移除
        let oldestIdx = 0;
        let oldestTime = Infinity;
        items.forEach((item, idx) => {
          if (!item.inUse && item.lastUsed < oldestTime) {
            oldestTime = item.lastUsed;
            oldestIdx = idx;
          }
        });
        const removed = items.splice(oldestIdx, 1)[0];
        this.borrowed.delete(removed.canvas);
      }

      items.push(item);
      this.borrowed.set(canvas, item);

      return { canvas, context };
    }

    // 找到一个空闲的
    const available = items.find(item => !item.inUse)!;
    available.inUse = true;
    available.lastUsed = Date.now();

    // 如果尺寸变化，更新 canvas
    if (available.canvas.width !== width || available.canvas.height !== height) {
      available.canvas.width = width;
      available.canvas.height = height;
      available.sizeKey = sizeKey;
    }

    // 清除之前的上下文状态
    available.context.setTransform(1, 0, 0, 1, 0, 0);
    available.context.clearRect(0, 0, width, height);

    this.borrowed.set(available.canvas, available);

    return { canvas: available.canvas, context: available.context };
  }

  /**
   * 释放 Canvas 回池中
   *
   * @param canvas 要释放的 Canvas
   */
  public release(canvas: HTMLCanvasElement): void {
    const item = this.borrowed.get(canvas);
    if (!item) {
      // 不属于我们管理的 Canvas，忽略
      return;
    }

    item.inUse = false;
    item.lastUsed = Date.now();
    this.borrowed.delete(canvas);
  }

  /**
   * 批量释放所有借出的 Canvas
   */
  public releaseAll(): void {
    for (const [, item] of this.borrowed) {
      item.inUse = false;
      item.lastUsed = Date.now();
    }
    this.borrowed.clear();
  }

  /**
   * 预热池（预创建指定尺寸的 Canvas）
   *
   * @param sizes 尺寸数组，每项为 [width, height]
   */
  public warmup(sizes: Array<[number, number]>): void {
    for (const [width, height] of sizes) {
      this.acquire(width, height);
      // 立即释放，让它们进入池中
      const sizeKey = this.getSizeKey(width, height);
      const items = this.pool.get(sizeKey);
      if (items && items.length > 0) {
        const item = items[items.length - 1];
        item.inUse = false;
        this.borrowed.delete(item.canvas);
      }
    }
  }

  /**
   * 获取池统计信息
   */
  public getStats(): {
    totalItems: number;
    borrowedCount: number;
    poolSizes: Record<string, { total: number; available: number }>;
  } {
    let totalItems = 0;
    let borrowedCount = 0;
    const poolSizes: Record<string, { total: number; available: number }> = {};

    for (const [key, items] of this.pool.entries()) {
      totalItems += items.length;
      borrowedCount += items.filter(i => i.inUse).length;
      poolSizes[key] = {
        total: items.length,
        available: items.filter(i => !i.inUse).length
      };
    }

    return { totalItems, borrowedCount, poolSizes };
  }

  /**
   * 清理并释放所有资源
   */
  public dispose(): void {
    this.pool.clear();
    this.borrowed.clear();
  }
}
