<template>
  <Teleport to="body">
    <div v-if="showContextMenu" @click.stop class="fixed bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg shadow-xl z-50 py-1 min-w-[160px] overflow-hidden" :style="{ left: contextMenuX + 'px', top: contextMenuY + 'px' }">
      <button @click="rotateSpriteInMenu(90)" class="w-full px-3 py-1.5 text-left hover:bg-green-50 dark:hover:bg-green-900/30 text-gray-700 dark:text-gray-200 flex items-center gap-2 transition-colors text-sm">
        <i class="fas fa-redo text-green-600 dark:text-green-400 text-xs w-4"></i>
        <span>Rotate +90°</span>
      </button>
      <button @click="flipSpriteInMenu('horizontal')" class="w-full px-3 py-1.5 text-left hover:bg-orange-50 dark:hover:bg-orange-900/30 text-gray-700 dark:text-gray-200 flex items-center gap-2 transition-colors text-sm">
        <i class="fas fa-arrows-alt-h text-orange-600 dark:text-orange-400 text-xs w-4"></i>
        <span>Flip Horizontal</span>
      </button>
      <button @click="flipSpriteInMenu('vertical')" class="w-full px-3 py-1.5 text-left hover:bg-orange-50 dark:hover:bg-orange-900/30 text-gray-700 dark:text-gray-200 flex items-center gap-2 transition-colors text-sm">
        <i class="fas fa-arrows-alt-v text-orange-600 dark:text-orange-400 text-xs w-4"></i>
        <span>Flip Vertical</span>
      </button>
      <button @click="replaceSprite" class="w-full px-3 py-1.5 text-left hover:bg-purple-50 dark:hover:bg-purple-900/30 text-gray-700 dark:text-gray-200 flex items-center gap-2 transition-colors text-sm">
        <i class="fas fa-exchange-alt text-purple-600 dark:text-purple-400 text-xs w-4"></i>
        <span>Replace Sprite</span>
      </button>
      <button @click="openCopyToFrameModal" class="w-full px-3 py-1.5 text-left hover:bg-cyan-50 dark:hover:bg-cyan-900/30 text-gray-700 dark:text-gray-200 flex items-center gap-2 transition-colors text-sm">
        <i class="fas fa-copy text-cyan-600 dark:text-cyan-400 text-xs w-4"></i>
        <span>Copy to Frame...</span>
      </button>
    </div>

    <!-- Copy to Frame Modal -->
    <div v-if="showCopyToFrameModal" class="fixed inset-0 bg-black/50 z-[60] flex items-center justify-center" @click.self="closeCopyToFrameModal">
      <div class="bg-white dark:bg-gray-800 rounded-xl shadow-2xl p-6 min-w-[320px] max-w-md" @click.stop>
        <h3 class="text-lg font-bold text-gray-900 dark:text-gray-100 mb-4">Copy Sprite to Frame</h3>

        <div class="space-y-4">
          <div>
            <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Target Frame</label>
            <select v-model.number="copyTargetFrame" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-blue-500">
              <option v-for="i in maxFrameCount" :key="i" :value="i - 1">Frame {{ i }}</option>
            </select>
          </div>

          <div>
            <label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Target Layer</label>
            <select v-model="copyTargetLayerId" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 focus:ring-2 focus:ring-blue-500">
              <option v-for="layer in props.layers" :key="layer.id" :value="layer.id">{{ layer.name }}</option>
            </select>
          </div>
        </div>

        <div class="flex justify-end gap-3 mt-6">
          <button @click="closeCopyToFrameModal" class="px-4 py-2 text-gray-600 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors">Cancel</button>
          <button @click="confirmCopyToFrame" class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors">Copy</button>
        </div>
      </div>
    </div>
  </Teleport>

  <div class="spritesheet-preview w-full h-full" @click="hideContextMenu">
    <div class="flex flex-col lg:flex-row gap-4 h-full min-h-0">
      <div class="flex-1 min-w-0 flex flex-col min-h-0">
        <div
          class="relative bg-gray-50 dark:bg-gray-800 border border-gray-300 dark:border-gray-700 rounded-lg overflow-auto flex-1 min-h-[200px] sm:min-h-[300px] max-h-[50vh] lg:max-h-none shadow-sm hover:shadow-md transition-shadow duration-200"
          @mousemove="drag"
          @mouseup="stopDrag"
          @mouseleave="stopDrag"
          @touchmove="handleTouchMove"
          @touchend="stopDrag"
        >
          <div
            ref="previewContainerRef"
            class="relative touch-manipulation inline-block"
            :class="{ 'ring-2 ring-blue-500 ring-offset-2': isDragOver }"
            :style="{
              transform: `scale(${zoom})`,
              transformOrigin: 'top left',
              width: `${cellDimensions.cellWidth}px`,
              height: `${cellDimensions.cellHeight}px`,
              backgroundColor: '#f9fafb',
              backgroundImage: getPreviewBackgroundImage(),
              backgroundSize: settingsStore.backgroundColor === 'transparent' ? '20px 20px' : 'auto',
              backgroundPosition: settingsStore.backgroundColor === 'transparent' ? '0 0, 0 10px, 10px -10px, -10px 0px' : '0 0',
              border: `1px solid ${settingsStore.darkMode ? '#4b5563' : '#e5e7eb'}`,
            }"
            @dragover.prevent="onDragOver"
            @dragleave="onDragLeave"
            @drop.prevent="onDrop"
          >
            <!-- Background sprites (dimmed for comparison) -->
            <template v-if="showAllSprites">
              <template v-for="i in maxFrames()" :key="`bg-${i - 1}`">
                <template v-if="i - 1 !== currentFrameIndex && !hiddenFrames.includes(i - 1)">
                  <template v-for="layer in getVisibleLayers()" :key="`${layer.id}-${i - 1}`">
                    <img
                      v-if="layer.sprites[i - 1]"
                      :src="layer.sprites[i - 1].url"
                      class="absolute pointer-events-none"
                      :style="{
                        left: `${cellDimensions.negativeSpacing + layer.sprites[i - 1].x}px`,
                        top: `${cellDimensions.negativeSpacing + layer.sprites[i - 1].y}px`,
                        width: `${layer.sprites[i - 1].width}px`,
                        height: `${layer.sprites[i - 1].height}px`,
                        opacity: '0.3',
                        imageRendering: settingsStore.pixelPerfect ? 'pixelated' : 'auto',
                        transform: `rotate(${layer.sprites[i - 1].rotation || 0}deg) scale(${layer.sprites[i - 1].flipX ? -1 : 1}, ${layer.sprites[i - 1].flipY ? -1 : 1})`,
                      }"
                      draggable="false"
                    />
                  </template>
                </template>
              </template>
            </template>

            <!-- Current frame sprites -->
            <template v-for="layer in getVisibleLayers()" :key="layer.id">
              <img
                v-if="layer.sprites[currentFrameIndex]"
                :src="layer.sprites[currentFrameIndex].url"
                class="absolute"
                :class="{ 'cursor-move': isDraggable }"
                :style="{
                  left: `${cellDimensions.negativeSpacing + layer.sprites[currentFrameIndex].x}px`,
                  top: `${cellDimensions.negativeSpacing + layer.sprites[currentFrameIndex].y}px`,
                  width: `${layer.sprites[currentFrameIndex].width}px`,
                  height: `${layer.sprites[currentFrameIndex].height}px`,
                  imageRendering: settingsStore.pixelPerfect ? 'pixelated' : 'auto',
                  transform: `rotate(${layer.sprites[currentFrameIndex].rotation || 0}deg) scale(${layer.sprites[currentFrameIndex].flipX ? -1 : 1}, ${layer.sprites[currentFrameIndex].flipY ? -1 : 1})`,
                }"
                @mousedown="startDrag($event, layer.sprites[currentFrameIndex], layer.id)"
                @touchstart="handleTouchStart($event, layer.sprites[currentFrameIndex], layer.id)"
                @contextmenu.prevent="openContextMenu($event, layer.sprites[currentFrameIndex], layer.id)"
                draggable="false"
              />
            </template>
          </div>

          <!-- Mobile zoom controls -->
          <div class="absolute bottom-3 right-3 flex space-x-2 lg:hidden bg-white/80 dark:bg-gray-800/80 p-2 rounded-lg shadow-md">
            <button @click="increaseZoom" class="p-2 bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-100 rounded-lg transition-colors">
              <i class="fas fa-plus"></i>
            </button>
            <button @click="decreaseZoom" class="p-2 bg-gray-100 hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-700 dark:text-gray-100 rounded-lg transition-colors">
              <i class="fas fa-minus"></i>
            </button>
          </div>

          <!-- Drop zone overlay -->
          <div v-if="isDragOver" class="absolute inset-0 bg-blue-500/20 flex items-center justify-center pointer-events-none z-10 rounded-lg">
            <div class="bg-white dark:bg-gray-800 rounded-lg shadow-lg px-4 py-3 flex items-center gap-2 border border-blue-300 dark:border-blue-600">
              <i class="fas fa-plus-circle text-blue-500"></i>
              <span class="text-sm font-medium text-gray-700 dark:text-gray-200">Drop to add sprite at frame {{ currentFrameIndex + 1 }}</span>
            </div>
          </div>
        </div>
      </div>

      <div class="lg:w-80 xl:w-96 flex-shrink-0 lg:h-full lg:min-h-0 flex flex-col">
        <div class="bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 shadow-sm overflow-hidden flex-1 flex flex-col lg:overflow-y-auto">
          <!-- Playback Controls -->
          <div class="p-4 border-b border-gray-100 dark:border-gray-700 flex-shrink-0">
            <h3 class="text-xs font-bold text-gray-400 dark:text-gray-500 uppercase tracking-wider mb-3">Playback</h3>
            <div class="flex items-center gap-2">
              <button @click="togglePlayback" class="flex items-center justify-center gap-2 bg-blue-600 hover:bg-blue-700 text-white px-4 h-10 rounded-lg transition-all cursor-pointer flex-1 shadow-sm active:scale-95">
                <span v-if="isPlaying" class="flex items-center gap-2 font-medium">
                  <i class="fas fa-pause"></i>
                  Pause
                </span>
                <span v-else class="flex items-center gap-2 font-medium">
                  <i class="fas fa-play"></i>
                  Play
                </span>
              </button>
              <button @click="previousFrame" class="btn btn-secondary rounded-lg h-10 w-10" :disabled="isPlaying" :class="{ 'opacity-50 cursor-not-allowed': isPlaying }">
                <i class="fas fa-step-backward"></i>
              </button>
              <button @click="nextFrame" class="btn btn-secondary rounded-lg h-10 w-10" :disabled="isPlaying" :class="{ 'opacity-50 cursor-not-allowed': isPlaying }">
                <i class="fas fa-step-forward"></i>
              </button>
            </div>
          </div>

          <!-- Animation Settings -->
          <div class="p-4 border-b border-gray-100 dark:border-gray-700 space-y-5 flex-shrink-0">
            <h3 class="text-xs font-bold text-gray-400 dark:text-gray-500 uppercase tracking-wider mb-1">Animation</h3>

            <!-- Frame Navigation -->
            <div class="space-y-2">
              <div class="flex justify-between items-center">
                <span class="text-sm font-medium text-gray-700 dark:text-gray-300">Frame</span>
                <span class="text-xs font-mono text-gray-500 dark:text-gray-400 bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">{{ visibleFrameNumber }} / {{ visibleFramesCount }}</span>
              </div>
              <input type="range" min="0" :max="visibleFrames.length - 1" :value="visibleFrameIndex" @input="handleSliderInput" class="w-full h-1.5 bg-gray-200 dark:bg-gray-700 rounded-lg appearance-none cursor-pointer accent-blue-600" :disabled="isPlaying" :class="{ 'opacity-50': isPlaying }" />
            </div>

            <!-- FPS Control -->
            <div class="space-y-2">
              <div class="flex justify-between items-center">
                <span class="text-sm font-medium text-gray-700 dark:text-gray-300">Speed (FPS)</span>
                <span class="text-xs font-mono text-gray-500 dark:text-gray-400 bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">{{ fps }}</span>
              </div>
              <input type="range" min="1" max="60" v-model.number="fps" class="w-full h-1.5 bg-gray-200 dark:bg-gray-700 rounded-lg appearance-none cursor-pointer accent-blue-600" />
            </div>
          </div>

          <!-- View Options -->
          <div class="p-4 space-y-5 flex-1">
            <h3 class="text-xs font-bold text-gray-400 dark:text-gray-500 uppercase tracking-wider mb-1">View Options</h3>

            <!-- Zoom Control -->
            <div class="space-y-2">
              <div class="flex justify-between items-center">
                <span class="text-sm font-medium text-gray-700 dark:text-gray-300">Zoom</span>
                <span class="text-xs font-mono text-gray-500 dark:text-gray-400 bg-gray-100 dark:bg-gray-700 px-2 py-0.5 rounded">{{ Math.round(zoom * 100) }}%</span>
              </div>
              <input type="range" min="0.5" max="5" step="0.1" v-model.number="zoom" class="w-full h-1.5 bg-gray-200 dark:bg-gray-700 rounded-lg appearance-none cursor-pointer accent-blue-600" />
            </div>

            <!-- Toggles -->
            <div class="space-y-3 pt-2">
              <label class="flex items-center justify-between cursor-pointer group">
                <span class="text-sm text-gray-700 dark:text-gray-300 group-hover:text-gray-900 dark:group-hover:text-gray-100 transition-colors">Pixel perfect</span>
                <div class="relative inline-flex items-center cursor-pointer">
                  <input type="checkbox" v-model="settingsStore.pixelPerfect" class="sr-only peer" />
                  <div
                    class="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"
                  ></div>
                </div>
              </label>

              <label class="flex items-center justify-between cursor-pointer group">
                <span class="text-sm text-gray-700 dark:text-gray-300 group-hover:text-gray-900 dark:group-hover:text-gray-100 transition-colors">Reposition mode</span>
                <div class="relative inline-flex items-center cursor-pointer">
                  <input type="checkbox" v-model="isDraggable" class="sr-only peer" />
                  <div
                    class="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"
                  ></div>
                </div>
              </label>

              <div class="pl-4 border-l-2 border-gray-100 dark:border-gray-700 transition-all space-y-2" :class="{ 'opacity-50 pointer-events-none': !isDraggable }">
                <label class="flex items-center justify-between cursor-pointer group">
                  <span class="text-sm text-gray-600 dark:text-gray-400 group-hover:text-gray-800 dark:group-hover:text-gray-200 transition-colors">Apply to all layers</span>
                  <input type="checkbox" v-model="repositionAllLayers" class="w-4 h-4 text-blue-600 rounded border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600" :disabled="!isDraggable" />
                </label>
                <label class="flex items-center justify-between cursor-pointer group">
                  <span class="text-sm text-gray-600 dark:text-gray-400 group-hover:text-gray-800 dark:group-hover:text-gray-200 transition-colors">Arrow key movement</span>
                  <input type="checkbox" v-model="arrowKeyMovement" class="w-4 h-4 text-blue-600 rounded border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600" :disabled="!isDraggable" />
                </label>
              </div>

              <label class="flex items-center justify-between cursor-pointer group">
                <span class="text-sm text-gray-700 dark:text-gray-300 group-hover:text-gray-900 dark:group-hover:text-gray-100 transition-colors">Compare sprites</span>
                <div class="relative inline-flex items-center cursor-pointer">
                  <input type="checkbox" v-model="showAllSprites" class="sr-only peer" />
                  <div
                    class="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"
                  ></div>
                </div>
              </label>
            </div>
          </div>

          <!-- Current frame offset display -->
          <div v-if="currentFrameSprite" class="px-4 pb-4 flex-shrink-0">
            <div class="p-3 bg-gray-50 dark:bg-gray-700/50 rounded-lg border border-gray-100 dark:border-gray-700 flex items-center justify-between">
              <span class="text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wide">Offset</span>
              <span class="text-xs font-mono font-bold text-gray-700 dark:text-gray-200">X: {{ currentFrameSprite.x }} <span class="text-gray-300 dark:text-gray-600 mx-1">|</span> Y: {{ currentFrameSprite.y }}</span>
            </div>
          </div>

          <!-- Frame Selection (when Compare sprites is enabled) -->
          <div v-if="showAllSprites" class="border-t border-gray-100 dark:border-gray-700 p-4 flex-shrink-0">
            <div class="flex items-center justify-between mb-3">
              <h3 class="text-xs font-bold text-gray-400 dark:text-gray-500 uppercase tracking-wider">Visible Frames</h3>
              <div class="flex gap-1">
                <button @click="showAllFrames" class="px-2 py-1 text-[10px] font-medium bg-blue-50 text-blue-600 hover:bg-blue-100 dark:bg-blue-900/30 dark:text-blue-300 dark:hover:bg-blue-900/50 rounded transition-colors">All</button>
                <button @click="hideAllFrames" class="px-2 py-1 text-[10px] font-medium bg-gray-100 text-gray-600 hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-400 dark:hover:bg-gray-600 rounded transition-colors">None</button>
              </div>
            </div>
            <div class="bg-gray-50 dark:bg-gray-900/50 rounded-lg border border-gray-200 dark:border-gray-700 p-2">
              <div class="max-h-[150px] overflow-y-auto pr-1 custom-scrollbar">
                <div class="space-y-1">
                  <div v-for="(sprite, index) in compositeFrames" :key="sprite.id" class="flex items-center gap-3 px-2 py-1.5 hover:bg-white dark:hover:bg-gray-700 cursor-pointer rounded-md transition-colors group" @click="toggleHiddenFrame(index)">
                    <input
                      type="checkbox"
                      :checked="!hiddenFrames.includes(index)"
                      class="w-4 h-4 text-blue-600 rounded border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600"
                      @click.stop
                      @change="toggleHiddenFrame(index)"
                    />
                    <div class="w-8 h-8 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded flex items-center justify-center overflow-hidden flex-shrink-0 shadow-sm">
                      <img :src="sprite.url" class="max-w-full max-h-full object-contain" :style="{ ...(settingsStore.pixelPerfect ? { 'image-rendering': 'pixelated' } : {}), transform: `rotate(${sprite.rotation || 0}deg) scale(${sprite.flipX ? -1 : 1}, ${sprite.flipY ? -1 : 1})` }" />
                    </div>
                    <span class="text-xs font-mono text-gray-600 dark:text-gray-300 group-hover:text-gray-900 dark:group-hover:text-gray-100">Frame {{ index + 1 }}</span>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    <input ref="fileInput" type="file" accept="image/*" class="hidden" @change="handleFileChange" />
  </div>
</template>

<script setup lang="ts">
  import { ref, onMounted, watch, onUnmounted, computed } from 'vue';
  import { useSettingsStore } from '@/stores/useSettingsStore';
  import type { Layer, Sprite } from '@/types/sprites';
  import { getMaxDimensionsAcrossLayers } from '@/composables/useLayers';
  import { useZoom } from '@/composables/useZoom';
  import { useAnimationFrames } from '@/composables/useAnimationFrames';
  import { calculateNegativeSpacing } from '@/composables/useNegativeSpacing';

  const props = defineProps<{
    layers: Layer[];
    activeLayerId: string;
    columns: number;
  }>();

  const emit = defineEmits<{
    (e: 'updateSprite', id: string, x: number, y: number): void;
    (e: 'updateSpriteInLayer', layerId: string, spriteId: string, x: number, y: number): void;
    (e: 'dropSprite', layerId: string, frameIndex: number, files: File[]): void;
    (e: 'rotateSprite', id: string, angle: number): void;
    (e: 'flipSprite', id: string, direction: 'horizontal' | 'vertical'): void;
    (e: 'copySpriteToFrame', spriteId: string, targetLayerId: string, targetFrameIndex: number): void;
    (e: 'replaceSprite', id: string, file: File): void;
  }>();

  const previewContainerRef = ref<HTMLDivElement | null>(null);

  // Get settings from store
  const settingsStore = useSettingsStore();

  const {
    zoom,
    increase: increaseZoom,
    decrease: decreaseZoom,
  } = useZoom({
    allowedValues: [0.5, 1, 2, 3, 4, 5],
    initial: 1,
  });

  const getVisibleLayers = () => props.layers.filter(l => l.visible);
  const maxFrames = () => Math.max(0, ...getVisibleLayers().map(l => l.sprites.length));

  const { currentFrameIndex, isPlaying, fps, hiddenFrames, visibleFrames, visibleFramesCount, visibleFrameIndex, visibleFrameNumber, togglePlayback, nextFrame, previousFrame, handleSliderInput, toggleHiddenFrame, showAllFrames, hideAllFrames, stopAnimation } = useAnimationFrames({
    sprites: () => {
      const len = maxFrames();
      const frames: Sprite[] = [];
      for (let i = 0; i < len; i++) {
        const s = getVisibleLayers().find(l => l.sprites[i])?.sprites[i];
        if (s) frames.push(s);
      }
      return frames;
    },
    onDraw: () => {}, // No longer needed for canvas drawing
  });

  // Preview state
  const isDraggable = ref(false);
  const repositionAllLayers = ref(false);
  const arrowKeyMovement = ref(false);
  const showAllSprites = ref(false);
  const isDragOver = ref(false);

  // Context menu state
  const showContextMenu = ref(false);
  const contextMenuX = ref(0);
  const contextMenuY = ref(0);
  const contextMenuSpriteId = ref<string | null>(null);
  const contextMenuLayerId = ref<string | null>(null);
  const fileInput = ref<HTMLInputElement | null>(null);
  const replacingSpriteId = ref<string | null>(null);

  // Copy to frame modal state
  const showCopyToFrameModal = ref(false);
  const copyTargetFrame = ref(0);
  const copyTargetLayerId = ref(props.activeLayerId);

  const maxFrameCount = computed(() => {
    const maxLen = Math.max(1, ...props.layers.map(l => l.sprites.length));
    return maxLen + 1; // Allow copying to one frame beyond current max
  });

  // Drag and drop for new sprites
  const onDragOver = () => {
    isDragOver.value = true;
  };

  const onDragLeave = () => {
    isDragOver.value = false;
  };

  const onDrop = (event: DragEvent) => {
    isDragOver.value = false;

    if (!event.dataTransfer?.files.length) return;

    const files = Array.from(event.dataTransfer.files).filter(file => file.type.startsWith('image/'));

    if (files.length === 0) return;

    emit('dropSprite', props.activeLayerId, currentFrameIndex.value, files);
  };

  const compositeFrames = computed<Sprite[]>(() => {
    // Show frames from the active layer for the thumbnail list
    const activeLayer = props.layers.find(l => l.id === props.activeLayerId);
    if (!activeLayer) {
      // Fallback to first visible layer if no active layer
      const v = getVisibleLayers();
      const len = maxFrames();
      const arr: Sprite[] = [];
      for (let i = 0; i < len; i++) {
        const s = v.find(l => l.sprites[i])?.sprites[i];
        if (s) arr.push(s);
      }
      return arr;
    }
    return activeLayer.sprites;
  });

  const currentFrameSprite = computed<Sprite | null>(() => {
    const layer = props.layers.find(l => l.id === props.activeLayerId);
    if (!layer) return null;
    return layer.sprites[currentFrameIndex.value] || null;
  });

  // Computed cell dimensions
  const cellDimensions = computed(() => {
    const visibleLayers = getVisibleLayers();
    // If manual cell size is enabled, use manual values
    if (settingsStore.manualCellSizeEnabled) {
      return {
        cellWidth: settingsStore.manualCellWidth,
        cellHeight: settingsStore.manualCellHeight,
        negativeSpacing: 0,
      };
    }

    // Otherwise, calculate from sprite dimensions
    const { maxWidth, maxHeight } = getMaxDimensionsAcrossLayers(visibleLayers);
    const allSprites = visibleLayers.flatMap(l => l.sprites);
    const negativeSpacing = calculateNegativeSpacing(allSprites, settingsStore.negativeSpacingEnabled);
    return {
      cellWidth: maxWidth + negativeSpacing,
      cellHeight: maxHeight + negativeSpacing,
      negativeSpacing,
    };
  });

  // Helper for background image (dark mode friendly)
  const getPreviewBackgroundImage = () => {
    if (settingsStore.backgroundColor === 'transparent' && settingsStore.checkerboardEnabled) {
      const color = settingsStore.darkMode ? '#4b5563' : '#d1d5db';
      return `linear-gradient(45deg, ${color} 25%, transparent 25%), linear-gradient(-45deg, ${color} 25%, transparent 25%), linear-gradient(45deg, transparent 75%, ${color} 75%), linear-gradient(-45deg, transparent 75%, ${color} 75%)`;
    }
    return 'none';
  };

  // Dragging state
  const isDragging = ref(false);
  const activeSpriteId = ref<string | null>(null);
  const activeLayerId = ref<string | null>(null);
  const dragStartX = ref(0);
  const dragStartY = ref(0);
  const spritePosBeforeDrag = ref({ x: 0, y: 0 });
  const allSpritesPosBeforeDrag = ref<Map<string, { x: number; y: number }>>(new Map());

  // Drag functionality
  const startDrag = (event: MouseEvent, sprite: Sprite, layerId: string) => {
    if (!isDraggable.value || !previewContainerRef.value) return;

    const rect = previewContainerRef.value.getBoundingClientRect();
    const scaleX = previewContainerRef.value.offsetWidth / (rect.width / zoom.value);
    const scaleY = previewContainerRef.value.offsetHeight / (rect.height / zoom.value);

    const mouseX = ((event.clientX - rect.left) / zoom.value) * scaleX;
    const mouseY = ((event.clientY - rect.top) / zoom.value) * scaleY;

    if (repositionAllLayers.value) {
      isDragging.value = true;
      activeSpriteId.value = 'ALL_LAYERS'; // Special marker for all layers
      dragStartX.value = mouseX;
      dragStartY.value = mouseY;

      // Store initial positions for all sprites in this frame from all visible layers
      allSpritesPosBeforeDrag.value.clear();
      const visibleLayers = getVisibleLayers();
      visibleLayers.forEach(layer => {
        const s = layer.sprites[currentFrameIndex.value];
        if (s) {
          allSpritesPosBeforeDrag.value.set(s.id, { x: s.x, y: s.y });
        }
      });
    } else {
      isDragging.value = true;
      activeSpriteId.value = sprite.id;
      activeLayerId.value = layerId;
      dragStartX.value = mouseX;
      dragStartY.value = mouseY;
      spritePosBeforeDrag.value = { x: sprite.x, y: sprite.y };
    }
  };

  const drag = (event: MouseEvent) => {
    if (!isDragging.value || !activeSpriteId.value || !previewContainerRef.value) return;

    const rect = previewContainerRef.value.getBoundingClientRect();
    const scaleX = previewContainerRef.value.offsetWidth / (rect.width / zoom.value);
    const scaleY = previewContainerRef.value.offsetHeight / (rect.height / zoom.value);

    const mouseX = ((event.clientX - rect.left) / zoom.value) * scaleX;
    const mouseY = ((event.clientY - rect.top) / zoom.value) * scaleY;

    const deltaX = Math.round(mouseX - dragStartX.value);
    const deltaY = Math.round(mouseY - dragStartY.value);

    const { cellWidth, cellHeight, negativeSpacing } = cellDimensions.value;

    if (activeSpriteId.value === 'ALL_LAYERS') {
      // Move all sprites in current frame from all visible layers
      const visibleLayers = getVisibleLayers();
      visibleLayers.forEach(layer => {
        const sprite = layer.sprites[currentFrameIndex.value];
        if (!sprite) return;

        const originalPos = allSpritesPosBeforeDrag.value.get(sprite.id);
        if (!originalPos) return;

        // Calculate new position with constraints
        let newX = Math.round(originalPos.x + deltaX);
        let newY = Math.round(originalPos.y + deltaY);

        // Constrain movement within expanded cell
        newX = Math.max(-negativeSpacing, Math.min(cellWidth - negativeSpacing - sprite.width, newX));
        newY = Math.max(-negativeSpacing, Math.min(cellHeight - negativeSpacing - sprite.height, newY));

        emit('updateSpriteInLayer', layer.id, sprite.id, newX, newY);
      });
    } else {
      // Move only the active layer sprite
      const activeSprite = props.layers.find(l => l.id === activeLayerId.value)?.sprites[currentFrameIndex.value];
      if (!activeSprite || activeSprite.id !== activeSpriteId.value) return;

      // Calculate new position with constraints and round to integers
      let newX = Math.round(spritePosBeforeDrag.value.x + deltaX);
      let newY = Math.round(spritePosBeforeDrag.value.y + deltaY);

      // Constrain movement within expanded cell (allow negative values up to -negativeSpacing)
      newX = Math.max(-negativeSpacing, Math.min(cellWidth - negativeSpacing - activeSprite.width, newX));
      newY = Math.max(-negativeSpacing, Math.min(cellHeight - negativeSpacing - activeSprite.height, newY));

      emit('updateSprite', activeSpriteId.value, newX, newY);
    }
  };

  const stopDrag = () => {
    isDragging.value = false;
    activeSpriteId.value = null;
    activeLayerId.value = null;
  };

  const handleTouchStart = (event: TouchEvent, sprite: Sprite, layerId: string) => {
    if (!isDraggable.value) return;

    if (event.touches.length === 1) {
      const touch = event.touches[0];
      const mouseEvent = new MouseEvent('mousedown', {
        clientX: touch.clientX,
        clientY: touch.clientY,
      });
      startDrag(mouseEvent, sprite, layerId);
    }
  };

  const handleTouchMove = (event: TouchEvent) => {
    if (!isDraggable.value) return;

    if (isDragging.value) {
      event.preventDefault(); // Only prevent default when actually dragging
    }

    if (event.touches.length === 1) {
      const touch = event.touches[0];
      const mouseEvent = new MouseEvent('mousemove', {
        clientX: touch.clientX,
        clientY: touch.clientY,
      });
      drag(mouseEvent);
    }
  };

  // Arrow key movement handler
  const handleKeyDown = (event: KeyboardEvent) => {
    if (!isDraggable.value || !arrowKeyMovement.value) return;

    const arrowKeys = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
    if (!arrowKeys.includes(event.key)) return;

    event.preventDefault();

    let deltaX = 0;
    let deltaY = 0;
    const step = event.shiftKey ? 10 : 1; // Hold Shift for larger steps

    switch (event.key) {
      case 'ArrowUp':
        deltaY = -step;
        break;
      case 'ArrowDown':
        deltaY = step;
        break;
      case 'ArrowLeft':
        deltaX = -step;
        break;
      case 'ArrowRight':
        deltaX = step;
        break;
    }

    const { cellWidth, cellHeight, negativeSpacing } = cellDimensions.value;

    if (repositionAllLayers.value) {
      // Move all sprites in current frame from all visible layers
      const visibleLayers = getVisibleLayers();
      visibleLayers.forEach(layer => {
        const sprite = layer.sprites[currentFrameIndex.value];
        if (!sprite) return;

        let newX = Math.round(sprite.x + deltaX);
        let newY = Math.round(sprite.y + deltaY);

        // Constrain movement within expanded cell
        newX = Math.max(-negativeSpacing, Math.min(cellWidth - negativeSpacing - sprite.width, newX));
        newY = Math.max(-negativeSpacing, Math.min(cellHeight - negativeSpacing - sprite.height, newY));

        emit('updateSpriteInLayer', layer.id, sprite.id, newX, newY);
      });
    } else {
      // Move only the active layer sprite
      const activeLayer = props.layers.find(l => l.id === props.activeLayerId);
      if (!activeLayer) return;

      const sprite = activeLayer.sprites[currentFrameIndex.value];
      if (!sprite) return;

      let newX = Math.round(sprite.x + deltaX);
      let newY = Math.round(sprite.y + deltaY);

      // Constrain movement within expanded cell
      newX = Math.max(-negativeSpacing, Math.min(cellWidth - negativeSpacing - sprite.width, newX));
      newY = Math.max(-negativeSpacing, Math.min(cellHeight - negativeSpacing - sprite.height, newY));

      emit('updateSprite', sprite.id, newX, newY);
    }
  };

  // Lifecycle hooks
  onMounted(() => {
    window.addEventListener('keydown', handleKeyDown);
  });

  onUnmounted(() => {
    stopAnimation();
    window.removeEventListener('keydown', handleKeyDown);
  });

  // Watchers - most canvas-related watchers removed
  // Keep layer watchers to ensure reactivity
  watch(
    () => props.layers,
    () => {},
    { deep: true }
  );
  watch(
    () => props.activeLayerId,
    () => {}
  );
  watch(currentFrameIndex, () => {});

  // Context menu functions
  const openContextMenu = (event: MouseEvent, sprite: Sprite, layerId: string) => {
    event.preventDefault();
    contextMenuSpriteId.value = sprite.id;
    contextMenuLayerId.value = layerId;
    contextMenuX.value = event.clientX;
    contextMenuY.value = event.clientY;
    showContextMenu.value = true;
  };

  const hideContextMenu = () => {
    showContextMenu.value = false;
    contextMenuSpriteId.value = null;
    contextMenuLayerId.value = null;
  };

  const rotateSpriteInMenu = (angle: number) => {
    if (contextMenuSpriteId.value) {
      emit('rotateSprite', contextMenuSpriteId.value, angle);
    }
    hideContextMenu();
  };

  const flipSpriteInMenu = (direction: 'horizontal' | 'vertical') => {
    if (contextMenuSpriteId.value) {
      emit('flipSprite', contextMenuSpriteId.value, direction);
    }
    hideContextMenu();
  };

  const openCopyToFrameModal = () => {
    if (contextMenuSpriteId.value) {
      copyTargetLayerId.value = contextMenuLayerId.value || props.activeLayerId;
      copyTargetFrame.value = 0;
      showCopyToFrameModal.value = true;
      showContextMenu.value = false;
    }
  };

  const closeCopyToFrameModal = () => {
    showCopyToFrameModal.value = false;
  };

  const confirmCopyToFrame = () => {
    if (contextMenuSpriteId.value) {
      emit('copySpriteToFrame', contextMenuSpriteId.value, copyTargetLayerId.value, copyTargetFrame.value);
      closeCopyToFrameModal();
      contextMenuSpriteId.value = null;
    }
  };

  const replaceSprite = () => {
    if (contextMenuSpriteId.value && fileInput.value) {
      replacingSpriteId.value = contextMenuSpriteId.value;
      fileInput.value.click();
      hideContextMenu();
    }
  };

  const handleFileChange = (event: Event) => {
    const input = event.target as HTMLInputElement;

    if (input.files && input.files.length > 0) {
      const file = input.files[0];
      if (file.type.startsWith('image/') && replacingSpriteId.value) {
        emit('replaceSprite', replacingSpriteId.value, file);
      }
    }
    replacingSpriteId.value = null;
    input.value = '';
  };
</script>

<style scoped>
  /* Custom styling for range inputs */
  input[type='range']::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: #3b82f6;
    cursor: pointer;
  }

  input[type='range']::-moz-range-thumb {
    width: 16px;
    height: 16px;
    border-radius: 50%;
    background: #3b82f6;
    cursor: pointer;
    border: none;
  }

  /* Custom scrollbar for controls panel */
  .custom-scrollbar::-webkit-scrollbar,
  .lg\:overflow-y-auto::-webkit-scrollbar {
    width: 6px;
  }

  .custom-scrollbar::-webkit-scrollbar-track,
  .lg\:overflow-y-auto::-webkit-scrollbar-track {
    background: transparent;
  }

  .custom-scrollbar::-webkit-scrollbar-thumb,
  .lg\:overflow-y-auto::-webkit-scrollbar-thumb {
    background-color: rgba(156, 163, 175, 0.5);
    border-radius: 3px;
  }

  .custom-scrollbar::-webkit-scrollbar-thumb:hover,
  .lg\:overflow-y-auto::-webkit-scrollbar-thumb:hover {
    background-color: rgba(156, 163, 175, 0.8);
  }

  :global(.dark) .custom-scrollbar::-webkit-scrollbar-thumb,
  :global(.dark) .lg\:overflow-y-auto::-webkit-scrollbar-thumb {
    background-color: rgba(75, 85, 99, 0.5);
  }

  :global(.dark) .custom-scrollbar::-webkit-scrollbar-thumb:hover,
  :global(.dark) .lg\:overflow-y-auto::-webkit-scrollbar-thumb:hover {
    background-color: rgba(75, 85, 99, 0.8);
  }
</style>
