export interface PooledCanvas {
  canvas: HTMLCanvasElement;
  ctx: CanvasRenderingContext2D;
  inUse: boolean;
  lastUsed: number;
}

export class CanvasPool {
  private static pool: PooledCanvas[] = [];
  private static maxPoolSize = 20;
  private static readonly MIN_CANVAS_SIZE = 256;
  private static readonly MAX_CANVAS_SIZE = 4096;

  public static acquire(width: number, height: number): PooledCanvas {
    // Try to find a suitable canvas from the pool
    const suitable = this.pool.find(
      (item) => !item.inUse &&
      item.canvas.width >= width &&
      item.canvas.height >= height &&
      item.canvas.width <= width * 1.5 &&
      item.canvas.height <= height * 1.5
    );

    if (suitable) {
      suitable.inUse = true;
      suitable.lastUsed = Date.now();

      // Clear and resize if needed
      suitable.canvas.width = width;
      suitable.canvas.height = height;
      suitable.ctx.clearRect(0, 0, width, height);

      return suitable;
    }

    // Create new canvas if pool not full
    if (this.pool.length < this.maxPoolSize) {
      const canvas = document.createElement('canvas');
      const ctx = canvas.getContext('2d', {
        alpha: true,
        desynchronized: true,
      }) as CanvasRenderingContext2D;

      canvas.width = Math.min(Math.max(width, this.MIN_CANVAS_SIZE), this.MAX_CANVAS_SIZE);
      canvas.height = Math.min(Math.max(height, this.MIN_CANVAS_SIZE), this.MAX_CANVAS_SIZE);

      const pooledCanvas: PooledCanvas = {
        canvas,
        ctx,
        inUse: true,
        lastUsed: Date.now(),
      };

      this.pool.push(pooledCanvas);
      return pooledCanvas;
    }

    // Evict and reuse least recently used canvas
    const lru = this.pool
      .filter((item) => !item.inUse)
      .sort((a, b) => a.lastUsed - b.lastUsed)[0];

    if (lru) {
      lru.canvas.width = width;
      lru.canvas.height = height;
      lru.ctx.clearRect(0, 0, width, height);
      lru.inUse = true;
      lru.lastUsed = Date.now();
      return lru;
    }

    // Fallback: create temporary canvas (shouldn't normally happen)
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
    canvas.width = width;
    canvas.height = height;

    return {
      canvas,
      ctx,
      inUse: true,
      lastUsed: Date.now(),
    };
  }

  public static release(pooledCanvas: PooledCanvas) {
    if (this.pool.includes(pooledCanvas)) {
      pooledCanvas.inUse = false;
      // Clear canvas to free memory
      pooledCanvas.ctx.clearRect(0, 0, pooledCanvas.canvas.width, pooledCanvas.canvas.height);
    }
  }

  public static releaseAll() {
    for (const item of this.pool) {
      item.inUse = false;
      item.ctx.clearRect(0, 0, item.canvas.width, item.canvas.height);
    }
  }

  public static destroy() {
    for (const item of this.pool) {
      item.canvas.width = 0;
      item.canvas.height = 0;
    }
    this.pool = [];
  }

  public static getStats() {
    return {
      poolSize: this.pool.length,
      maxPoolSize: this.maxPoolSize,
      inUse: this.pool.filter((item) => item.inUse).length,
      available: this.pool.filter((item) => !item.inUse).length,
    };
  }

  public static adjustPoolSize(newSize: number) {
    if (newSize < this.pool.length) {
      // Remove excess canvases
      const toRemove = this.pool.length - newSize;
      const removed = this.pool
        .filter((item) => !item.inUse)
        .slice(0, toRemove);

      for (const item of removed) {
        const index = this.pool.indexOf(item);
        if (index > -1) {
          this.pool.splice(index, 1);
        }
      }
    }
    this.maxPoolSize = newSize;
  }
}