<template>
  <Teleport to="body">
    <div v-if="showContextMenu" @click.stop class="fixed bg-white dark:bg-gray-800 border-2 border-gray-300 dark:border-gray-600 rounded-xl shadow-2xl z-50 py-2 min-w-[200px] overflow-hidden" :style="{ left: contextMenuX + 'px', top: contextMenuY + 'px' }">
      <button @click="addSprite" class="w-full px-5 py-3 text-left hover:bg-blue-50 dark:hover:bg-blue-900/30 text-gray-700 dark:text-gray-200 flex items-center gap-3 transition-colors font-medium">
        <i class="fas fa-plus text-blue-600 dark:text-blue-400"></i>
        <span>Add Sprite</span>
      </button>
      <button v-if="contextMenuSpriteId" @click="replaceSprite" class="w-full px-5 py-3 text-left hover:bg-purple-50 dark:hover:bg-purple-900/30 text-gray-700 dark:text-gray-200 flex items-center gap-3 transition-colors font-medium">
        <i class="fas fa-exchange-alt text-purple-600 dark:text-purple-400"></i>
        <span>Replace Sprite</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-5 py-3 text-left hover:bg-red-50 dark:hover:bg-red-900/30 text-red-600 dark:text-red-400 flex items-center gap-3 transition-colors font-medium">
        <i class="fas fa-trash"></i>
        <span>Remove Sprite</span>
      </button>
    </div>
  </Teleport>

  <div class="space-y-6 w-full max-w-full overflow-hidden">
    <div class="bg-cyan-50 dark:bg-cyan-900/20 rounded-lg p-3 border border-cyan-100 dark:border-cyan-800/50 flex items-start gap-3">
      <i class="fas fa-info-circle text-cyan-600 dark:text-cyan-400 mt-0.5 flex-shrink-0"></i>
      <p class="text-sm text-cyan-800 dark:text-cyan-200"><span class="font-semibold">Tip:</span> Right-click any sprite to open the context menu for quick actions: add, replace, or remove sprites.</p>
    </div>

    <section class="w-full bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 p-1 shadow-sm">
      <div class="flex flex-wrap items-center gap-1">
        <!-- Toggles Group -->
        <div class="flex items-center gap-1 p-1">
          <label class="flex items-center gap-2 px-3 py-2 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer transition-colors" title="Pixel Perfect">
            <input id="pixel-perfect" type="checkbox" v-model="settingsStore.pixelPerfect" class="w-4 h-4 rounded text-blue-600 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600" />
            <span class="text-sm font-medium text-gray-700 dark:text-gray-200">Pixel Perfect</span>
          </label>
          <label class="flex items-center gap-2 px-3 py-2 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer transition-colors" title="Cell Swapping">
            <input id="allow-cell-swap" type="checkbox" v-model="allowCellSwap" class="w-4 h-4 rounded text-blue-600 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600" />
            <span class="text-sm font-medium text-gray-700 dark:text-gray-200">Swap</span>
          </label>
          <label class="flex items-center gap-2 px-3 py-2 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer transition-colors" title="Compare Sprites">
            <input id="show-all-sprites" type="checkbox" v-model="showAllSprites" class="w-4 h-4 rounded text-blue-600 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600" />
            <span class="text-sm font-medium text-gray-700 dark:text-gray-200">Compare</span>
          </label>
        </div>

        <div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-1"></div>

        <!-- Spacing & Grid Group -->
        <div class="flex items-center gap-1 p-1">
          <label class="flex items-center gap-2 px-3 py-2 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer transition-colors" title="Negative Spacing">
            <input id="negative-spacing" type="checkbox" v-model="settingsStore.negativeSpacingEnabled" class="w-4 h-4 rounded text-blue-600 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600" />
            <span class="text-sm font-medium text-gray-700 dark:text-gray-200">Spacing</span>
          </label>
          <label class="flex items-center gap-2 px-3 py-2 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer transition-colors" title="Checkerboard Background">
            <input id="checkerboard" type="checkbox" v-model="settingsStore.checkerboardEnabled" class="w-4 h-4 rounded text-blue-600 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600" />
            <span class="text-sm font-medium text-gray-700 dark:text-gray-200">Grid</span>
          </label>
        </div>

        <div class="w-px h-6 bg-gray-200 dark:bg-gray-700 mx-1"></div>

        <!-- Background Color -->
        <div class="flex items-center gap-2 px-3 py-2">
          <label for="bg-color" class="text-sm font-medium text-gray-600 dark:text-gray-400">Bg:</label>
          <div class="flex items-center gap-2">
            <select
              id="bg-color"
              v-model="bgSelectValue"
              class="px-2 py-1 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 dark:text-gray-200 text-sm focus:ring-2 focus:ring-blue-500 outline-none transition-all cursor-pointer hover:border-gray-400 dark:hover:border-gray-500"
            >
              <option value="transparent">Transparent</option>
              <option value="#ffffff">White</option>
              <option value="#000000">Black</option>
              <option value="#f9fafb">Light Gray</option>
              <option value="custom">Custom</option>
            </select>
            <div v-if="bgSelectValue === 'custom'" class="relative w-6 h-6 rounded-full overflow-hidden border border-gray-300 dark:border-gray-600 shadow-sm">
              <input type="color" v-model="customColor" @input="settingsStore.setBackgroundColor(customColor)" class="absolute -top-2 -left-2 w-10 h-10 cursor-pointer p-0 border-0" />
            </div>
          </div>
        </div>

        <div class="flex-1"></div>

        <!-- Zoom Controls -->
        <div class="flex items-center bg-gray-100 dark:bg-gray-700 rounded-lg p-1 mr-1">
          <button @click="zoomOut" class="p-1.5 hover:bg-white dark:hover:bg-gray-600 text-gray-600 dark:text-gray-300 rounded-md transition-all" title="Zoom Out">
            <i class="fas fa-minus text-xs"></i>
          </button>
          <span class="px-2 text-xs font-mono text-gray-600 dark:text-gray-300 min-w-[3ch] text-center">{{ Math.round(zoom * 100) }}%</span>
          <button @click="zoomIn" class="p-1.5 hover:bg-white dark:hover:bg-gray-600 text-gray-600 dark:text-gray-300 rounded-md transition-all" title="Zoom In">
            <i class="fas fa-plus text-xs"></i>
          </button>
          <div class="w-px h-4 bg-gray-300 dark:bg-gray-600 mx-1"></div>
          <button @click="resetZoom" class="p-1.5 hover:bg-white dark:hover:bg-gray-600 text-gray-600 dark:text-gray-300 rounded-md transition-all" title="Reset Zoom">
            <i class="fas fa-expand text-xs"></i>
          </button>
        </div>

        <!-- Offset Labels Toggle -->
        <label class="flex items-center gap-2 px-3 py-2 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer transition-colors border-l border-gray-100 dark:border-gray-700" title="Show Offset Labels">
          <input id="show-offset-labels" type="checkbox" v-model="showOffsetLabels" class="w-4 h-4 rounded text-blue-600 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600" />
          <span class="text-sm font-medium text-gray-700 dark:text-gray-200">Labels</span>
        </label>
      </div>
    </section>

    <div class="relative bg-white dark:bg-gray-800 border-2 border-gray-200 dark:border-gray-700 rounded-2xl shadow-lg overflow-auto max-h-[calc(100vh-400px)] min-h-[500px] w-full">
      <div class="canvas-container touch-manipulation relative inline-block min-w-full">
        <div
          ref="gridContainerRef"
          :style="{
            transform: `scale(${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: `${getCellPosition(cellIndex - 1).x}px`,
              top: `${getCellPosition(cellIndex - 1).y}px`,
              width: `${gridMetrics.maxWidth}px`,
              height: `${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: `${getCellPosition(cellIndex - 1).x + gridMetrics.negativeSpacing + sprite.x}px`,
                      top: `${getCellPosition(cellIndex - 1).y + gridMetrics.negativeSpacing + sprite.y}px`,
                      width: `${sprite.width}px`,
                      height: `${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"
                  :style="{
                    left: `${getCellPosition(index).x + gridMetrics.negativeSpacing + sprite.x}px`,
                    top: `${getCellPosition(index).y + gridMetrics.negativeSpacing + sprite.y}px`,
                    width: `${sprite.width}px`,
                    height: `${sprite.height}px`,
                    opacity: layer.id === activeLayerId ? '1' : '0.85',
                    imageRendering: settingsStore.pixelPerfect ? 'pixelated' : 'auto',
                  }"
                  :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: `${ghostSprite.x}px`,
              top: `${ghostSprite.y}px`,
              width: `${activeSpriteSprite?.width}px`,
              height: `${activeSpriteSprite?.height}px`,
              opacity: '0.6',
              imageRendering: settingsStore.pixelPerfect ? 'pixelated' : 'auto',
            }"
            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: `${position.cellX + position.maxWidth - 2}px`,
              top: `${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 { useZoom } from '@/composables/useZoom';
  import { useDragSprite } from '@/composables/useDragSprite';
  import { useFileDrop } from '@/composables/useFileDrop';

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

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

  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: 'replaceSprite', id: string, file: File): void;
    (e: 'addSprite', file: File): void;
    (e: 'addSpriteWithResize', file: File): void;
  }>();

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

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

  const {
    zoom,
    increase: zoomIn,
    decrease: zoomOut,
    reset: resetZoom,
  } = useZoom({
    min: 0.5,
    max: 3,
    step: 0.25,
    initial: 1,
  });

  const allowCellSwap = ref(false);

  const getMousePosition = (event: MouseEvent, z?: number) => {
    if (!gridContainerRef.value) return null;
    const currentZoom = z ?? zoom.value;
    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,
    calculateMaxDimensions,
  } = useDragSprite({
    sprites: computed(() => props.layers.find(l => l.id === props.activeLayerId)?.sprites ?? []),
    layers: toRef(props, 'layers'),
    columns: toRef(props, 'columns'),
    zoom,
    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 showAllSprites = ref(false);
  const showOffsetLabels = ref(false);
  const showContextMenu = ref(false);
  const contextMenuX = ref(0);
  const contextMenuY = ref(0);
  const contextMenuSpriteId = ref<string | null>(null);
  const replacingSpriteId = ref<string | null>(null);
  const fileInput = ref<HTMLInputElement | null>(null);
  const customColor = ref('#ffffff');

  // Grid metrics
  const gridMetrics = computed(() => calculateMaxDimensions());

  const totalCells = computed(() => {
    const maxLen = Math.max(1, ...props.layers.map(l => (l.visible ? l.sprites.length : 1)));
    return Math.max(1, Math.ceil(maxLen / props.columns)) * props.columns;
  });

  const gridDimensions = computed(() => {
    const maxLen = Math.max(1, ...props.layers.map(l => (l.visible ? l.sprites.length : 1)));
    const rows = Math.max(1, Math.ceil(maxLen / props.columns));
    return {
      width: gridMetrics.value.maxWidth * props.columns,
      height: gridMetrics.value.maxHeight * rows,
    };
  });

  const getCellPosition = (index: number) => {
    const col = index % props.columns;
    const row = Math.floor(index / props.columns);
    return {
      x: col * gridMetrics.value.maxWidth,
      y: row * gridMetrics.value.maxHeight,
    };
  };

  const getCellBackground = () => {
    const bg = settingsStore.backgroundColor;
    if (bg === 'transparent') {
      return 'transparent';
    }
    return bg;
  };

  const getCellBackgroundImage = () => {
    const bg = settingsStore.backgroundColor;
    if (bg === 'transparent' && settingsStore.checkerboardEnabled) {
      // Checkerboard pattern for transparent backgrounds (dark mode friendly)
      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';
  };

  const getCellBackgroundSize = () => {
    const bg = settingsStore.backgroundColor;
    if (bg === 'transparent') {
      return '20px 20px';
    }
    return 'auto';
  };

  const getCellBackgroundPosition = () => {
    const bg = settingsStore.backgroundColor;
    if (bg === 'transparent') {
      return '0 0, 0 10px, 10px -10px, -10px 0px';
    }
    return '0 0';
  };

  // Background select handling
  const presetBgColors = ['transparent', '#ffffff', '#000000', '#f9fafb'] as const;
  const isHexColor = (val: string) => /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(val);

  if (isHexColor(settingsStore.backgroundColor) && !presetBgColors.includes(settingsStore.backgroundColor as any)) {
    customColor.value = settingsStore.backgroundColor;
  }

  const isCustomMode = ref(isHexColor(settingsStore.backgroundColor) && !presetBgColors.includes(settingsStore.backgroundColor as any));

  const bgSelectValue = computed<string>({
    get() {
      if (isCustomMode.value) {
        const val = settingsStore.backgroundColor;
        if (isHexColor(val)) {
          customColor.value = val;
        }
        return 'custom';
      }

      const val = settingsStore.backgroundColor;
      if (presetBgColors.includes(val as any)) return val;
      if (isHexColor(val)) {
        customColor.value = val;
        isCustomMode.value = true;
        return 'custom';
      }
      return 'transparent';
    },
    set(v: string) {
      if (v === 'custom') {
        isCustomMode.value = true;
        const fallback = '#ffffff';
        const current = settingsStore.backgroundColor;
        const fromStore = isHexColor(current) ? current : null;
        const fromLocal = isHexColor(customColor.value) ? customColor.value : null;
        const color = fromStore || fromLocal || fallback;
        customColor.value = color;
        settingsStore.setBackgroundColor(color);
      } else {
        isCustomMode.value = false;
        settingsStore.setBackgroundColor(v);
      }
    },
  });

  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, zoom.value);
      if (!pos) return;

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

      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;

    // 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 (contextMenuSpriteId.value) {
      emit('removeSprite', contextMenuSpriteId.value);
      showContextMenu.value = false;
      contextMenuSpriteId.value = null;
    }
  };

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

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

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

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

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

  // Watch for background color changes
  watch(
    () => settingsStore.backgroundColor,
    async () => {
      await nextTick();
    }
  );
</script>

<style scoped></style>
