import { ref, computed, type Ref } from 'vue';
import type { SpritePreview, SpriteRegion, GridSplitOptions, AutoDetectOptions, SpriteWorkerResponse } from '@/types/spritesheet';

/**
 * Composable for spritesheet splitting logic
 * Handles grid-based and auto-detection sprite extraction
 */
export function useSpritesheetSplitter() {
  const isProcessing = ref(false);
  const previewSprites: Ref<SpritePreview[]> = ref([]);
  const worker: Ref<Worker | null> = ref(null);

  /**
   * Split image into grid cells
   */
  async function splitByGrid(img: HTMLImageElement, options: GridSplitOptions): Promise<SpritePreview[]> {
    const { cellWidth, cellHeight, preserveCellSize, removeEmpty } = options;

    if (cellWidth <= 0 || cellHeight <= 0) {
      return [];
    }

    const cols = Math.floor(img.width / cellWidth);
    const rows = Math.floor(img.height / cellHeight);
    const sprites: SpritePreview[] = [];

    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    const cropCanvas = document.createElement('canvas');
    const cropCtx = cropCanvas.getContext('2d');

    if (!ctx || !cropCtx) return [];

    canvas.width = cellWidth;
    canvas.height = cellHeight;

    for (let row = 0; row < rows; row++) {
      for (let col = 0; col < cols; col++) {
        ctx.clearRect(0, 0, cellWidth, cellHeight);
        ctx.drawImage(img, col * cellWidth, row * cellHeight, cellWidth, cellHeight, 0, 0, cellWidth, cellHeight);

        const isEmpty = removeEmpty ? isCanvasEmpty(ctx, cellWidth, cellHeight) : false;

        if (!removeEmpty || !isEmpty) {
          let url: string;
          let x = 0;
          let y = 0;
          let width = cellWidth;
          let height = cellHeight;

          if (preserveCellSize) {
            // Keep full cell with transparent padding
            url = canvas.toDataURL('image/png');
          } else {
            // Crop to sprite bounds
            const bounds = getSpriteBounds(ctx, cellWidth, cellHeight);
            if (bounds) {
              x = bounds.x;
              y = bounds.y;
              width = bounds.width;
              height = bounds.height;

              cropCanvas.width = width;
              cropCanvas.height = height;
              cropCtx.clearRect(0, 0, width, height);
              cropCtx.drawImage(canvas, x, y, width, height, 0, 0, width, height);
              url = cropCanvas.toDataURL('image/png');
            } else {
              url = canvas.toDataURL('image/png');
            }
          }

          sprites.push({ url, x, y, width, height, isEmpty });
        }
      }
    }

    return sprites;
  }

  /**
   * Detect sprites using web worker for irregular layouts
   */
  async function detectSprites(img: HTMLImageElement, options: AutoDetectOptions): Promise<SpritePreview[]> {
    const { sensitivity, removeEmpty } = options;

    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    if (!ctx) return [];

    canvas.width = img.width;
    canvas.height = img.height;
    ctx.drawImage(img, 0, 0);

    const imageData = ctx.getImageData(0, 0, img.width, img.height);

    // Initialize worker lazily
    if (!worker.value) {
      try {
        worker.value = new Worker(new URL('../workers/irregularSpriteDetection.worker.ts', import.meta.url), { type: 'module' });
      } catch (error) {
        console.error('Failed to create worker:', error);
        return [];
      }
    }

    return new Promise<SpritePreview[]>(resolve => {
      const w = worker.value!;

      const timeout = setTimeout(() => {
        w.removeEventListener('message', handleMessage);
        console.warn('Worker timeout');
        resolve([]);
      }, 30000);

      const handleMessage = async (e: MessageEvent<SpriteWorkerResponse>) => {
        clearTimeout(timeout);
        w.removeEventListener('message', handleMessage);

        if (e.data.type === 'spritesDetected') {
          const sprites = await processDetectedSprites(img, e.data.sprites, e.data.backgroundColor, removeEmpty);
          resolve(sprites);
        }
      };

      w.addEventListener('message', handleMessage);

      w.postMessage({
        type: 'detectIrregularSprites',
        imageData,
        sensitivity,
        maxSize: 2048,
      });
    });
  }

  /**
   * Process sprites detected by worker into preview format
   */
  async function processDetectedSprites(img: HTMLImageElement, regions: SpriteRegion[], backgroundColor: [number, number, number, number], removeEmpty: boolean): Promise<SpritePreview[]> {
    if (!regions?.length) return [];

    const sprites: SpritePreview[] = [];
    const sourceCanvas = document.createElement('canvas');
    const sourceCtx = sourceCanvas.getContext('2d');
    const spriteCanvas = document.createElement('canvas');
    const spriteCtx = spriteCanvas.getContext('2d');

    if (!sourceCtx || !spriteCtx) return [];

    sourceCanvas.width = img.width;
    sourceCanvas.height = img.height;
    sourceCtx.drawImage(img, 0, 0);

    for (const region of regions) {
      const { x, y, width, height } = region;
      if (width <= 0 || height <= 0) continue;

      spriteCanvas.width = width;
      spriteCanvas.height = height;
      spriteCtx.clearRect(0, 0, width, height);
      spriteCtx.drawImage(sourceCanvas, x, y, width, height, 0, 0, width, height);

      // Remove background color
      removeBackground(spriteCtx, width, height, backgroundColor);

      const isEmpty = removeEmpty ? isCanvasEmpty(spriteCtx, width, height) : false;

      if (!removeEmpty || !isEmpty) {
        sprites.push({
          url: spriteCanvas.toDataURL('image/png'),
          x,
          y,
          width,
          height,
          isEmpty,
        });
      }
    }

    return sprites;
  }

  /**
   * Get bounding box of non-transparent pixels
   */
  function getSpriteBounds(ctx: CanvasRenderingContext2D, width: number, height: number): { x: number; y: number; width: number; height: number } | null {
    const imageData = ctx.getImageData(0, 0, width, height);
    const data = imageData.data;

    let minX = width,
      minY = height,
      maxX = 0,
      maxY = 0;
    let hasContent = false;

    for (let y = 0; y < height; y++) {
      for (let x = 0; x < width; x++) {
        const alpha = data[(y * width + x) * 4 + 3];
        if (alpha > 10) {
          minX = Math.min(minX, x);
          minY = Math.min(minY, y);
          maxX = Math.max(maxX, x);
          maxY = Math.max(maxY, y);
          hasContent = true;
        }
      }
    }

    if (!hasContent) return null;

    // Add small padding
    const pad = 1;
    return {
      x: Math.max(0, minX - pad),
      y: Math.max(0, minY - pad),
      width: Math.min(width - Math.max(0, minX - pad), maxX - minX + 1 + pad * 2),
      height: Math.min(height - Math.max(0, minY - pad), maxY - minY + 1 + pad * 2),
    };
  }

  /**
   * Check if canvas is empty (all transparent or same color)
   */
  function isCanvasEmpty(ctx: CanvasRenderingContext2D, width: number, height: number): boolean {
    const data = ctx.getImageData(0, 0, width, height).data;
    let allTransparent = true;
    let allSameColor = true;

    const [firstR, firstG, firstB, firstA] = [data[0], data[1], data[2], data[3]];

    for (let i = 0; i < data.length; i += 4) {
      if (data[i + 3] > 10) allTransparent = false;
      if (data[i] !== firstR || data[i + 1] !== firstG || data[i + 2] !== firstB || Math.abs(data[i + 3] - firstA) > 10) {
        allSameColor = false;
      }
      if (!allTransparent && !allSameColor) break;
    }

    return allTransparent || allSameColor;
  }

  /**
   * Remove background color from sprite, making it transparent
   */
  function removeBackground(ctx: CanvasRenderingContext2D, width: number, height: number, bgColor: [number, number, number, number]): void {
    const imageData = ctx.getImageData(0, 0, width, height);
    const data = imageData.data;
    const [bgR, bgG, bgB, bgA] = bgColor;
    const tolerance = 30;

    for (let i = 0; i < data.length; i += 4) {
      const a = data[i + 3];
      if (a < 10) {
        data[i + 3] = 0;
        continue;
      }

      const rDiff = data[i] - bgR;
      const gDiff = data[i + 1] - bgG;
      const bDiff = data[i + 2] - bgB;
      const dist = Math.sqrt(rDiff * rDiff + gDiff * gDiff + bDiff * bDiff);

      if (dist <= tolerance && Math.abs(a - bgA) <= 40) {
        data[i + 3] = 0;
      }
    }

    ctx.putImageData(imageData, 0, 0);
  }

  /**
   * Cleanup worker on unmount
   */
  function cleanup() {
    if (worker.value) {
      worker.value.terminate();
      worker.value = null;
    }
  }

  /**
   * Calculate suggested cell size based on image dimensions
   */
  function getSuggestedCellSize(width: number, height: number): { width: number; height: number } {
    const commonSizes = [256, 192, 128, 96, 64, 48, 32, 16];

    let cellWidth = 64;
    let cellHeight = 64;

    for (const size of commonSizes) {
      if (width % size === 0 && width / size <= 16 && width / size >= 1) {
        cellWidth = size;
        break;
      }
    }

    for (const size of commonSizes) {
      if (height % size === 0 && height / size <= 16 && height / size >= 1) {
        cellHeight = size;
        break;
      }
    }

    return { width: cellWidth, height: cellHeight };
  }

  return {
    isProcessing,
    previewSprites,
    splitByGrid,
    detectSprites,
    getSuggestedCellSize,
    cleanup,
  };
}
