<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="addSprite" class="w-full px-3 py-1.5 text-left hover:bg-blue-50 dark:hover:bg-blue-900/30 text-gray-700 dark:text-gray-200 flex items-center gap-2 transition-colors text-sm">
        <i class="fas fa-plus text-blue-600 dark:text-blue-400 text-xs w-4"></i>
        <span>Add sprite</span>
      </button>
      <button v-if="contextMenuSpriteId" @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 v-if="contextMenuSpriteId" @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 v-if="contextMenuSpriteId" @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 v-if="contextMenuSpriteId" @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 v-if="contextMenuSpriteId" @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 v-if="contextMenuSpriteId" class="h-px bg-gray-200 dark:bg-gray-600 my-1"></div>
      <button v-if="contextMenuSpriteId" @click="removeSprite" class="w-full px-3 py-1.5 text-left hover:bg-red-50 dark:hover:bg-red-900/30 text-red-600 dark:text-red-400 flex items-center gap-2 transition-colors text-sm">
        <i class="fas fa-trash text-xs w-4"></i>
        <span>{{ selectedSpriteIds.size > 1 ? `Remove ${selectedSpriteIds.size} sprites` : 'Remove sprite' }}</span>
      </button>
    </div>

    <!-- Copy to Frame Modal -->
    <CopyToFrameModal :is-open="showCopyToFrameModal" :layers="props.layers" :initial-layer-id="copyTargetLayerId" @close="closeCopyToFrameModal" @copy="confirmCopyToFrame" />
  </Teleport>

  <div class="h-full w-full flex flex-col">
    <div class="relative bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-xl shadow-sm overflow-auto h-full w-full">
      <div class="canvas-container touch-manipulation relative inline-block min-w-full">
        <div
          ref="gridContainerRef"
          :style="{
            transform: `scale(${props.zoom})`,
            transformOrigin: 'top left',
            width: `${gridDimensions.width}px`,
            height: `${gridDimensions.height}px`,
            position: 'relative',
          }"
          class="inline-block"
          @mousedown="startDrag"
          @mousemove="drag"
          @mouseup="stopDrag"
          @mouseleave="stopDrag"
          @touchstart="handleTouchStart"
          @touchmove="handleTouchMove"
          @touchend="stopDrag"
          @contextmenu.prevent
          @dragover="handleDragOver"
          @dragenter="handleDragEnter"
          @dragleave="onDragLeave"
          @drop="handleDrop"
          :class="{ 'ring-4 ring-blue-500 ring-opacity-50': isDragOver }"
        >
          <!-- Grid cells -->
          <div
            v-for="cellIndex in totalCells"
            :key="`cell-${cellIndex - 1}`"
            class="absolute"
            :style="{
              left: `${Math.round(getCellPosition(cellIndex - 1).x)}px`,
              top: `${Math.round(getCellPosition(cellIndex - 1).y)}px`,
              width: `${Math.round(gridMetrics.maxWidth)}px`,
              height: `${Math.round(gridMetrics.maxHeight)}px`,
              backgroundColor: getCellBackground(),
              backgroundImage: getCellBackgroundImage(),
              backgroundSize: getCellBackgroundSize(),
              backgroundPosition: getCellBackgroundPosition(),
              border: `1px solid ${settingsStore.darkMode ? '#4b5563' : '#e5e7eb'}`,
            }"
            :class="{
              'bg-blue-500 bg-opacity-20': highlightCell && highlightCell.col === (cellIndex - 1) % columns && highlightCell.row === Math.floor((cellIndex - 1) / columns),
            }"
          ></div>

          <!-- Background sprites (for compare mode) -->
          <template v-if="showAllSprites">
            <template v-for="layer in visibleLayers" :key="`bg-layer-${layer.id}`">
              <template v-for="(sprite, spriteIndex) in layer.sprites" :key="`bg-${sprite.id}`">
                <template v-for="cellIndex in totalCells" :key="`bg-${sprite.id}-${cellIndex}`">
                  <img
                    v-if="spriteIndex !== cellIndex - 1 && !(activeSpriteId === sprite.id && ghostSprite)"
                    :src="sprite.url"
                    class="absolute pointer-events-none"
                    :style="{
                      left: `${Math.round(getCellPosition(cellIndex - 1).x + gridMetrics.negativeSpacing + sprite.x)}px`,
                      top: `${Math.round(getCellPosition(cellIndex - 1).y + gridMetrics.negativeSpacing + sprite.y)}px`,
                      width: `${Math.round(sprite.width)}px`,
                      height: `${Math.round(sprite.height)}px`,
                      opacity: '0.25',
                      imageRendering: settingsStore.pixelPerfect ? 'pixelated' : 'auto',
                    }"
                    draggable="false"
                  />
                </template>
              </template>
            </template>
          </template>

          <!-- Layer sprites -->
          <template v-for="layer in layers" :key="layer.id">
            <template v-if="layer.visible">
              <template v-for="(sprite, index) in layer.sprites" :key="sprite.id">
                <img
                  v-if="!(activeSpriteId === sprite.id && ghostSprite)"
                  :src="sprite.url"
                  class="absolute cursor-move transition-transform duration-200"
                  :class="{ 'ring-2 ring-blue-500 ring-offset-1 dark:ring-offset-gray-900': showActiveBorder && selectedSpriteIds.has(sprite.id) }"
                  :style="{
                    left: `${Math.round(getCellPosition(index).x + gridMetrics.negativeSpacing + sprite.x)}px`,
                    top: `${Math.round(getCellPosition(index).y + gridMetrics.negativeSpacing + sprite.y)}px`,
                    width: `${Math.round(sprite.width)}px`,
                    height: `${Math.round(sprite.height)}px`,
                    opacity: layer.id === activeLayerId ? '1' : '0.85',
                    imageRendering: settingsStore.pixelPerfect ? 'pixelated' : 'auto',
                    transform: `rotate(${sprite.rotation}deg) scale(${sprite.flipX ? -1 : 1}, ${sprite.flipY ? -1 : 1})`,
                  }"
                  :data-sprite-id="sprite.id"
                  :data-layer-id="layer.id"
                  draggable="false"
                />
              </template>
            </template>
          </template>

          <!-- Ghost sprite (while dragging) -->
          <img
            v-if="ghostSprite && activeSpriteId"
            :src="activeSpriteSprite?.url"
            class="absolute pointer-events-none"
            :style="{
              left: `${Math.round(ghostSprite.x)}px`,
              top: `${Math.round(ghostSprite.y)}px`,
              width: `${Math.round(activeSpriteSprite?.width || 0)}px`,
              height: `${Math.round(activeSpriteSprite?.height || 0)}px`,
              opacity: '0.6',
              imageRendering: settingsStore.pixelPerfect ? 'pixelated' : 'auto',
              transform: `rotate(${activeSpriteSprite?.rotation || 0}deg) scale(${activeSpriteSprite?.flipX ? -1 : 1}, ${activeSpriteSprite?.flipY ? -1 : 1})`,
            }"
            draggable="false"
          />

          <!-- Offset labels -->
          <div
            v-if="showOffsetLabels"
            v-for="position in spritePositions"
            :key="`label-${position.id}`"
            class="absolute text-[23px] leading-none font-mono text-cyan-600 dark:text-cyan-400 bg-white/90 dark:bg-gray-900/90 px-1 py-0.5 rounded-sm pointer-events-none"
            :style="{
              left: `${Math.round(position.cellX + position.maxWidth - 2)}px`,
              top: `${Math.round(position.cellY + position.maxHeight - 2)}px`,
              transform: 'translate(-100%, -100%)',
            }"
          >
            <span>{{ position.x }},{{ position.y }}</span>
          </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, toRef, computed, nextTick } from 'vue';
  import { useSettingsStore } from '@/stores/useSettingsStore';
  import type { Sprite } from '@/types/sprites';
  import { useDragSprite } from '@/composables/useDragSprite';
  import { useFileDrop } from '@/composables/useFileDrop';
  import { useGridMetrics } from '@/composables/useGridMetrics';
  import { useBackgroundStyles } from '@/composables/useBackgroundStyles';
  import CopyToFrameModal from './utilities/CopyToFrameModal.vue';

  import type { Layer } from '@/types/sprites';

  const props = defineProps<{
    layers: Layer[];
    activeLayerId: string;
    columns: number;
    zoom: number;
    isMultiSelectMode: boolean;
    showActiveBorder: boolean;
    allowCellSwap: boolean;
    showAllSprites: boolean;
    showOffsetLabels: boolean;
  }>();

  const emit = defineEmits<{
    (e: 'updateSprite', id: string, x: number, y: number): void;
    (e: 'updateSpriteCell', id: string, newIndex: number): void;
    (e: 'removeSprite', id: string): void;
    (e: 'removeSprites', ids: string[]): void;
    (e: 'replaceSprite', id: string, file: File): void;
    (e: 'addSprite', file: File, index?: number): void;
    (e: 'addSpriteWithResize', file: 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;
  }>();

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

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

  const getMousePosition = (event: MouseEvent, z?: number) => {
    if (!gridContainerRef.value) return null;
    const currentZoom = z ?? props.zoom;
    const rect = gridContainerRef.value.getBoundingClientRect();
    const scaleX = gridContainerRef.value.offsetWidth / (rect.width / currentZoom);
    const scaleY = gridContainerRef.value.offsetHeight / (rect.height / currentZoom);
    return {
      x: ((event.clientX - rect.left) / currentZoom) * scaleX,
      y: ((event.clientY - rect.top) / currentZoom) * scaleY,
    };
  };

  const {
    isDragging,
    activeSpriteId,
    ghostSprite,
    highlightCell,
    spritePositions,
    startDrag: dragStart,
    drag: dragMove,
    stopDrag,
    handleTouchStart,
    handleTouchMove,
    findSpriteAtPosition,
    findCellAtPosition,
    calculateMaxDimensions,
  } = useDragSprite({
    sprites: computed(() => props.layers.find(l => l.id === props.activeLayerId)?.sprites ?? []),
    layers: toRef(props, 'layers'),
    columns: toRef(props, 'columns'),
    zoom: toRef(props, 'zoom'),
    allowCellSwap: toRef(props, 'allowCellSwap'),
    negativeSpacingEnabled: toRef(settingsStore, 'negativeSpacingEnabled'),
    manualCellSizeEnabled: toRef(settingsStore, 'manualCellSizeEnabled'),
    manualCellWidth: toRef(settingsStore, 'manualCellWidth'),
    manualCellHeight: toRef(settingsStore, 'manualCellHeight'),
    getMousePosition,
    onUpdateSprite: (id, x, y) => emit('updateSprite', id, x, y),
    onUpdateSpriteCell: (id, newIndex) => emit('updateSpriteCell', id, newIndex),
    onDraw: () => {},
  });

  const activeSprites = computed(() => props.layers.find(l => l.id === props.activeLayerId)?.sprites ?? []);
  const visibleLayers = computed(() => props.layers.filter(l => l.visible));
  const activeSpriteSprite = computed(() => activeSprites.value.find(s => s.id === activeSpriteId.value));

  const { isDragOver, handleDragOver, handleDragEnter, handleDragLeave, handleDrop } = useFileDrop({
    sprites: activeSprites,
    onAddSprite: file => emit('addSprite', file),
    onAddSpriteWithResize: file => emit('addSpriteWithResize', file),
  });

  const showContextMenu = ref(false);
  const contextMenuX = ref(0);
  const contextMenuY = ref(0);
  const contextMenuIndex = ref<number | null>(null);
  const contextMenuSpriteId = ref<string | null>(null);
  const selectedSpriteIds = ref<Set<string>>(new Set());
  const replacingSpriteId = ref<string | null>(null);
  const fileInput = ref<HTMLInputElement | null>(null);

  // Copy to frame modal state
  const showCopyToFrameModal = ref(false);
  const copyTargetLayerId = ref(props.activeLayerId);
  const copySpriteId = ref<string | null>(null);

  // Clear selection when toggling multi-select mode
  watch(
    () => props.isMultiSelectMode,
    () => {
      selectedSpriteIds.value.clear();
    }
  );

  // Use the new useGridMetrics composable for consistent calculations
  const { gridMetrics: gridMetricsRef, getCellPosition: getCellPositionHelper } = useGridMetrics({
    layers: toRef(props, 'layers'),
    negativeSpacingEnabled: toRef(settingsStore, 'negativeSpacingEnabled'),
    manualCellSizeEnabled: toRef(settingsStore, 'manualCellSizeEnabled'),
    manualCellWidth: toRef(settingsStore, 'manualCellWidth'),
    manualCellHeight: toRef(settingsStore, 'manualCellHeight'),
  });

  const gridMetrics = gridMetricsRef;

  const totalCells = computed(() => {
    // Use all layers regardless of visibility to keep canvas size stable
    const maxLen = Math.max(1, ...props.layers.map(l => l.sprites.length));
    return Math.max(1, Math.ceil(maxLen / props.columns)) * props.columns;
  });

  const gridDimensions = computed(() => {
    // Use all layers regardless of visibility to keep canvas size stable
    const maxLen = Math.max(1, ...props.layers.map(l => l.sprites.length));
    const rows = Math.max(1, Math.ceil(maxLen / props.columns));
    return {
      width: Math.round(gridMetrics.value.maxWidth * props.columns),
      height: Math.round(gridMetrics.value.maxHeight * rows),
    };
  });

  const getCellPosition = (index: number) => {
    return getCellPositionHelper(index, props.columns, gridMetrics.value);
  };

  // Use the new useBackgroundStyles composable for consistent background styling
  const {
    backgroundColor: cellBackgroundColor,
    backgroundImage: cellBackgroundImage,
    backgroundSize: cellBackgroundSize,
    backgroundPosition: cellBackgroundPosition,
  } = useBackgroundStyles({
    backgroundColor: toRef(settingsStore, 'backgroundColor'),
    checkerboardEnabled: toRef(settingsStore, 'checkerboardEnabled'),
    darkMode: toRef(settingsStore, 'darkMode'),
  });

  const getCellBackground = () => cellBackgroundColor.value;
  const getCellBackgroundImage = () => cellBackgroundImage.value;
  const getCellBackgroundSize = () => cellBackgroundSize.value;
  const getCellBackgroundPosition = () => cellBackgroundPosition.value;

  const startDrag = (event: MouseEvent) => {
    // If the click originated from an interactive element (button, link, input), ignore drag handling
    const target = event.target as HTMLElement;
    if (target && target.closest('button, a, input, select, textarea')) {
      return;
    }
    if (!gridContainerRef.value) return;

    // Hide context menu if open
    showContextMenu.value = false;

    // Handle right-click for context menu
    if ('button' in event && (event as MouseEvent).button === 2) {
      event.preventDefault();
      const pos = getMousePosition(event, props.zoom);
      if (!pos) return;

      const clickedSprite = findSpriteAtPosition(pos.x, pos.y);
      contextMenuIndex.value = findCellAtPosition(pos.x, pos.y)?.index ?? null;
      contextMenuSpriteId.value = clickedSprite?.id || null;

      if (clickedSprite) {
        // If the right-clicked sprite is not in the selection, clear selection and select just this one
        if (!selectedSpriteIds.value.has(clickedSprite.id)) {
          selectedSpriteIds.value.clear();
          selectedSpriteIds.value.add(clickedSprite.id);
        }
        // If it IS in the selection, keep the current selection (so we can apply action to all)
      } else {
        // Right click on empty space
        selectedSpriteIds.value.clear();
      }

      contextMenuX.value = event.clientX;
      contextMenuY.value = event.clientY;

      showContextMenu.value = true;
      return;
    }

    // Ignore non-left mouse buttons
    if ('button' in event && (event as MouseEvent).button !== 0) return;

    // Handle selection logic for left click
    const pos = getMousePosition(event, props.zoom);
    if (pos) {
      const clickedSprite = findSpriteAtPosition(pos.x, pos.y);
      if (clickedSprite) {
        // Selection logic with multi-select mode check
        if (event.ctrlKey || event.metaKey || props.isMultiSelectMode) {
          // Toggle selection
          if (selectedSpriteIds.value.has(clickedSprite.id)) {
            selectedSpriteIds.value.delete(clickedSprite.id);
          } else {
            selectedSpriteIds.value.add(clickedSprite.id);
          }
        } else {
          // Single select (but don't clear if dragging starts immediately?
          // Usually standard behavior is to clear others unless shift/ctrl held)
          if (!selectedSpriteIds.value.has(clickedSprite.id)) {
            selectedSpriteIds.value.clear();
            selectedSpriteIds.value.add(clickedSprite.id);
          }
        }
      } else {
        // Clicked on empty space
        selectedSpriteIds.value.clear();
      }
    }

    // Delegate to composable for actual drag handling
    dragStart(event);
  };

  const pendingDrag = ref(false);
  const latestEvent = ref<MouseEvent | null>(null);

  const drag = (event: MouseEvent) => {
    // Store the latest event and schedule a single animation frame update
    latestEvent.value = event;
    if (!pendingDrag.value) {
      pendingDrag.value = true;
      requestAnimationFrame(() => {
        if (latestEvent.value) {
          dragMove(latestEvent.value);
        }
        pendingDrag.value = false;
        latestEvent.value = null;
      });
    }
  };

  const removeSprite = () => {
    if (selectedSpriteIds.value.size > 0) {
      emit('removeSprites', Array.from(selectedSpriteIds.value));
      selectedSpriteIds.value.clear();
      showContextMenu.value = false;
      contextMenuSpriteId.value = null;
    } else if (contextMenuSpriteId.value) {
      emit('removeSprite', contextMenuSpriteId.value);
      showContextMenu.value = false;
      contextMenuSpriteId.value = null;
    }
  };

  const rotateSpriteInMenu = (angle: number) => {
    if (selectedSpriteIds.value.size > 0) {
      selectedSpriteIds.value.forEach(id => {
        emit('rotateSprite', id, angle);
      });
    } else if (contextMenuSpriteId.value) {
      emit('rotateSprite', contextMenuSpriteId.value, angle);
    }
    showContextMenu.value = false;
  };

  const flipSpriteInMenu = (direction: 'horizontal' | 'vertical') => {
    if (selectedSpriteIds.value.size > 0) {
      selectedSpriteIds.value.forEach(id => {
        emit('flipSprite', id, direction);
      });
    } else if (contextMenuSpriteId.value) {
      emit('flipSprite', contextMenuSpriteId.value, direction);
    }
    showContextMenu.value = false;
  };

  const handleKeyDown = (event: KeyboardEvent) => {
    if (event.key === 'Delete' || event.key === 'Backspace') {
      // Don't delete if editing text/input
      if (document.activeElement?.tagName === 'INPUT' || document.activeElement?.tagName === 'TEXTAREA') return;

      if (selectedSpriteIds.value.size > 0) {
        emit('removeSprites', Array.from(selectedSpriteIds.value));
        selectedSpriteIds.value.clear();
      }
    }
  };

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

  const addSprite = () => {
    if (fileInput.value) {
      fileInput.value.click();
      showContextMenu.value = false;
      contextMenuSpriteId.value = null;
    }
  };

  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/')) {
        if (replacingSpriteId.value) {
          emit('replaceSprite', replacingSpriteId.value, file);
        } else {
          if (contextMenuIndex.value !== null) {
            emit('addSprite', file, contextMenuIndex.value);
          } else {
            emit('addSprite', file);
          }
        }
      } else {
        alert('Please select an image file.');
      }
    }
    replacingSpriteId.value = null;
    contextMenuIndex.value = null;
    input.value = '';
  };

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

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

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

  const confirmCopyToFrame = (targetLayerId: string, targetFrameIndex: number) => {
    if (copySpriteId.value) {
      emit('copySpriteToFrame', copySpriteId.value, targetLayerId, targetFrameIndex);
      closeCopyToFrameModal();
    }
  };

  const onDragLeave = (event: DragEvent) => {
    handleDragLeave(event, gridContainerRef.value);
  };

  onMounted(() => {
    document.addEventListener('mouseup', stopDrag);
    document.addEventListener('keydown', handleKeyDown);
  });

  onUnmounted(() => {
    document.removeEventListener('mouseup', stopDrag);
    document.removeEventListener('keydown', handleKeyDown);
  });

  // Watch for background color changes
</script>

<style scoped></style>
