import { ref, watch, onUnmounted } from 'vue';
import type { Sprite } from '../types/sprites';

export const useSprites = () => {
  const sprites = ref<Sprite[]>([]);
  const columns = ref(4);

  // Clamp and coerce columns to a safe range [1..10]
  watch(columns, val => {
    const num = typeof val === 'number' ? val : parseInt(String(val));
    const safe = Number.isFinite(num) && num >= 1 ? Math.min(num, 10) : 1;
    if (safe !== columns.value) columns.value = safe;
  });

  const updateSpritePosition = (id: string, x: number, y: number) => {
    const i = sprites.value.findIndex(s => s.id === id);
    if (i !== -1) {
      sprites.value[i].x = Math.floor(x);
      sprites.value[i].y = Math.floor(y);
    }
  };

  const alignSprites = (position: 'left' | 'center' | 'right' | 'top' | 'middle' | 'bottom') => {
    if (!sprites.value.length) return;

    const { maxWidth, maxHeight } = getMaxDimensions(sprites.value);

    sprites.value = sprites.value.map(sprite => {
      let x = sprite.x;
      let y = sprite.y;

      switch (position) {
        case 'left':
          x = 0;
          break;
        case 'center':
          x = Math.floor((maxWidth - sprite.width) / 2);
          break;
        case 'right':
          x = Math.floor(maxWidth - sprite.width);
          break;
        case 'top':
          y = 0;
          break;
        case 'middle':
          y = Math.floor((maxHeight - sprite.height) / 2);
          break;
        case 'bottom':
          y = Math.floor(maxHeight - sprite.height);
          break;
      }

      return { ...sprite, x: Math.floor(x), y: Math.floor(y) };
    });

    triggerForceRedraw();
  };

  const updateSpriteCell = (id: string, newIndex: number) => {
    const currentIndex = sprites.value.findIndex(s => s.id === id);
    if (currentIndex === -1 || currentIndex === newIndex) return;

    const next = [...sprites.value];
    if (newIndex < sprites.value.length) {
      const moving = { ...next[currentIndex] };
      const target = { ...next[newIndex] };
      next[currentIndex] = target;
      next[newIndex] = moving;
    } else {
      const [moved] = next.splice(currentIndex, 1);
      next.splice(newIndex, 0, moved);
    }
    sprites.value = next;
  };

  const removeSprite = (id: string) => {
    const i = sprites.value.findIndex(s => s.id === id);
    if (i === -1) return;
    const s = sprites.value[i];
    revokeIfBlob(s.url);
    sprites.value.splice(i, 1);
  };

  const replaceSprite = (id: string, file: File) => {
    const i = sprites.value.findIndex(s => s.id === id);
    if (i === -1) return;
    const old = sprites.value[i];
    revokeIfBlob(old.url);

    const reader = new FileReader();
    reader.onload = e => {
      const url = e.target?.result as string;
      const img = new Image();
      img.onload = () => {
        const next: Sprite = {
          id: old.id,
          file,
          img,
          url,
          width: img.width,
          height: img.height,
          x: old.x,
          y: old.y,
          rotation: 0,
          flipX: false,
          flipY: false,
        };
        const arr = [...sprites.value];
        arr[i] = next;
        sprites.value = arr;
      };
      img.onerror = () => {
        console.error('Failed to load replacement image:', file.name);
      };
      img.src = url;
    };
    reader.onerror = () => {
      console.error('Failed to read replacement image file:', file.name);
    };
    reader.readAsDataURL(file);
  };

  const addSprite = (file: File) => {
    const reader = new FileReader();
    reader.onload = e => {
      const url = e.target?.result as string;
      const img = new Image();
      img.onload = () => {
        const s: Sprite = {
          id: crypto.randomUUID(),
          file,
          img,
          url,
          width: img.width,
          height: img.height,
          x: 0,
          y: 0,
          rotation: 0,
          flipX: false,
          flipY: false,
        };
        sprites.value = [...sprites.value, s];
      };
      img.onerror = () => {
        console.error('Failed to load new sprite image:', file.name);
      };
      img.src = url;
    };
    reader.onerror = () => {
      console.error('Failed to read sprite image file:', file.name);
    };
    reader.readAsDataURL(file);
  };

  const addSpriteWithResize = (file: File) => {
    const reader = new FileReader();
    reader.onload = e => {
      const url = e.target?.result as string;
      const img = new Image();
      img.onload = () => {
        const { maxWidth, maxHeight } = getMaxDimensions(sprites.value);

        const newSprite: Sprite = {
          id: crypto.randomUUID(),
          file,
          img,
          url,
          width: img.width,
          height: img.height,
          x: 0,
          y: 0,
          rotation: 0,
          flipX: false,
          flipY: false,
        };

        const newMaxWidth = Math.max(maxWidth, img.width);
        const newMaxHeight = Math.max(maxHeight, img.height);

        if (img.width > maxWidth || img.height > maxHeight) {
          sprites.value = sprites.value.map(sprite => {
            let newX = sprite.x;
            let newY = sprite.y;

            if (img.width > maxWidth) {
              const relativeX = maxWidth > 0 ? sprite.x / maxWidth : 0;
              newX = Math.floor(relativeX * newMaxWidth);
              newX = Math.max(0, Math.min(newX, newMaxWidth - sprite.width));
            }

            if (img.height > maxHeight) {
              const relativeY = maxHeight > 0 ? sprite.y / maxHeight : 0;
              newY = Math.floor(relativeY * newMaxHeight);
              newY = Math.max(0, Math.min(newY, newMaxHeight - sprite.height));
            }

            return { ...sprite, x: newX, y: newY };
          });
        }

        sprites.value = [...sprites.value, newSprite];
        triggerForceRedraw();
      };
      img.onerror = () => {
        console.error('Failed to load new sprite image:', file.name);
      };
      img.src = url;
    };
    reader.onerror = () => {
      console.error('Failed to read sprite image file:', file.name);
    };
    reader.readAsDataURL(file);
  };

  const processImageFiles = (files: File[]) => {
    Promise.all(
      files.map(
        file =>
          new Promise<Sprite>((resolve, reject) => {
            const reader = new FileReader();
            reader.onload = e => {
              const url = e.target?.result as string;
              const img = new Image();
              img.onload = () => {
                resolve({
                  id: crypto.randomUUID(),
                  file,
                  img,
                  url,
                  width: img.width,
                  height: img.height,
                  x: 0,
                  y: 0,
                  rotation: 0,
                  flipX: false,
                  flipY: false,
                });
              };
              img.onerror = () => reject(new Error(`Failed to load image: ${file.name}`));
              img.src = url;
            };
            reader.onerror = () => reject(new Error(`Failed to read file: ${file.name}`));
            reader.readAsDataURL(file);
          })
      )
    )
      .then(newSprites => {
        sprites.value = [...sprites.value, ...newSprites];
      })
      .catch(err => {
        console.error('Error processing image files:', err);
      });
  };

  onUnmounted(() => {
    sprites.value.forEach(s => revokeIfBlob(s.url));
  });

  return {
    sprites,
    columns,
    updateSpritePosition,
    alignSprites,
    updateSpriteCell,
    removeSprite,
    replaceSprite,
    addSprite,
    addSpriteWithResize,
    processImageFiles,
  };
};

export const getMaxDimensions = (arr: Sprite[] | Readonly<Sprite[]>): { maxWidth: number; maxHeight: number } => {
  let maxWidth = 0;
  let maxHeight = 0;
  arr.forEach(s => {
    if (s.width > maxWidth) maxWidth = s.width;
    if (s.height > maxHeight) maxHeight = s.height;
  });
  return { maxWidth, maxHeight };
};

export const revokeIfBlob = (url?: string) => {
  if (url && url.startsWith('blob:')) {
    try {
      URL.revokeObjectURL(url);
    } catch {}
  }
};

export const triggerForceRedraw = () => {
  setTimeout(() => {
    window.dispatchEvent(new Event('forceRedraw'));
  }, 0);
};
