<script setup lang="ts">
/**
 * CanvasView Component
 * WebGPU canvas component for rendering and interaction
 */
import { ref, onMounted, onUnmounted, watch } from 'vue';
import { Renderer } from '../core/renderer';
import { getSceneManager } from '../core/scene';
import { getInputHandler } from '../core/input';
import { getHistoryManager } from '../core/history';
import { getSceneService } from '../utils/scene-service';
import type { MediaElement, RenderElement, ImageData, GifData, VideoData, TextData } from '../types';
import { loadImageFromBase64 } from '../utils/image';
import { GifAnimator, type GifData as GifDataUtil } from '../utils/gif';
import { VideoController, createVideoFromUrl } from '../utils/video';
import { renderTextToCanvas } from '../utils/text';

// ============================================
// Props & Emits
// ============================================

const props = defineProps<{
  width?: number;
  height?: number;
}>();

const emit = defineEmits<{
  (e: 'element-selected', id: string): void;
  (e: 'element-moved', id: string, x: number, y: number): void;
  (e: 'element-resized', id: string, width: number, height: number): void;
  (e: 'context-menu', x: number, y: number): void;
  (e: 'file-drop', files: File[]): void;
  (e: 'webgpu-error', message: string): void;
  (e: 'initialized'): void;
}>();

// ============================================
// Refs
// ============================================

const canvasRef = ref<HTMLCanvasElement | null>(null);
const containerRef = ref<HTMLDivElement | null>(null);
const isInitialized = ref(false);
const webgpuError = ref<string | null>(null);

// ============================================
// Core Instances
// ============================================

let renderer: Renderer | null = null;
const sceneManager = getSceneManager();
const inputHandler = getInputHandler();
const historyManager = getHistoryManager();
const sceneService = getSceneService();

// ============================================
// Media Element Texture Management
// ============================================

// Map to track GIF animators for each GIF element
const gifAnimators: Map<string, GifAnimator> = new Map();

// Map to track video controllers for each video element
const videoControllers: Map<string, VideoController> = new Map();

// Map to track loaded textures for elements
const elementTextures: Map<string, GPUTexture> = new Map();

// Map to track video elements for texture updates
const videoElements: Map<string, HTMLVideoElement> = new Map();

// Map to track text texture scale factors
const textTextureScales: Map<string, number> = new Map();

// ============================================
// Element Resize State
// ============================================

// Edge detection threshold in pixels
const EDGE_THRESHOLD = 10;

// Resize handle types
type ResizeHandle = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'top' | 'bottom' | 'left' | 'right' | null;

// Current resize state (supports both single and multi-selection)
let resizeState: {
  isMultiSelect: boolean;
  elementId: string; // For single selection
  handle: ResizeHandle;
  startX: number;
  startY: number;
  // For single element resize
  startWidth: number;
  startHeight: number;
  startElementX: number;
  startElementY: number;
  aspectRatio: number;
  // For multi-selection resize
  startBounds: { x: number; y: number; width: number; height: number } | null;
  accumulatedDeltaX: number;
  accumulatedDeltaY: number;
} | null = null;

// Track accumulated delta for resize operations
let resizeAccumulatedX = 0;
let resizeAccumulatedY = 0;

// Store initial positions for multi-selection resize
let multiResizeStartPositions: Array<{
  id: string;
  x: number;
  y: number;
  width: number;
  height: number;
}> = [];

// ============================================
// Initialization
// ============================================

async function initWebGPU() {
  if (!canvasRef.value) return;

  try {
    // Check WebGPU support
    const supported = await Renderer.isSupported();
    if (!supported) {
      webgpuError.value = 'WebGPU is not supported. Please update your browser or graphics driver.';
      emit('webgpu-error', webgpuError.value);
      return;
    }

    // Create and initialize renderer
    renderer = new Renderer();
    renderer.setOnError((error) => {
      webgpuError.value = error.message;
      emit('webgpu-error', error.message);
    });

    await renderer.init(canvasRef.value);
    
    // Set initial size
    updateCanvasSize();
    
    // Start render loop
    renderer.startRenderLoop();
    
    // Attach input handler
    inputHandler.attach(canvasRef.value);
    setupInputHandlers();
    
    isInitialized.value = true;
    emit('initialized');
    
    console.log('CanvasView initialized');
  } catch (error) {
    const message = error instanceof Error ? error.message : 'Failed to initialize WebGPU';
    webgpuError.value = message;
    emit('webgpu-error', message);
  }
}

// ============================================
// Canvas Size Management
// ============================================

function updateCanvasSize() {
  if (!canvasRef.value || !containerRef.value || !renderer) return;

  const rect = containerRef.value.getBoundingClientRect();
  const width = Math.floor(props.width || rect.width || window.innerWidth);
  const height = Math.floor(props.height || rect.height || window.innerHeight);

  // Set CSS size to match container exactly
  canvasRef.value.style.width = `${width}px`;
  canvasRef.value.style.height = `${height}px`;

  // Set actual canvas pixel size (for WebGPU rendering)
  renderer.resize(width, height);
}

// ResizeObserver for responsive canvas
let resizeObserver: ResizeObserver | null = null;
let resizeRAF: number | null = null;

function setupResizeObserver() {
  if (!containerRef.value) return;

  // Use ResizeObserver for container size changes
  resizeObserver = new ResizeObserver(() => {
    // Use requestAnimationFrame to batch resize updates
    if (resizeRAF !== null) {
      cancelAnimationFrame(resizeRAF);
    }
    resizeRAF = requestAnimationFrame(() => {
      updateCanvasSize();
      resizeRAF = null;
    });
  });
  resizeObserver.observe(containerRef.value);

  // Also listen to window resize for immediate response
  window.addEventListener('resize', handleWindowResize);
}

function handleWindowResize() {
  // Immediate update on window resize
  updateCanvasSize();
}

// ============================================
// Input Handlers
// ============================================

function setupInputHandlers() {
  inputHandler.onAction((action, data) => {
    handleInputAction(action, data);
  });
}

function handleInputAction(action: string, data: unknown) {
  const d = data as Record<string, unknown>;

  switch (action) {
    // Click handling
    case 'click':
      handleClick(d.x as number, d.y as number, d.shift as boolean, d.ctrl as boolean);
      break;

    // Drag operations
    case 'dragStart':
      handleDragStart(d.x as number, d.y as number, d.shift as boolean);
      break;

    // Pan operations
    case 'pan':
      handlePan(d.deltaX as number, d.deltaY as number);
      break;

    // Zoom operations
    case 'zoom':
      handleZoom(d.delta as number, d.x as number, d.y as number);
      break;
    case 'zoomDrag':
      handleZoomDrag(d.deltaX as number, d.x as number, d.y as number);
      break;

    // Element operations
    case 'elementDrag':
      handleElementDrag(d.deltaX as number, d.deltaY as number);
      break;
    case 'elementDragEnd':
      handleElementDragEnd();
      break;

    // Element resize operations
    case 'elementResize':
      handleElementResize(d.deltaX as number, d.deltaY as number);
      break;
    case 'elementResizeEnd':
      handleElementResizeEnd();
      break;

    // Selection operations
    case 'selectionUpdate':
      handleSelectionUpdate(d as { startX: number; startY: number; width: number; height: number });
      break;
    case 'selectionEnd':
      handleSelectionEnd(d as { startX: number; startY: number; width: number; height: number });
      break;

    // Context menu
    case 'contextMenu':
      emit('context-menu', d.x as number, d.y as number);
      break;

    // Mouse move for cursor updates
    case 'mouseMove':
      updateResizeCursor(d.x as number, d.y as number);
      break;

    // Keyboard shortcuts
    case 'undo':
      handleUndo();
      break;
    case 'redo':
      handleRedo();
      break;
    case 'delete':
      handleDelete();
      break;
    case 'selectAll':
      sceneManager.selectAll();
      syncRendererElements();
      break;
    case 'deselect':
      sceneManager.deselectAll();
      syncRendererElements();
      break;
  }
}

// ============================================
// Click Handling
// ============================================

function handleClick(x: number, y: number, shift: boolean, _ctrl: boolean) {
  console.log('handleClick called at:', x, y);
  const element = sceneManager.getElementAtPoint(x, y);

  if (element) {
    // Record state for undo
    recordState();
    
    if (shift) {
      sceneManager.toggleSelection(element.id);
    } else {
      sceneManager.selectElement(element.id);
    }
    
    // Bring selected element to front
    sceneManager.bringToFront();
    
    emit('element-selected', element.id);
  } else {
    console.log('handleClick - no element at point, deselecting all');
    sceneManager.deselectAll();
  }

  syncRendererElements();
}

// ============================================
// Drag Handling
// ============================================

function handleDragStart(x: number, y: number, shift: boolean) {
  console.log('handleDragStart at:', x, y, 'selected elements:', sceneManager.selectedElements.value.length);
  
  const selectedElements = sceneManager.selectedElements.value;
  
  // Check for multi-selection resize first
  if (selectedElements.length > 1) {
    const bounds = sceneManager.getSelectionBounds();
    if (bounds) {
      const handle = detectResizeHandleOnBounds(x, y, bounds);
      if (handle) {
        console.log('Starting multi-select resize with handle:', handle);
        startMultiResize(bounds, handle, x, y);
        return;
      }
    }
  }
  
  // Check for single element resize
  for (const element of selectedElements) {
    const handle = detectResizeHandle(x, y, element);
    console.log('Checking resize handle for element:', element.id, 'handle:', handle);
    if (handle) {
      console.log('Starting resize with handle:', handle);
      startResize(element, handle, x, y);
      return;
    }
  }

  // Not resizing, check for element at point
  const element = sceneManager.getElementAtPoint(x, y);

  if (element) {
    // Start element dragging
    if (!element.selected) {
      if (!shift) {
        sceneManager.deselectAll();
      }
      sceneManager.selectElement(element.id, shift);
    }
    
    // Bring selected element(s) to front when starting drag
    sceneManager.bringToFront();
    
    inputHandler.startDragging();
    recordState();
  } else {
    // Start selection box
    inputHandler.startSelection(x, y);
  }

  syncRendererElements();
}

function handleElementDrag(deltaX: number, deltaY: number) {
  const scale = sceneManager.viewTransform.value.scale;
  sceneManager.moveSelected(deltaX / scale, deltaY / scale);
  syncRendererElements();
}

function handleElementDragEnd() {
  // Element positions already updated
  sceneService.markDirty();
  syncRendererElements();
}

// ============================================
// Selection Box Handling
// ============================================

function handleSelectionUpdate(rect: { startX: number; startY: number; width: number; height: number }) {
  // Convert screen coordinates to world coordinates
  const startWorld = sceneManager.screenToWorld(rect.startX, rect.startY);
  const endWorld = sceneManager.screenToWorld(rect.startX + rect.width, rect.startY + rect.height);
  
  // Calculate world rect - ensure we use the correct corner as origin
  const worldX = Math.min(startWorld.x, endWorld.x);
  const worldY = Math.min(startWorld.y, endWorld.y);
  const worldWidth = Math.abs(endWorld.x - startWorld.x);
  const worldHeight = Math.abs(endWorld.y - startWorld.y);

  // Debug logging
  console.log('Selection rect:', { worldX, worldY, worldWidth, worldHeight });
  const elements = sceneManager.getAllElements();
  elements.forEach(el => {
    console.log('Element:', el.id, { x: el.x, y: el.y, width: el.width, height: el.height });
  });

  sceneManager.selectInRect(worldX, worldY, worldWidth, worldHeight);
  syncRendererElements();
}

function handleSelectionEnd(rect: { startX: number; startY: number; width: number; height: number }) {
  handleSelectionUpdate(rect);
}

// ============================================
// Pan & Zoom Handling
// ============================================

function handlePan(deltaX: number, deltaY: number) {
  console.log('handlePan:', deltaX, deltaY);
  sceneManager.pan(deltaX, deltaY);
  syncViewTransform();
}

function handleZoom(delta: number, x: number, y: number) {
  sceneManager.zoom(delta, x, y);
  syncViewTransform();
  // Update text textures if zoom level increased significantly
  updateTextTexturesForZoom();
}

function handleZoomDrag(deltaX: number, x: number, y: number) {
  const zoomDelta = deltaX * 0.005;
  sceneManager.zoom(zoomDelta, x, y);
  syncViewTransform();
  // Update text textures if zoom level increased significantly
  updateTextTexturesForZoom();
}

// ============================================
// History Operations
// ============================================

function recordState() {
  const state = sceneManager.getState();
  historyManager.record(state.elements, state.viewTransform);
}

function handleUndo() {
  const currentState = sceneManager.getState();
  const snapshot = historyManager.createSnapshot(currentState.elements, currentState.viewTransform);
  const previousState = historyManager.undo(snapshot);

  if (previousState) {
    sceneManager.restoreState({
      elements: previousState.elements,
      viewTransform: previousState.viewTransform,
    });
    // Clear selection after undo
    sceneManager.deselectAll();
    syncRendererElements();
    syncViewTransform();
  }
}

function handleRedo() {
  const currentState = sceneManager.getState();
  const snapshot = historyManager.createSnapshot(currentState.elements, currentState.viewTransform);
  const nextState = historyManager.redo(snapshot);

  if (nextState) {
    sceneManager.restoreState({
      elements: nextState.elements,
      viewTransform: nextState.viewTransform,
    });
    // Clear selection after redo
    sceneManager.deselectAll();
    syncRendererElements();
    syncViewTransform();
  }
}

async function handleDelete() {
  if (sceneManager.getSelectedCount() > 0) {
    recordState();
    sceneManager.deleteSelected();
    sceneService.markDirty();
    await syncRendererElements();
    // Force render update
    renderer?.requestRender();
  }
}

// ============================================
// Renderer Sync
// ============================================

async function syncRendererElements() {
  if (!renderer) return;

  // Clear existing render elements
  renderer.clearElements();

  // Get all scene elements
  const elements = sceneManager.getAllElements();
  
  console.log('syncRendererElements - elements:', elements.map(el => ({ id: el.id, selected: el.selected })));
  
  // Track which element IDs are still in the scene
  const currentElementIds = new Set(elements.map(el => el.id));
  
  // Clean up textures for removed elements
  for (const elementId of elementTextures.keys()) {
    if (!currentElementIds.has(elementId)) {
      cleanupElementTexture(elementId);
    }
  }

  // Load textures for new elements and add all elements to renderer
  for (const element of elements) {
    // Load texture if not already loaded
    if (!elementTextures.has(element.id)) {
      // Don't await here to avoid blocking - texture will be loaded async
      loadElementTexture(element);
    }
    
    const renderElement = createRenderElement(element);
    if (renderElement) {
      console.log('Adding render element:', renderElement.id, 'selected:', renderElement.selected);
      renderer.addElement(renderElement);
    }
  }

  renderer.requestRender();
}

function syncViewTransform() {
  if (!renderer) return;
  renderer.setViewTransform(sceneManager.viewTransform.value);
}

/**
 * Create a render element with proper texture based on element type
 */
function createRenderElement(element: MediaElement): RenderElement | null {
  if (!renderer) return null;

  const textureManager = renderer.getTextureManager();
  if (!textureManager) return null;

  // Check if we already have a texture for this element
  let texture = elementTextures.get(element.id);
  
  // If texture exists, use it (for GIF/video, texture is updated separately)
  if (texture) {
    return {
      id: element.id,
      texture,
      x: element.x,
      y: element.y,
      width: element.width,
      height: element.height,
      rotation: element.rotation,
      zIndex: element.zIndex,
      selected: element.selected,
    };
  }

  // Use placeholder texture while actual texture is being loaded
  const placeholderTexture = textureManager.getPlaceholderTexture();
  if (!placeholderTexture) return null;

  return {
    id: element.id,
    texture: placeholderTexture,
    x: element.x,
    y: element.y,
    width: element.width,
    height: element.height,
    rotation: element.rotation,
    zIndex: element.zIndex,
    selected: element.selected,
  };
}

/**
 * Load and create texture for an image element
 */
async function loadImageTexture(element: MediaElement): Promise<void> {
  if (!renderer) return;
  
  const textureManager = renderer.getTextureManager();
  if (!textureManager) return;

  const data = element.data as ImageData;
  if (!data.src) return;

  try {
    const imageBitmap = await loadImageFromBase64(data.src);
    const texture = textureManager.createFromImage(element.id, imageBitmap);
    elementTextures.set(element.id, texture);
    imageBitmap.close();
    // Re-sync to update the render element with the loaded texture
    syncRendererElements();
  } catch (error) {
    console.error(`Failed to load image texture for element ${element.id}:`, error);
  }
}

/**
 * Load and create texture for a GIF element with animation support
 */
async function loadGifTexture(element: MediaElement): Promise<void> {
  if (!renderer) return;
  
  const textureManager = renderer.getTextureManager();
  if (!textureManager) return;

  const data = element.data as GifData;
  
  // Check if we have frames in the data
  if (data.frames && data.frames.length > 0) {
    // Create texture from first frame
    const firstFrame = data.frames[0];
    const texture = textureManager.createFromImage(element.id, firstFrame);
    elementTextures.set(element.id, texture);

    // Create GIF animator if multiple frames
    if (data.frames.length > 1) {
      const gifData: GifDataUtil = {
        frames: data.frames.map((bitmap, index) => ({
          bitmap,
          delay: data.frameDelays[index] || 100,
          index,
        })),
        totalDuration: data.frameDelays.reduce((sum, d) => sum + d, 0),
        width: data.originalWidth,
        height: data.originalHeight,
        loopCount: 0,
        originalData: data.src,
      };

      const animator = new GifAnimator(gifData);
      animator.setOnFrameChange((frame) => {
        // Update texture with new frame
        if (textureManager.hasTexture(element.id)) {
          textureManager.updateFromImage(element.id, frame.bitmap);
          renderer?.requestRender();
        }
      });
      animator.play();
      gifAnimators.set(element.id, animator);
    }

    // Re-sync to update the render element with the loaded texture
    syncRendererElements();
  } else if (data.src) {
    // Fallback: load from base64 as static image
    try {
      const imageBitmap = await loadImageFromBase64(data.src);
      const texture = textureManager.createFromImage(element.id, imageBitmap);
      elementTextures.set(element.id, texture);
      imageBitmap.close();
      // Re-sync to update the render element with the loaded texture
      syncRendererElements();
    } catch (error) {
      console.error(`Failed to load GIF texture for element ${element.id}:`, error);
    }
  }
}

/**
 * Load and create texture for a video element with frame updates
 */
async function loadVideoTexture(element: MediaElement): Promise<void> {
  if (!renderer) return;
  
  const textureManager = renderer.getTextureManager();
  if (!textureManager) return;

  const data = element.data as VideoData;
  if (!data.src) return;

  try {
    const video = await createVideoFromUrl(data.src);
    video.muted = data.muted;
    
    // Wait for video to be ready
    await new Promise<void>((resolve, reject) => {
      if (video.readyState >= 3) {
        resolve();
        return;
      }
      video.oncanplay = () => resolve();
      video.onerror = () => reject(new Error('Video load failed'));
    });

    // Create texture from video
    const texture = textureManager.createFromVideo(element.id, video);
    elementTextures.set(element.id, texture);
    videoElements.set(element.id, video);

    // Create video controller for playback and frame updates
    const controller = new VideoController(video);
    controller.setLoopPoints(data.loopStart, data.loopEnd);
    controller.setFrameUpdateCallback(() => {
      // Update texture with current video frame
      if (textureManager.hasTexture(element.id)) {
        textureManager.updateFromVideo(element.id, video);
        renderer?.requestRender();
      }
    });
    
    // Start playback and frame updates
    await controller.play();
    videoControllers.set(element.id, controller);

    // Re-sync to update the render element with the loaded texture
    syncRendererElements();
  } catch (error) {
    console.error(`Failed to load video texture for element ${element.id}:`, error);
  }
}

/**
 * Load and create texture for a text element
 */
function loadTextTexture(element: MediaElement, scale: number = 1): void {
  if (!renderer) return;
  
  const textureManager = renderer.getTextureManager();
  if (!textureManager) return;

  const data = element.data as TextData;
  if (!data.content) return;

  try {
    // Use higher resolution for better quality when zoomed in
    // Minimum scale factor of 2 for retina-like quality
    const textureScale = Math.max(2, Math.ceil(scale));
    const scaledFontSize = data.fontSize * textureScale;
    
    // Render text to canvas at higher resolution
    const canvas = renderTextToCanvas(data.content, {
      fontSize: scaledFontSize,
      color: data.color,
      backgroundColor: 'transparent',
    });

    // Create texture from canvas
    const texture = textureManager.createFromCanvas(element.id, canvas);
    elementTextures.set(element.id, texture);
    
    // Store the texture scale for reference
    textTextureScales.set(element.id, textureScale);
    
    // Re-sync to update the render element with the loaded texture
    syncRendererElements();
  } catch (error) {
    console.error(`Failed to load text texture for element ${element.id}:`, error);
  }
}

/**
 * Update text texture when zoom level changes significantly
 */
function updateTextTexturesForZoom(): void {
  const currentScale = sceneManager.viewTransform.value.scale;
  const elements = sceneManager.getAllElements();
  
  for (const element of elements) {
    if (element.type === 'text') {
      const currentTextureScale = textTextureScales.get(element.id) || 2;
      const neededScale = Math.max(2, Math.ceil(currentScale));
      
      // Only regenerate if we need higher resolution
      if (neededScale > currentTextureScale) {
        // Clean up old texture
        const textureManager = renderer?.getTextureManager();
        if (textureManager && textureManager.hasTexture(element.id)) {
          textureManager.destroy(element.id);
        }
        elementTextures.delete(element.id);
        
        // Regenerate at higher resolution
        loadTextTexture(element, currentScale);
      }
    }
  }
}

/**
 * Load texture for an element based on its type
 */
async function loadElementTexture(element: MediaElement): Promise<void> {
  // Skip if texture already exists
  if (elementTextures.has(element.id)) return;

  switch (element.type) {
    case 'image':
      await loadImageTexture(element);
      break;
    case 'gif':
      await loadGifTexture(element);
      break;
    case 'video':
      await loadVideoTexture(element);
      break;
    case 'text':
      loadTextTexture(element);
      break;
    // YouTube elements would need special handling (iframe or external API)
    default:
      break;
  }
}

/**
 * Clean up texture and associated resources for an element
 */
function cleanupElementTexture(elementId: string): void {
  // Clean up GIF animator
  const animator = gifAnimators.get(elementId);
  if (animator) {
    animator.destroy();
    gifAnimators.delete(elementId);
  }

  // Clean up video controller
  const controller = videoControllers.get(elementId);
  if (controller) {
    controller.destroy();
    videoControllers.delete(elementId);
  }

  // Clean up video element
  videoElements.delete(elementId);

  // Clean up texture
  const textureManager = renderer?.getTextureManager();
  if (textureManager && textureManager.hasTexture(elementId)) {
    textureManager.destroy(elementId);
  }
  elementTextures.delete(elementId);
  
  // Clean up text texture scale
  textTextureScales.delete(elementId);
}

/**
 * Clean up all element textures and resources
 */
function cleanupAllTextures(): void {
  // Clean up all GIF animators
  for (const animator of gifAnimators.values()) {
    animator.destroy();
  }
  gifAnimators.clear();

  // Clean up all video controllers
  for (const controller of videoControllers.values()) {
    controller.destroy();
  }
  videoControllers.clear();

  // Clear video elements
  videoElements.clear();

  // Clear text texture scales
  textTextureScales.clear();

  // Clear texture references
  elementTextures.clear();
}

// ============================================
// Element Resize Detection and Handling
// ============================================

/**
 * Detect resize handle on a bounding box
 */
function detectResizeHandleOnBounds(
  screenX: number,
  screenY: number,
  bounds: { x: number; y: number; width: number; height: number }
): ResizeHandle {
  const scale = sceneManager.viewTransform.value.scale;
  const threshold = EDGE_THRESHOLD / scale;
  
  const worldPos = sceneManager.screenToWorld(screenX, screenY);
  const wx = worldPos.x;
  const wy = worldPos.y;
  
  const left = bounds.x;
  const right = bounds.x + bounds.width;
  const top = bounds.y;
  const bottom = bounds.y + bounds.height;
  
  const nearLeft = Math.abs(wx - left) <= threshold;
  const nearRight = Math.abs(wx - right) <= threshold;
  const nearTop = Math.abs(wy - top) <= threshold;
  const nearBottom = Math.abs(wy - bottom) <= threshold;
  
  const withinX = wx >= left - threshold && wx <= right + threshold;
  const withinY = wy >= top - threshold && wy <= bottom + threshold;
  
  if (!withinX || !withinY) return null;
  
  if (nearTop && nearLeft) return 'top-left';
  if (nearTop && nearRight) return 'top-right';
  if (nearBottom && nearLeft) return 'bottom-left';
  if (nearBottom && nearRight) return 'bottom-right';
  if (nearTop) return 'top';
  if (nearBottom) return 'bottom';
  if (nearLeft) return 'left';
  if (nearRight) return 'right';
  
  return null;
}

/**
 * Detect which resize handle (if any) is at the given screen position
 */
function detectResizeHandle(screenX: number, screenY: number, element: MediaElement): ResizeHandle {
  const scale = sceneManager.viewTransform.value.scale;
  const threshold = EDGE_THRESHOLD / scale;
  
  // Convert screen coordinates to world coordinates
  const worldPos = sceneManager.screenToWorld(screenX, screenY);
  const wx = worldPos.x;
  const wy = worldPos.y;
  
  // Element bounds in world coordinates
  const left = element.x;
  const right = element.x + element.width;
  const top = element.y;
  const bottom = element.y + element.height;
  
  // Check if point is near element edges
  const nearLeft = Math.abs(wx - left) <= threshold;
  const nearRight = Math.abs(wx - right) <= threshold;
  const nearTop = Math.abs(wy - top) <= threshold;
  const nearBottom = Math.abs(wy - bottom) <= threshold;
  
  // Check if point is within element bounds (with threshold)
  const withinX = wx >= left - threshold && wx <= right + threshold;
  const withinY = wy >= top - threshold && wy <= bottom + threshold;
  
  if (!withinX || !withinY) return null;
  
  // Corner handles (prioritize corners)
  if (nearTop && nearLeft) return 'top-left';
  if (nearTop && nearRight) return 'top-right';
  if (nearBottom && nearLeft) return 'bottom-left';
  if (nearBottom && nearRight) return 'bottom-right';
  
  // Edge handles
  if (nearTop) return 'top';
  if (nearBottom) return 'bottom';
  if (nearLeft) return 'left';
  if (nearRight) return 'right';
  
  return null;
}

/**
 * Get cursor style for a resize handle
 */
function getCursorForHandle(handle: ResizeHandle): string {
  switch (handle) {
    case 'top-left':
    case 'bottom-right':
      return 'nwse-resize';
    case 'top-right':
    case 'bottom-left':
      return 'nesw-resize';
    case 'top':
    case 'bottom':
      return 'ns-resize';
    case 'left':
    case 'right':
      return 'ew-resize';
    default:
      return 'default';
  }
}

/**
 * Start resizing an element
 */
function startResize(element: MediaElement, handle: ResizeHandle, screenX: number, screenY: number): void {
  if (!handle) return;
  
  // Reset accumulated deltas
  resizeAccumulatedX = 0;
  resizeAccumulatedY = 0;
  
  resizeState = {
    isMultiSelect: false,
    elementId: element.id,
    handle,
    startX: screenX,
    startY: screenY,
    startWidth: element.width,
    startHeight: element.height,
    startElementX: element.x,
    startElementY: element.y,
    aspectRatio: element.width / element.height,
    startBounds: null,
    accumulatedDeltaX: 0,
    accumulatedDeltaY: 0,
  };
  
  inputHandler.startResizing();
  recordState();
}

/**
 * Start resizing multiple selected elements as a group
 */
function startMultiResize(
  bounds: { x: number; y: number; width: number; height: number },
  handle: ResizeHandle,
  screenX: number,
  screenY: number
): void {
  if (!handle) return;
  
  // Reset accumulated deltas
  resizeAccumulatedX = 0;
  resizeAccumulatedY = 0;
  
  resizeState = {
    isMultiSelect: true,
    elementId: '', // Not used for multi-select
    handle,
    startX: screenX,
    startY: screenY,
    startWidth: bounds.width,
    startHeight: bounds.height,
    startElementX: bounds.x,
    startElementY: bounds.y,
    aspectRatio: bounds.width / bounds.height,
    startBounds: { ...bounds },
    accumulatedDeltaX: 0,
    accumulatedDeltaY: 0,
  };
  
  // Store initial positions of all selected elements for proportional scaling
  const selectedElements = sceneManager.selectedElements.value;
  multiResizeStartPositions = selectedElements.map(el => ({
    id: el.id,
    x: el.x,
    y: el.y,
    width: el.width,
    height: el.height,
  }));
  
  inputHandler.startResizing();
  recordState();
}

/**
 * Handle element resize during drag
 * Maintains aspect ratio for proportional resizing
 */
function handleElementResize(deltaX: number, deltaY: number): void {
  if (!resizeState) return;
  
  const scale = sceneManager.viewTransform.value.scale;
  
  // Accumulate deltas (convert screen delta to world delta)
  resizeAccumulatedX += deltaX / scale;
  resizeAccumulatedY += deltaY / scale;
  
  // Use accumulated deltas for calculation
  const totalDeltaX = resizeAccumulatedX;
  const totalDeltaY = resizeAccumulatedY;
  
  // Handle multi-selection resize
  if (resizeState.isMultiSelect && resizeState.startBounds) {
    handleMultiElementResize(totalDeltaX, totalDeltaY);
    return;
  }
  
  const element = sceneManager.getElement(resizeState.elementId);
  if (!element) return;
  
  let newWidth = resizeState.startWidth;
  let newHeight = resizeState.startHeight;
  let newX = resizeState.startElementX;
  let newY = resizeState.startElementY;
  
  const handle = resizeState.handle;
  const aspectRatio = resizeState.aspectRatio;
  
  // Calculate new dimensions based on handle using accumulated total delta
  switch (handle) {
    case 'bottom-right':
      // Resize from bottom-right corner (most common)
      newWidth = Math.max(20, resizeState.startWidth + totalDeltaX);
      newHeight = newWidth / aspectRatio;
      break;
      
    case 'bottom-left':
      // Resize from bottom-left corner
      newWidth = Math.max(20, resizeState.startWidth - totalDeltaX);
      newHeight = newWidth / aspectRatio;
      newX = resizeState.startElementX + (resizeState.startWidth - newWidth);
      break;
      
    case 'top-right':
      // Resize from top-right corner
      newWidth = Math.max(20, resizeState.startWidth + totalDeltaX);
      newHeight = newWidth / aspectRatio;
      newY = resizeState.startElementY + (resizeState.startHeight - newHeight);
      break;
      
    case 'top-left':
      // Resize from top-left corner
      newWidth = Math.max(20, resizeState.startWidth - totalDeltaX);
      newHeight = newWidth / aspectRatio;
      newX = resizeState.startElementX + (resizeState.startWidth - newWidth);
      newY = resizeState.startElementY + (resizeState.startHeight - newHeight);
      break;
      
    case 'right':
      // Resize from right edge (proportional)
      newWidth = Math.max(20, resizeState.startWidth + totalDeltaX);
      newHeight = newWidth / aspectRatio;
      // Center vertically
      newY = resizeState.startElementY + (resizeState.startHeight - newHeight) / 2;
      break;
      
    case 'left':
      // Resize from left edge (proportional)
      newWidth = Math.max(20, resizeState.startWidth - totalDeltaX);
      newHeight = newWidth / aspectRatio;
      newX = resizeState.startElementX + (resizeState.startWidth - newWidth);
      // Center vertically
      newY = resizeState.startElementY + (resizeState.startHeight - newHeight) / 2;
      break;
      
    case 'bottom':
      // Resize from bottom edge (proportional)
      newHeight = Math.max(20, resizeState.startHeight + totalDeltaY);
      newWidth = newHeight * aspectRatio;
      // Center horizontally
      newX = resizeState.startElementX + (resizeState.startWidth - newWidth) / 2;
      break;
      
    case 'top':
      // Resize from top edge (proportional)
      newHeight = Math.max(20, resizeState.startHeight - totalDeltaY);
      newWidth = newHeight * aspectRatio;
      newY = resizeState.startElementY + (resizeState.startHeight - newHeight);
      // Center horizontally
      newX = resizeState.startElementX + (resizeState.startWidth - newWidth) / 2;
      break;
  }
  
  // Update element
  sceneManager.updateElement(resizeState.elementId, {
    x: newX,
    y: newY,
    width: newWidth,
    height: newHeight,
  });
  
  // Emit resize event
  emit('element-resized', resizeState.elementId, newWidth, newHeight);
  
  syncRendererElements();
}

/**
 * Handle multi-element resize (group resize)
 */
function handleMultiElementResize(totalDeltaX: number, totalDeltaY: number): void {
  if (!resizeState || !resizeState.startBounds) return;
  
  const startBounds = resizeState.startBounds;
  const handle = resizeState.handle;
  const aspectRatio = resizeState.aspectRatio;
  
  // Calculate new bounds based on handle
  let newWidth = startBounds.width;
  let newHeight = startBounds.height;
  let newX = startBounds.x;
  let newY = startBounds.y;
  
  switch (handle) {
    case 'bottom-right':
      newWidth = Math.max(20, startBounds.width + totalDeltaX);
      newHeight = newWidth / aspectRatio;
      break;
      
    case 'bottom-left':
      newWidth = Math.max(20, startBounds.width - totalDeltaX);
      newHeight = newWidth / aspectRatio;
      newX = startBounds.x + (startBounds.width - newWidth);
      break;
      
    case 'top-right':
      newWidth = Math.max(20, startBounds.width + totalDeltaX);
      newHeight = newWidth / aspectRatio;
      newY = startBounds.y + (startBounds.height - newHeight);
      break;
      
    case 'top-left':
      newWidth = Math.max(20, startBounds.width - totalDeltaX);
      newHeight = newWidth / aspectRatio;
      newX = startBounds.x + (startBounds.width - newWidth);
      newY = startBounds.y + (startBounds.height - newHeight);
      break;
      
    case 'right':
      newWidth = Math.max(20, startBounds.width + totalDeltaX);
      newHeight = newWidth / aspectRatio;
      newY = startBounds.y + (startBounds.height - newHeight) / 2;
      break;
      
    case 'left':
      newWidth = Math.max(20, startBounds.width - totalDeltaX);
      newHeight = newWidth / aspectRatio;
      newX = startBounds.x + (startBounds.width - newWidth);
      newY = startBounds.y + (startBounds.height - newHeight) / 2;
      break;
      
    case 'bottom':
      newHeight = Math.max(20, startBounds.height + totalDeltaY);
      newWidth = newHeight * aspectRatio;
      newX = startBounds.x + (startBounds.width - newWidth) / 2;
      break;
      
    case 'top':
      newHeight = Math.max(20, startBounds.height - totalDeltaY);
      newWidth = newHeight * aspectRatio;
      newY = startBounds.y + (startBounds.height - newHeight);
      newX = startBounds.x + (startBounds.width - newWidth) / 2;
      break;
  }
  
  // Apply proportional scaling to all selected elements
  for (const startPos of multiResizeStartPositions) {
    // Calculate relative position within the original bounds
    const relX = (startPos.x - startBounds.x) / startBounds.width;
    const relY = (startPos.y - startBounds.y) / startBounds.height;
    const relWidth = startPos.width / startBounds.width;
    const relHeight = startPos.height / startBounds.height;
    
    // Apply scaling
    const elementNewX = newX + relX * newWidth;
    const elementNewY = newY + relY * newHeight;
    const elementNewWidth = relWidth * newWidth;
    const elementNewHeight = relHeight * newHeight;
    
    sceneManager.updateElement(startPos.id, {
      x: elementNewX,
      y: elementNewY,
      width: elementNewWidth,
      height: elementNewHeight,
    });
  }
  
  syncRendererElements();
}

/**
 * End element resize operation
 */
function handleElementResizeEnd(): void {
  resizeState = null;
  resizeAccumulatedX = 0;
  resizeAccumulatedY = 0;
  multiResizeStartPositions = [];
  sceneService.markDirty();
  syncRendererElements();
}

/**
 * Update cursor based on mouse position over selected elements
 */
function updateResizeCursor(screenX: number, screenY: number): void {
  if (!canvasRef.value) return;
  
  const selectedElements = sceneManager.selectedElements.value;
  
  // Check for multi-selection bounds first
  if (selectedElements.length > 1) {
    const bounds = sceneManager.getSelectionBounds();
    if (bounds) {
      const handle = detectResizeHandleOnBounds(screenX, screenY, bounds);
      if (handle) {
        canvasRef.value.style.cursor = getCursorForHandle(handle);
        return;
      }
    }
  }
  
  // Check single element resize handles
  for (const element of selectedElements) {
    const handle = detectResizeHandle(screenX, screenY, element);
    if (handle) {
      canvasRef.value.style.cursor = getCursorForHandle(handle);
      return;
    }
  }
  
  // Reset cursor if not over any resize handle
  canvasRef.value.style.cursor = 'default';
}

// ============================================
// File Drop Handling
// ============================================

function handleDragOver(event: DragEvent) {
  event.preventDefault();
  event.dataTransfer!.dropEffect = 'copy';
}

function handleDrop(event: DragEvent) {
  event.preventDefault();

  const files = event.dataTransfer?.files;
  if (files && files.length > 0) {
    const fileArray = Array.from(files);
    emit('file-drop', fileArray);
  }
}

// ============================================
// Lifecycle
// ============================================

onMounted(async () => {
  setupResizeObserver();
  await initWebGPU();
});

onUnmounted(() => {
  // Clean up resize handling
  if (resizeObserver) {
    resizeObserver.disconnect();
    resizeObserver = null;
  }
  if (resizeRAF !== null) {
    cancelAnimationFrame(resizeRAF);
    resizeRAF = null;
  }
  window.removeEventListener('resize', handleWindowResize);

  inputHandler.detach();

  // Clean up all element textures and resources
  cleanupAllTextures();

  if (renderer) {
    renderer.destroy();
    renderer = null;
  }
});

// Watch for prop changes
watch(() => [props.width, props.height], () => {
  updateCanvasSize();
});

// ============================================
// Exposed Methods
// ============================================

defineExpose({
  getRenderer: () => renderer,
  getSceneManager: () => sceneManager,
  syncRendererElements,
  syncViewTransform,
  isInitialized: () => isInitialized.value,
});
</script>

<template>
  <div
    ref="containerRef"
    class="canvas-container"
    @dragover="handleDragOver"
    @drop="handleDrop"
  >
    <canvas
      ref="canvasRef"
      class="canvas"
      :class="{ 'canvas--error': webgpuError }"
    />
    
    <!-- WebGPU Error Overlay -->
    <div v-if="webgpuError" class="canvas-error">
      <div class="canvas-error__icon">⚠️</div>
      <div class="canvas-error__title">WebGPU 不可用</div>
      <div class="canvas-error__message">{{ webgpuError }}</div>
      <div class="canvas-error__hint">
        请更新您的浏览器或显卡驱动程序
      </div>
    </div>
    
    <!-- Loading Overlay -->
    <div v-if="!isInitialized && !webgpuError" class="canvas-loading">
      <div class="canvas-loading__spinner"></div>
      <div class="canvas-loading__text">初始化中...</div>
    </div>
  </div>
</template>

<style scoped>
.canvas-container {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background-color: #191919;
}

.canvas {
  display: block;
  cursor: default;
  /* Size is set via JavaScript to match container exactly */
}

.canvas--error {
  opacity: 0.3;
}

.canvas-error {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
  color: #fff;
  padding: 2rem;
  background: rgba(0, 0, 0, 0.8);
  border-radius: 8px;
  max-width: 400px;
}

.canvas-error__icon {
  font-size: 3rem;
  margin-bottom: 1rem;
}

.canvas-error__title {
  font-size: 1.5rem;
  font-weight: bold;
  margin-bottom: 0.5rem;
}

.canvas-error__message {
  color: #ff6b6b;
  margin-bottom: 1rem;
}

.canvas-error__hint {
  color: #888;
  font-size: 0.9rem;
}

.canvas-loading {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  text-align: center;
  color: #fff;
}

.canvas-loading__spinner {
  width: 40px;
  height: 40px;
  border: 3px solid #333;
  border-top-color: #00c8ff;
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin: 0 auto 1rem;
}

.canvas-loading__text {
  color: #888;
}

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}
</style>
