import type { Ref } from 'vue';
import GIF from 'gif.js';
import gifWorkerUrl from 'gif.js/dist/gif.worker.js?url';
import JSZip from 'jszip';
import type { Sprite } from '../types/sprites';
import { getMaxDimensions } from './useSprites';
import { calculateNegativeSpacing } from './useNegativeSpacing';

export const useExport = (sprites: Ref<Sprite[]>, columns: Ref<number>, negativeSpacingEnabled: Ref<boolean>, backgroundColor?: Ref<string>, manualCellSizeEnabled?: Ref<boolean>, manualCellWidth?: Ref<number>, manualCellHeight?: Ref<number>) => {
  const getCellDimensions = () => {
    // If manual cell size is enabled, use manual values
    if (manualCellSizeEnabled?.value) {
      return {
        cellWidth: manualCellWidth?.value ?? 64,
        cellHeight: manualCellHeight?.value ?? 64,
        negativeSpacing: 0,
      };
    }

    // Otherwise, calculate from sprite dimensions
    const { maxWidth, maxHeight } = getMaxDimensions(sprites.value);
    const negativeSpacing = calculateNegativeSpacing(sprites.value, negativeSpacingEnabled.value);
    return {
      cellWidth: maxWidth + negativeSpacing,
      cellHeight: maxHeight + negativeSpacing,
      negativeSpacing,
    };
  };

  const downloadSpritesheet = () => {
    if (!sprites.value.length) {
      alert('Please upload or import sprites before downloading the spritesheet.');
      return;
    }

    const { cellWidth, cellHeight, negativeSpacing } = getCellDimensions();
    const rows = Math.ceil(sprites.value.length / columns.value);

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

    canvas.width = cellWidth * columns.value;
    canvas.height = cellHeight * rows;
    ctx.imageSmoothingEnabled = false;

    // Apply background color if not transparent
    if (backgroundColor?.value && backgroundColor.value !== 'transparent') {
      ctx.fillStyle = backgroundColor.value;
      ctx.fillRect(0, 0, canvas.width, canvas.height);
    }

    sprites.value.forEach((sprite, index) => {
      const col = index % columns.value;
      const row = Math.floor(index / columns.value);
      const cellX = Math.floor(col * cellWidth);
      const cellY = Math.floor(row * cellHeight);
      if (sprite.rotation || sprite.flipX || sprite.flipY) {
        ctx.save();
        const centerX = Math.floor(cellX + negativeSpacing + sprite.x + sprite.width / 2);
        const centerY = Math.floor(cellY + negativeSpacing + sprite.y + sprite.height / 2);
        ctx.translate(centerX, centerY);
        ctx.rotate((sprite.rotation * Math.PI) / 180);
        ctx.scale(sprite.flipX ? -1 : 1, sprite.flipY ? -1 : 1);
        ctx.drawImage(sprite.img, -sprite.width / 2, -sprite.height / 2);
        ctx.restore();
      } else {
        ctx.drawImage(sprite.img, Math.floor(cellX + negativeSpacing + sprite.x), Math.floor(cellY + negativeSpacing + sprite.y));
      }
    });

    const link = document.createElement('a');
    link.download = 'spritesheet.png';
    link.href = canvas.toDataURL('image/png', 1.0);
    link.click();
  };

  const exportSpritesheetJSON = async () => {
    if (!sprites.value.length) {
      alert('Nothing to export. Please add sprites first.');
      return;
    }

    const spritesData = await Promise.all(
      sprites.value.map(async sprite => {
        const canvas = document.createElement('canvas');
        const ctx = canvas.getContext('2d');
        if (!ctx) return null;
        canvas.width = sprite.width;
        canvas.height = sprite.height;
        if (sprite.rotation) {
          ctx.save();
          ctx.translate(sprite.width / 2, sprite.height / 2);
          ctx.rotate((sprite.rotation * Math.PI) / 180);
          ctx.drawImage(sprite.img, -sprite.width / 2, -sprite.height / 2);
          ctx.restore();
        } else {
          ctx.drawImage(sprite.img, 0, 0);
        }
        const base64Data = canvas.toDataURL('image/png');
        return {
          id: sprite.id,
          width: sprite.width,
          height: sprite.height,
          x: sprite.x,
          y: sprite.y,
          rotation: sprite.rotation || 0,
          flipX: sprite.flipX || false,
          flipY: sprite.flipY || false,
          base64: base64Data,
          name: sprite.file.name,
        };
      })
    );

    const jsonData = {
      columns: columns.value,
      negativeSpacingEnabled: negativeSpacingEnabled.value,
      backgroundColor: backgroundColor?.value || 'transparent',
      manualCellSizeEnabled: manualCellSizeEnabled?.value || false,
      manualCellWidth: manualCellWidth?.value || 64,
      manualCellHeight: manualCellHeight?.value || 64,
      sprites: spritesData.filter(Boolean),
    };
    const jsonString = JSON.stringify(jsonData, null, 2);
    const blob = new Blob([jsonString], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.download = 'spritesheet.json';
    link.href = url;
    link.click();
    URL.revokeObjectURL(url);
  };

  const importSpritesheetJSON = async (jsonFile: File) => {
    const jsonText = await jsonFile.text();
    const jsonData = JSON.parse(jsonText);

    if (!jsonData.sprites || !Array.isArray(jsonData.sprites)) throw new Error('Invalid JSON format: missing sprites array');

    if (jsonData.columns && typeof jsonData.columns === 'number') columns.value = jsonData.columns;
    if (typeof jsonData.negativeSpacingEnabled === 'boolean') negativeSpacingEnabled.value = jsonData.negativeSpacingEnabled;
    if (typeof jsonData.backgroundColor === 'string' && backgroundColor) backgroundColor.value = jsonData.backgroundColor;
    if (typeof jsonData.manualCellSizeEnabled === 'boolean' && manualCellSizeEnabled) manualCellSizeEnabled.value = jsonData.manualCellSizeEnabled;
    if (typeof jsonData.manualCellWidth === 'number' && manualCellWidth) manualCellWidth.value = jsonData.manualCellWidth;
    if (typeof jsonData.manualCellHeight === 'number' && manualCellHeight) manualCellHeight.value = jsonData.manualCellHeight;

    // revoke existing blob urls
    if (sprites.value.length) {
      sprites.value.forEach(s => {
        if (s.url && s.url.startsWith('blob:')) {
          try {
            URL.revokeObjectURL(s.url);
          } catch {}
        }
      });
    }

    const imported = await Promise.all(
      jsonData.sprites.map((spriteData: any) => {
        return new Promise<Sprite>(resolve => {
          const img = new Image();
          img.onload = () => {
            const byteString = atob(spriteData.base64.split(',')[1]);
            const mimeType = spriteData.base64.split(',')[0].split(':')[1].split(';')[0];
            const ab = new ArrayBuffer(byteString.length);
            const ia = new Uint8Array(ab);
            for (let i = 0; i < byteString.length; i++) ia[i] = byteString.charCodeAt(i);
            const blob = new Blob([ab], { type: mimeType });
            const fileName = spriteData.name || `sprite-${spriteData.id}.png`;
            const file = new File([blob], fileName, { type: mimeType });
            resolve({
              id: spriteData.id || crypto.randomUUID(),
              file,
              img,
              url: spriteData.base64,
              width: spriteData.width,
              height: spriteData.height,
              x: spriteData.x || 0,
              y: spriteData.y || 0,
              rotation: spriteData.rotation || 0,
              flipX: spriteData.flipX || false,
              flipY: spriteData.flipY || false,
            });
          };
          img.src = spriteData.base64;
        });
      })
    );

    sprites.value = imported;
  };

  const downloadAsGif = (fps: number) => {
    if (!sprites.value.length) {
      alert('Please upload or import sprites before generating a GIF.');
      return;
    }

    const { cellWidth, cellHeight, negativeSpacing } = getCellDimensions();
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    if (!ctx) return;
    canvas.width = cellWidth;
    canvas.height = cellHeight;
    ctx.imageSmoothingEnabled = false;

    const gif = new GIF({ workers: 2, quality: 10, width: cellWidth, height: cellHeight, workerScript: gifWorkerUrl });

    sprites.value.forEach(sprite => {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      // Apply background color if not transparent
      if (backgroundColor?.value && backgroundColor.value !== 'transparent') {
        ctx.fillStyle = backgroundColor.value;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
      }
      if (sprite.rotation) {
        ctx.save();
        const centerX = Math.floor(negativeSpacing + sprite.x + sprite.width / 2);
        const centerY = Math.floor(negativeSpacing + sprite.y + sprite.height / 2);
        ctx.translate(centerX, centerY);
        ctx.rotate((sprite.rotation * Math.PI) / 180);
        ctx.drawImage(sprite.img, -sprite.width / 2, -sprite.height / 2);
        ctx.restore();
      } else {
        ctx.drawImage(sprite.img, Math.floor(negativeSpacing + sprite.x), Math.floor(negativeSpacing + sprite.y));
      }
      gif.addFrame(ctx, { copy: true, delay: 1000 / fps });
    });

    gif.on('finished', (blob: Blob) => {
      const url = URL.createObjectURL(blob);
      const link = document.createElement('a');
      link.download = 'animation.gif';
      link.href = url;
      link.click();
      URL.revokeObjectURL(url);
    });

    gif.render();
  };

  const downloadAsZip = async () => {
    if (!sprites.value.length) {
      alert('Please upload or import sprites before downloading a ZIP.');
      return;
    }

    const zip = new JSZip();
    const { cellWidth, cellHeight, negativeSpacing } = getCellDimensions();
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    if (!ctx) return;
    canvas.width = cellWidth;
    canvas.height = cellHeight;
    ctx.imageSmoothingEnabled = false;

    sprites.value.forEach((sprite, index) => {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      // Apply background color if not transparent
      if (backgroundColor?.value && backgroundColor.value !== 'transparent') {
        ctx.fillStyle = backgroundColor.value;
        ctx.fillRect(0, 0, canvas.width, canvas.height);
      }
      if (sprite.rotation) {
        ctx.save();
        const centerX = Math.floor(negativeSpacing + sprite.x + sprite.width / 2);
        const centerY = Math.floor(negativeSpacing + sprite.y + sprite.height / 2);
        ctx.translate(centerX, centerY);
        ctx.rotate((sprite.rotation * Math.PI) / 180);
        ctx.drawImage(sprite.img, -sprite.width / 2, -sprite.height / 2);
        ctx.restore();
      } else {
        ctx.drawImage(sprite.img, Math.floor(negativeSpacing + sprite.x), Math.floor(negativeSpacing + sprite.y));
      }
      const dataURL = canvas.toDataURL('image/png');
      const binary = atob(dataURL.split(',')[1]);
      const buf = new ArrayBuffer(binary.length);
      const view = new Uint8Array(buf);
      for (let i = 0; i < binary.length; i++) view[i] = binary.charCodeAt(i);
      const baseName = sprite.file?.name ? sprite.file.name.replace(/\s+/g, '_') : `sprite_${index + 1}.png`;
      const name = `${index + 1}_${baseName}`;
      zip.file(name, view);
    });

    const content = await zip.generateAsync({ type: 'blob' });
    const url = URL.createObjectURL(content);
    const link = document.createElement('a');
    link.download = 'sprites.zip';
    link.href = url;
    link.click();
    URL.revokeObjectURL(url);
  };

  return { downloadSpritesheet, exportSpritesheetJSON, importSpritesheetJSON, downloadAsGif, downloadAsZip };
};
