import { computed, ref, watch } from 'vue';
import type { Layer, Sprite } from '@/types/sprites';
import { getMaxDimensions as getMaxDimensionsSingle, useSprites as useSpritesSingle } from './useSprites';
import { useSettingsStore } from '@/stores/useSettingsStore';

export const createEmptyLayer = (name: string): Layer => ({
  id: crypto.randomUUID(),
  name,
  sprites: [],
  visible: true,
  locked: false,
});

const layers = ref<Layer[]>([createEmptyLayer('Base')]);
const activeLayerId = ref<string>(layers.value[0].id);
const columns = ref(4);

export const useLayers = () => {
  const settingsStore = useSettingsStore();

  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 activeLayer = computed(() => layers.value.find(l => l.id === activeLayerId.value) || layers.value[0]);

  const getMaxDimensions = (sprites: Sprite[]) => getMaxDimensionsSingle(sprites);

  const visibleLayers = computed(() => layers.value.filter(l => l.visible));

  const hasSprites = computed(() => layers.value.some(l => l.sprites.length > 0));

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

  const updateSpriteInLayer = (layerId: string, spriteId: string, x: number, y: number) => {
    const l = layers.value.find(layer => layer.id === layerId);
    if (!l) return;
    const i = l.sprites.findIndex(s => s.id === spriteId);
    if (i !== -1) {
      l.sprites[i].x = Math.floor(x);
      l.sprites[i].y = Math.floor(y);
    }
  };

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

    // Determine the cell dimensions to align within
    let cellWidth: number;
    let cellHeight: number;

    if (settingsStore.manualCellSizeEnabled) {
      // Use manual cell size (without negative spacing)
      cellWidth = settingsStore.manualCellWidth;
      cellHeight = settingsStore.manualCellHeight;
    } else {
      // Use auto-calculated dimensions based on ALL visible layers (not just active layer)
      const { maxWidth, maxHeight } = getMaxDimensionsAcrossLayers(visibleLayers.value);
      cellWidth = maxWidth;
      cellHeight = maxHeight;
    }

    l.sprites = l.sprites.map(sprite => {
      let x = sprite.x;
      let y = sprite.y;
      switch (position) {
        case 'left':
          x = 0;
          break;
        case 'center':
          x = Math.floor((cellWidth - sprite.width) / 2);
          break;
        case 'right':
          x = Math.floor(cellWidth - sprite.width);
          break;
        case 'top':
          y = 0;
          break;
        case 'middle':
          y = Math.floor((cellHeight - sprite.height) / 2);
          break;
        case 'bottom':
          y = Math.floor(cellHeight - sprite.height);
          break;
      }
      return { ...sprite, x: Math.floor(x), y: Math.floor(y) };
    });
  };

  const updateSpriteCell = (id: string, newIndex: number) => {
    const l = activeLayer.value;
    if (!l) return;
    const currentIndex = l.sprites.findIndex(s => s.id === id);
    if (currentIndex === -1 || currentIndex === newIndex) return;
    const next = [...l.sprites];
    if (newIndex < next.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);
    }
    l.sprites = next;
  };

  const removeSprite = (id: string) => {
    const l = activeLayer.value;
    if (!l) return;
    const i = l.sprites.findIndex(s => s.id === id);
    if (i === -1) return;
    const s = l.sprites[i];
    if (s.url && s.url.startsWith('blob:')) {
      try {
        URL.revokeObjectURL(s.url);
      } catch {}
    }
    l.sprites.splice(i, 1);
  };

  const removeSprites = (ids: string[]) => {
    const l = activeLayer.value;
    if (!l) return;

    // Sort indices in descending order to avoid shift issues when splicing
    const indicesToRemove: number[] = [];
    ids.forEach(id => {
      const i = l.sprites.findIndex(s => s.id === id);
      if (i !== -1) indicesToRemove.push(i);
    });

    indicesToRemove.sort((a, b) => b - a);

    indicesToRemove.forEach(i => {
      const s = l.sprites[i];
      if (s.url && s.url.startsWith('blob:')) {
        try {
          URL.revokeObjectURL(s.url);
        } catch {}
      }
      l.sprites.splice(i, 1);
    });
  };

  const rotateSprite = (id: string, angle: number) => {
    const l = activeLayer.value;
    if (!l) return;
    const s = l.sprites.find(s => s.id === id);
    if (s) {
      s.rotation = (s.rotation + angle) % 360;
    }
  };

  const flipSprite = (id: string, direction: 'horizontal' | 'vertical') => {
    const l = activeLayer.value;
    if (!l) return;
    const s = l.sprites.find(s => s.id === id);
    if (s) {
      if (direction === 'horizontal') s.flipX = !s.flipX;
      if (direction === 'vertical') s.flipY = !s.flipY;
    }
  };

  const replaceSprite = (id: string, file: File) => {
    const l = activeLayer.value;
    if (!l) return;
    const i = l.sprites.findIndex(s => s.id === id);
    if (i === -1) return;
    const old = l.sprites[i];
    if (old.url && old.url.startsWith('blob:')) {
      try {
        URL.revokeObjectURL(old.url);
      } catch {}
    }

    const reader = new FileReader();
    reader.onload = e => {
      const url = e.target?.result as string;
      const img = new Image();
      img.onload = () => {
        l.sprites[i] = { id: old.id, file, img, url, width: img.width, height: img.height, x: old.x, y: old.y, rotation: old.rotation, flipX: old.flipX || false, flipY: old.flipY || false };
      };
      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 l = activeLayer.value;
    if (!l) return;
    const reader = new FileReader();
    reader.onload = e => {
      const url = e.target?.result as string;
      const img = new Image();
      img.onload = () => {
        const next: Sprite = {
          id: crypto.randomUUID(),
          file,
          img,
          url,
          width: img.width,
          height: img.height,
          x: 0,
          y: 0,
          rotation: 0,
          flipX: false,
          flipY: false,
        };
        l.sprites = [...l.sprites, next];
      };
      img.onerror = () => {
        console.error('Failed to load sprite image:', file.name);
      };
      img.src = url;
    };
    reader.onerror = () => {
      console.error('Failed to read sprite image file:', file.name);
    };
    reader.readAsDataURL(file);
  };

  const processImageFiles = async (files: File[]) => {
    for (const f of files) addSprite(f);
  };

  const addLayer = (name?: string) => {
    const l = createEmptyLayer(name || `Layer ${layers.value.length + 1}`);
    layers.value.push(l);
    activeLayerId.value = l.id;
  };

  const removeLayer = (id: string) => {
    if (layers.value.length === 1) return;
    const idx = layers.value.findIndex(l => l.id === id);
    if (idx === -1) return;
    layers.value.splice(idx, 1);
    if (activeLayerId.value === id) activeLayerId.value = layers.value[0].id;
  };

  const moveLayer = (id: string, direction: 'up' | 'down') => {
    const idx = layers.value.findIndex(l => l.id === id);
    if (idx === -1) return;
    if (direction === 'up' && idx > 0) {
      const [l] = layers.value.splice(idx, 1);
      layers.value.splice(idx - 1, 0, l);
    }
    if (direction === 'down' && idx < layers.value.length - 1) {
      const [l] = layers.value.splice(idx, 1);
      layers.value.splice(idx + 1, 0, l);
    }
  };

  const copySpriteToFrame = (spriteId: string, targetLayerId: string, targetFrameIndex: number) => {
    // Find the source sprite in any layer
    let sourceSprite: Sprite | undefined;
    for (const layer of layers.value) {
      sourceSprite = layer.sprites.find(s => s.id === spriteId);
      if (sourceSprite) break;
    }

    if (!sourceSprite) return;

    // Find target layer
    const targetLayer = layers.value.find(l => l.id === targetLayerId);
    if (!targetLayer) return;

    // Create a deep copy of the sprite with a new ID
    const copiedSprite: Sprite = {
      id: crypto.randomUUID(),
      file: sourceSprite.file,
      img: sourceSprite.img,
      url: sourceSprite.url,
      width: sourceSprite.width,
      height: sourceSprite.height,
      x: sourceSprite.x,
      y: sourceSprite.y,
      rotation: sourceSprite.rotation,
      flipX: sourceSprite.flipX,
      flipY: sourceSprite.flipY,
    };

    // Expand the sprites array if necessary with empty placeholder sprites
    while (targetLayer.sprites.length < targetFrameIndex) {
      targetLayer.sprites.push({
        id: crypto.randomUUID(),
        file: new File([], 'empty'),
        img: new Image(),
        url: '',
        width: 0,
        height: 0,
        x: 0,
        y: 0,
        rotation: 0,
        flipX: false,
        flipY: false,
      });
    }

    // Replace or insert the sprite at the target index
    if (targetFrameIndex < targetLayer.sprites.length) {
      // Replace existing sprite at this frame
      const old = targetLayer.sprites[targetFrameIndex];
      if (old.url && old.url.startsWith('blob:')) {
        try {
          URL.revokeObjectURL(old.url);
        } catch {}
      }
      targetLayer.sprites[targetFrameIndex] = copiedSprite;
    } else {
      // Add at the end
      targetLayer.sprites.push(copiedSprite);
    }
  };

  return {
    layers,
    visibleLayers,
    activeLayerId,
    activeLayer,
    columns,
    getMaxDimensions,
    updateSpritePosition,
    updateSpriteInLayer,
    updateSpriteCell,
    removeSprite,
    removeSprites,
    rotateSprite,
    flipSprite,
    replaceSprite,
    addSprite,
    processImageFiles,
    alignSprites,
    addLayer,
    removeLayer,
    moveLayer,
    copySpriteToFrame,
    hasSprites,
  };
};

export const getMaxDimensionsAcrossLayers = (layers: Layer[]) => {
  const sprites = layers.flatMap(l => (l.visible ? l.sprites : []));
  return getMaxDimensionsSingle(sprites);
};
