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 { Layer, Sprite } from '@/types/sprites';
import { getMaxDimensionsAcrossLayers } from './useLayers';
import { calculateNegativeSpacing } from './useNegativeSpacing';

export const useExportLayers = (layersRef: Ref<Layer[]>, columns: Ref<number>, negativeSpacingEnabled: Ref<boolean>, activeLayerId?: Ref<string>, backgroundColor?: Ref<string>, manualCellSizeEnabled?: Ref<boolean>, manualCellWidth?: Ref<number>, manualCellHeight?: Ref<number>) => {
  const getVisibleLayers = () => layersRef.value.filter(l => l.visible);
  const getAllVisibleSprites = () => getVisibleLayers().flatMap(l => l.sprites);

  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 visibleLayers = getVisibleLayers();
    const { maxWidth, maxHeight } = getMaxDimensionsAcrossLayers(visibleLayers);
    const negativeSpacing = calculateNegativeSpacing(getAllVisibleSprites(), negativeSpacingEnabled.value);
    return {
      cellWidth: maxWidth + negativeSpacing,
      cellHeight: maxHeight + negativeSpacing,
      negativeSpacing,
    };
  };

  const drawCompositeCell = (ctx: CanvasRenderingContext2D, cellIndex: number, cellWidth: number, cellHeight: number, negativeSpacing: number) => {
    ctx.clearRect(0, 0, cellWidth, cellHeight);
    // Apply background color if not transparent
    if (backgroundColor?.value && backgroundColor.value !== 'transparent') {
      ctx.fillStyle = backgroundColor.value;
      ctx.fillRect(0, 0, cellWidth, cellHeight);
    }
    const vLayers = getVisibleLayers();
    vLayers.forEach(layer => {
      const sprite = layer.sprites[cellIndex];
      if (!sprite) return;

      if (sprite.rotation || sprite.flipX || sprite.flipY) {
        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.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(negativeSpacing + sprite.x), Math.floor(negativeSpacing + sprite.y));
      }
    });
  };

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

    const { cellWidth, cellHeight, negativeSpacing } = getCellDimensions();
    const maxLen = Math.max(...visibleLayers.map(l => l.sprites.length));
    const rows = Math.ceil(maxLen / 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 to entire canvas if not transparent
    if (backgroundColor?.value && backgroundColor.value !== 'transparent') {
      ctx.fillStyle = backgroundColor.value;
      ctx.fillRect(0, 0, canvas.width, canvas.height);
    }

    for (let index = 0; index < maxLen; 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);

      const cellCanvas = document.createElement('canvas');
      const cellCtx = cellCanvas.getContext('2d');
      if (!cellCtx) return;
      cellCanvas.width = cellWidth;
      cellCanvas.height = cellHeight;
      cellCtx.imageSmoothingEnabled = false;
      drawCompositeCell(cellCtx, index, cellWidth, cellHeight, negativeSpacing);
      ctx.drawImage(cellCanvas, cellX, cellY);
    }

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

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

    const layersData = await Promise.all(
      layersRef.value.map(async layer => {
        const sprites = await Promise.all(
          layer.sprites.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;
            ctx.drawImage(sprite.img, 0, 0);
            const base64 = canvas.toDataURL('image/png');
            return { id: sprite.id, width: sprite.width, height: sprite.height, x: sprite.x, y: sprite.y, rotation: sprite.rotation, flipX: sprite.flipX, flipY: sprite.flipY, base64, name: sprite.file.name };
          })
        );
        return { id: layer.id, name: layer.name, visible: layer.visible, locked: layer.locked, sprites: sprites.filter(Boolean) };
      })
    );

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

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

    const loadSprite = (spriteData: any) =>
      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;
      });

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

    if (Array.isArray(data.layers)) {
      const newLayers: Layer[] = [];
      for (const layerData of data.layers) {
        const sprites: Sprite[] = await Promise.all(layerData.sprites.map((s: any) => loadSprite(s)));
        newLayers.push({ id: layerData.id || crypto.randomUUID(), name: layerData.name || 'Layer', visible: layerData.visible !== false, locked: !!layerData.locked, sprites });
      }
      // Ensure at least one layer with sprites is visible
      if (newLayers.length > 0 && !newLayers.some(l => l.visible && l.sprites.length > 0)) {
        const firstLayerWithSprites = newLayers.find(l => l.sprites.length > 0);
        if (firstLayerWithSprites) {
          firstLayerWithSprites.visible = true;
        }
      }
      layersRef.value = newLayers;
      // Set active layer to the first layer with sprites
      if (activeLayerId && newLayers.length > 0) {
        const firstWithSprites = newLayers.find(l => l.sprites.length > 0);
        activeLayerId.value = firstWithSprites ? firstWithSprites.id : newLayers[0].id;
      }
      return;
    }

    if (Array.isArray(data.sprites)) {
      const sprites: Sprite[] = await Promise.all(data.sprites.map((s: any) => loadSprite(s)));
      const baseLayerId = crypto.randomUUID();
      layersRef.value = [
        { id: baseLayerId, name: 'Base', visible: true, locked: false, sprites },
        { id: crypto.randomUUID(), name: 'Other', visible: true, locked: false, sprites: [] },
      ];
      if (activeLayerId) {
        activeLayerId.value = baseLayerId;
      }
      return;
    }

    throw new Error('Invalid JSON format');
  };

  const downloadAsGif = (fps: number) => {
    const visibleLayers = getVisibleLayers();
    if (!visibleLayers.length || !visibleLayers.some(l => l.sprites.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 });
    const maxLen = Math.max(...visibleLayers.map(l => l.sprites.length));
    for (let i = 0; i < maxLen; i++) {
      drawCompositeCell(ctx, i, cellWidth, cellHeight, negativeSpacing);
      gif.addFrame(ctx, { copy: true, delay: 1000 / fps });
    }

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

  const downloadAsZip = async () => {
    const visibleLayers = getVisibleLayers();
    if (!visibleLayers.length || !visibleLayers.some(l => l.sprites.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;

    const maxLen = Math.max(...visibleLayers.map(l => l.sprites.length));
    for (let i = 0; i < maxLen; i++) {
      drawCompositeCell(ctx, i, cellWidth, cellHeight, negativeSpacing);
      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 j = 0; j < binary.length; j++) view[j] = binary.charCodeAt(j);
      zip.file(`frames/frame_${String(i + 1).padStart(3, '0')}.png`, view);
    }

    const jsonFolder = zip.folder('export')!;
    const jsonBlobPromise = (async () => {
      const layersPayload = await Promise.all(
        layersRef.value.map(async layer => ({
          id: layer.id,
          name: layer.name,
          visible: layer.visible,
          locked: layer.locked,
          sprites: await Promise.all(layer.sprites.map(async s => ({ id: s.id, width: s.width, height: s.height, x: s.x, y: s.y, rotation: s.rotation, flipX: s.flipX, flipY: s.flipY, name: s.file.name }))),
        }))
      );
      const meta = {
        version: 2,
        columns: columns.value,
        negativeSpacingEnabled: negativeSpacingEnabled.value,
        backgroundColor: backgroundColor?.value || 'transparent',
        manualCellSizeEnabled: manualCellSizeEnabled?.value || false,
        manualCellWidth: manualCellWidth?.value || 64,
        manualCellHeight: manualCellHeight?.value || 64,
        layers: layersPayload,
      };
      const metaStr = JSON.stringify(meta, null, 2);
      jsonFolder.file('spritesheet.meta.json', metaStr);
    })();
    await jsonBlobPromise;

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

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