import { createModuleLogger } from "./utils/LoggerUtils.js";
import { snapToGrid, getSnapAdjustment } from "./utils/CommonUtils.js";
import type { Canvas } from './Canvas';
import type { Layer, Point } from './types';

const log = createModuleLogger('CanvasInteractions');

interface MouseCoordinates {
    world: Point;
    view: Point;
}

interface ModifierState {
    ctrl: boolean;
    shift: boolean;
    alt: boolean;
    meta: boolean;
}

interface TransformOrigin {
    x: number;
    y: number;
    width: number;
    height: number;
    rotation: number;
    centerX: number;
    centerY: number;
    originalWidth?: number;
    originalHeight?: number;
    cropBounds?: { x: number; y: number; width: number; height: number };
}

interface InteractionState {
    mode: 'none' | 'panning' | 'dragging' | 'resizing' | 'rotating' | 'drawingMask' | 'resizingCanvas' | 'movingCanvas' | 'potential-drag' | 'drawingShape';
    panStart: Point;
    dragStart: Point;
    transformOrigin: TransformOrigin | null;
    resizeHandle: string | null;
    resizeAnchor: Point;
    canvasResizeStart: Point;
    isCtrlPressed: boolean;
    isMetaPressed: boolean;
    isAltPressed: boolean;
    isShiftPressed: boolean;
    isSPressed: boolean;
    hasClonedInDrag: boolean;
    lastClickTime: number;
    transformingLayer: Layer | null;
    keyMovementInProgress: boolean;
    canvasResizeRect: { x: number, y: number, width: number, height: number } | null;
    canvasMoveRect: { x: number, y: number, width: number, height: number } | null;
}

export class CanvasInteractions {
    private canvas: Canvas;
    public interaction: InteractionState;
    private originalLayerPositions: Map<Layer, Point>;

    // Bound event handlers to enable proper removeEventListener and avoid leaks
    private onMouseDown = (e: MouseEvent) => this.handleMouseDown(e);
    private onMouseMove = (e: MouseEvent) => this.handleMouseMove(e);
    private onMouseUp = (e: MouseEvent) => this.handleMouseUp(e);
    private onMouseEnter = (e: MouseEvent) => { this.canvas.isMouseOver = true; this.handleMouseEnter(e); };
    private onMouseLeave = (e: MouseEvent) => { this.canvas.isMouseOver = false; this.handleMouseLeave(e); };
    private onWheel = (e: WheelEvent) => this.handleWheel(e);
    private onKeyDown = (e: KeyboardEvent) => this.handleKeyDown(e);
    private onKeyUp = (e: KeyboardEvent) => this.handleKeyUp(e);
    private onDragOver = (e: DragEvent) => this.handleDragOver(e);
    private onDragEnter = (e: DragEvent) => this.handleDragEnter(e);
    private onDragLeave = (e: DragEvent) => this.handleDragLeave(e);
    private onDrop = (e: DragEvent) => { this.handleDrop(e); };
    private onContextMenu = (e: MouseEvent) => this.handleContextMenu(e);
    private onBlur = () => this.handleBlur();
    private onPaste = (e: ClipboardEvent) => this.handlePasteEvent(e);

    constructor(canvas: Canvas) {
        this.canvas = canvas;
        this.interaction = {
            mode: 'none',
            panStart: { x: 0, y: 0 },
            dragStart: { x: 0, y: 0 },
            transformOrigin: null,
            resizeHandle: null,
            resizeAnchor: { x: 0, y: 0 },
            canvasResizeStart: { x: 0, y: 0 },
            isCtrlPressed: false,
            isMetaPressed: false,
            isAltPressed: false,
            isShiftPressed: false,
            isSPressed: false,
            hasClonedInDrag: false,
            lastClickTime: 0,
            transformingLayer: null,
            keyMovementInProgress: false,
            canvasResizeRect: null,
            canvasMoveRect: null,
        };
        this.originalLayerPositions = new Map();
    }

    // Helper functions to eliminate code duplication

    private getMouseCoordinates(e: MouseEvent | WheelEvent): MouseCoordinates {
        return {
            world: this.canvas.getMouseWorldCoordinates(e),
            view: this.canvas.getMouseViewCoordinates(e)
        };
    }

    private getModifierState(e?: MouseEvent | WheelEvent | KeyboardEvent): ModifierState {
        return {
            ctrl: this.interaction.isCtrlPressed || (e as any)?.ctrlKey || false,
            shift: this.interaction.isShiftPressed || (e as any)?.shiftKey || false,
            alt: this.interaction.isAltPressed || (e as any)?.altKey || false,
            meta: this.interaction.isMetaPressed || (e as any)?.metaKey || false,
        };
    }

    private preventEventDefaults(e: Event): void {
        e.preventDefault();
        e.stopPropagation();
    }

    private performZoomOperation(worldCoords: Point, zoomFactor: number): void {
        const mouseBufferX = (worldCoords.x - this.canvas.viewport.x) * this.canvas.viewport.zoom;
        const mouseBufferY = (worldCoords.y - this.canvas.viewport.y) * this.canvas.viewport.zoom;

        const newZoom = Math.max(0.1, Math.min(10, this.canvas.viewport.zoom * zoomFactor));
        
        this.canvas.viewport.zoom = newZoom;
        this.canvas.viewport.x = worldCoords.x - (mouseBufferX / this.canvas.viewport.zoom);
        this.canvas.viewport.y = worldCoords.y - (mouseBufferY / this.canvas.viewport.zoom);
        
        // Update stroke overlay if mask tool is drawing during zoom
        if (this.canvas.maskTool.isDrawing) {
            this.canvas.maskTool.handleViewportChange();
        }
        
        this.canvas.onViewportChange?.();
    }

    private renderAndSave(shouldSave: boolean = false): void {
        this.canvas.render();
        if (shouldSave) {
            this.canvas.saveState();
            this.canvas.canvasState.saveStateToDB();
        }
    }

    private setDragDropStyling(active: boolean): void {
        if (active) {
            this.canvas.canvas.style.backgroundColor = 'rgba(45, 90, 160, 0.1)';
            this.canvas.canvas.style.border = '2px dashed #2d5aa0';
        } else {
            this.canvas.canvas.style.backgroundColor = '';
            this.canvas.canvas.style.border = '';
        }
    }

    setupEventListeners(): void {
        this.canvas.canvas.addEventListener('mousedown', this.onMouseDown as EventListener);
        this.canvas.canvas.addEventListener('mousemove', this.onMouseMove as EventListener);
        this.canvas.canvas.addEventListener('mouseup', this.onMouseUp as EventListener);
        this.canvas.canvas.addEventListener('wheel', this.onWheel as EventListener, { passive: false });
        this.canvas.canvas.addEventListener('keydown', this.onKeyDown as EventListener);
        this.canvas.canvas.addEventListener('keyup', this.onKeyUp as EventListener);

        // Add a blur event listener to the window to reset key states
        window.addEventListener('blur', this.onBlur);

        document.addEventListener('paste', this.onPaste as unknown as EventListener);

        this.canvas.canvas.addEventListener('mouseenter', this.onMouseEnter as EventListener);
        this.canvas.canvas.addEventListener('mouseleave', this.onMouseLeave as EventListener);

        this.canvas.canvas.addEventListener('dragover', this.onDragOver as EventListener);
        this.canvas.canvas.addEventListener('dragenter', this.onDragEnter as EventListener);
        this.canvas.canvas.addEventListener('dragleave', this.onDragLeave as EventListener);
        this.canvas.canvas.addEventListener('drop', this.onDrop as unknown as EventListener);

        this.canvas.canvas.addEventListener('contextmenu', this.onContextMenu as EventListener);
    }

    teardownEventListeners(): void {
        this.canvas.canvas.removeEventListener('mousedown', this.onMouseDown as EventListener);
        this.canvas.canvas.removeEventListener('mousemove', this.onMouseMove as EventListener);
        this.canvas.canvas.removeEventListener('mouseup', this.onMouseUp as EventListener);
        this.canvas.canvas.removeEventListener('wheel', this.onWheel as EventListener);
        this.canvas.canvas.removeEventListener('keydown', this.onKeyDown as EventListener);
        this.canvas.canvas.removeEventListener('keyup', this.onKeyUp as EventListener);

        window.removeEventListener('blur', this.onBlur);
        document.removeEventListener('paste', this.onPaste as unknown as EventListener);

        this.canvas.canvas.removeEventListener('mouseenter', this.onMouseEnter as EventListener);
        this.canvas.canvas.removeEventListener('mouseleave', this.onMouseLeave as EventListener);

        this.canvas.canvas.removeEventListener('dragover', this.onDragOver as EventListener);
        this.canvas.canvas.removeEventListener('dragenter', this.onDragEnter as EventListener);
        this.canvas.canvas.removeEventListener('dragleave', this.onDragLeave as EventListener);
        this.canvas.canvas.removeEventListener('drop', this.onDrop as unknown as EventListener);

        this.canvas.canvas.removeEventListener('contextmenu', this.onContextMenu as EventListener);
    }

    /**
     * Sprawdza czy punkt znajduje się w obszarze któregokolwiek z zaznaczonych layerów
     */
    isPointInSelectedLayers(worldX: number, worldY: number): boolean {
        for (const layer of this.canvas.canvasSelection.selectedLayers) {
            if (!layer.visible) continue;

            const centerX = layer.x + layer.width / 2;
            const centerY = layer.y + layer.height / 2;

            // Przekształć punkt do lokalnego układu współrzędnych layera
            const dx = worldX - centerX;
            const dy = worldY - centerY;

            const rad = -layer.rotation * Math.PI / 180;
            const rotatedX = dx * Math.cos(rad) - dy * Math.sin(rad);
            const rotatedY = dx * Math.sin(rad) + dy * Math.cos(rad);

            // Sprawdź czy punkt jest wewnątrz prostokąta layera
            if (Math.abs(rotatedX) <= layer.width / 2 && 
                Math.abs(rotatedY) <= layer.height / 2) {
                return true;
            }
        }
        return false;
    }

    resetInteractionState(): void {
        this.interaction.mode = 'none';
        this.interaction.resizeHandle = null;
        this.originalLayerPositions.clear();
        this.interaction.canvasResizeRect = null;
        this.interaction.canvasMoveRect = null;
        this.interaction.hasClonedInDrag = false;
        this.interaction.transformingLayer = null;
        this.canvas.canvas.style.cursor = 'default';
    }

    handleMouseDown(e: MouseEvent): void {
        this.canvas.canvas.focus();
        const coords = this.getMouseCoordinates(e);
        const mods = this.getModifierState(e);

        if (this.interaction.mode === 'drawingMask') {
            this.canvas.maskTool.handleMouseDown(coords.world, coords.view);
            // Don't render here - mask tool will handle its own drawing
            return;
        }

        if (this.canvas.shapeTool.isActive) {
            this.canvas.shapeTool.addPoint(coords.world);
            return;
        }

        // --- Ostateczna, poprawna kolejność sprawdzania ---

        // 1. Akcje globalne z modyfikatorami (mają najwyższy priorytet)
        if (mods.shift && mods.ctrl) {
            this.startCanvasMove(coords.world);
            return;
        }
        if (mods.shift) {
            // Clear custom shape when starting canvas resize
            if (this.canvas.outputAreaShape) {
                // If auto-apply shape mask is enabled, remove the mask before clearing the shape
                if (this.canvas.autoApplyShapeMask) {
                    log.info("Removing shape mask before clearing custom shape for canvas resize");
                    this.canvas.maskTool.removeShapeMask();
                }
                this.canvas.outputAreaShape = null;
                this.canvas.render();
            }
            this.startCanvasResize(coords.world);
            return;
        }
        
        // 2. Inne przyciski myszy
        if (e.button === 2) { // Prawy przycisk myszy
            this.preventEventDefaults(e);
            
            // Sprawdź czy kliknięto w obszarze któregokolwiek z zaznaczonych layerów (niezależnie od przykrycia)
            if (this.isPointInSelectedLayers(coords.world.x, coords.world.y)) {
                // Nowa logika przekazuje tylko współrzędne świata, menu pozycjonuje się samo
                this.canvas.canvasLayers.showBlendModeMenu(coords.world.x, coords.world.y);
            }
            return;
        }
        if (e.button === 1) { // Środkowy przycisk
            this.startPanning(e);
            return;
        }

        // 3. Interakcje z elementami na płótnie (lewy przycisk)
        const transformTarget = this.canvas.canvasLayers.getHandleAtPosition(coords.world.x, coords.world.y);
        if (transformTarget) {
            this.startLayerTransform(transformTarget.layer, transformTarget.handle, coords.world);
            return;
        }

        const clickedLayerResult = this.canvas.canvasLayers.getLayerAtPosition(coords.world.x, coords.world.y);
        if (clickedLayerResult) {
            this.prepareForDrag(clickedLayerResult.layer, coords.world);
            return;
        }
        
        // 4. Domyślna akcja na tle (lewy przycisk bez modyfikatorów)
        this.startPanning(e, true); // clearSelection = true
    }

    handleMouseMove(e: MouseEvent): void {
        const coords = this.getMouseCoordinates(e);
        this.canvas.lastMousePosition = coords.world; // Zawsze aktualizuj ostatnią pozycję myszy
        
        // Sprawdź, czy rozpocząć przeciąganie
        if (this.interaction.mode === 'potential-drag') {
            const dx = coords.world.x - this.interaction.dragStart.x;
            const dy = coords.world.y - this.interaction.dragStart.y;
            if (Math.sqrt(dx * dx + dy * dy) > 3) { // Próg 3 pikseli
                this.interaction.mode = 'dragging';
                this.originalLayerPositions.clear();
                this.canvas.canvasSelection.selectedLayers.forEach((l: Layer) => {
                    this.originalLayerPositions.set(l, { x: l.x, y: l.y });
                });
            }
        }
        
        switch (this.interaction.mode) {
            case 'drawingMask':
                this.canvas.maskTool.handleMouseMove(coords.world, coords.view);
                // Don't render during mask drawing - it's handled by mask tool internally
                break;
            case 'panning':
                this.panViewport(e);
                break;
            case 'dragging':
                this.dragLayers(coords.world);
                break;
            case 'resizing':
                this.resizeLayerFromHandle(coords.world, e.shiftKey);
                break;
            case 'rotating':
                this.rotateLayerFromHandle(coords.world, e.shiftKey);
                break;
            case 'resizingCanvas':
                this.updateCanvasResize(coords.world);
                break;
            case 'movingCanvas':
                this.updateCanvasMove(coords.world);
                break;
            default:
                this.updateCursor(coords.world);
                // Update brush cursor on overlay if mask tool is active
                if (this.canvas.maskTool.isActive) {
                    this.canvas.canvasRenderer.drawMaskBrushCursor(coords.world);
                }
                break;
        }

        // --- DYNAMICZNY PODGLĄD LINII CUSTOM SHAPE ---
        if (this.canvas.shapeTool.isActive && !this.canvas.shapeTool.shape.isClosed) {
            this.canvas.render();
        }
    }

    handleMouseUp(e: MouseEvent): void {
        const coords = this.getMouseCoordinates(e);
        
        if (this.interaction.mode === 'drawingMask') {
            this.canvas.maskTool.handleMouseUp(coords.view);
            // Render only once after drawing is complete
            this.canvas.render();
            return;
        }

        if (this.interaction.mode === 'resizingCanvas') {
            this.finalizeCanvasResize();
        }
        if (this.interaction.mode === 'movingCanvas') {
            this.finalizeCanvasMove();
        }

        // Log layer positions when dragging ends
        if (this.interaction.mode === 'dragging' && this.canvas.canvasSelection.selectedLayers.length > 0) {
            this.logDragCompletion(coords);
        }

        // Handle end of crop bounds transformation before resetting interaction state
        if (this.interaction.mode === 'resizing' && this.interaction.transformingLayer?.cropMode) {
            this.canvas.canvasLayers.handleCropBoundsTransformEnd(this.interaction.transformingLayer);
        }
        
        // Handle end of scale transformation (normal transform mode) before resetting interaction state
        if (this.interaction.mode === 'resizing' && this.interaction.transformingLayer && !this.interaction.transformingLayer.cropMode) {
            this.canvas.canvasLayers.handleScaleTransformEnd(this.interaction.transformingLayer);
        }

        // Zapisz stan tylko, jeśli faktycznie doszło do zmiany (przeciąganie, transformacja, duplikacja)
        const stateChangingInteraction = ['dragging', 'resizing', 'rotating'].includes(this.interaction.mode);
        const duplicatedInDrag = this.interaction.hasClonedInDrag;

        if (stateChangingInteraction || duplicatedInDrag) {
            this.renderAndSave(true);
        }

        this.resetInteractionState();
        this.canvas.render();
    }

    private logDragCompletion(coords: MouseCoordinates): void {
        const bounds = this.canvas.outputAreaBounds;
        log.info("=== LAYER DRAG COMPLETED ===");
        log.info(`Mouse position: world(${coords.world.x.toFixed(1)}, ${coords.world.y.toFixed(1)}) view(${coords.view.x.toFixed(1)}, ${coords.view.y.toFixed(1)})`);
        log.info(`Output Area Bounds: x=${bounds.x}, y=${bounds.y}, w=${bounds.width}, h=${bounds.height}`);
        log.info(`Viewport: x=${this.canvas.viewport.x.toFixed(1)}, y=${this.canvas.viewport.y.toFixed(1)}, zoom=${this.canvas.viewport.zoom.toFixed(2)}`);
        
        this.canvas.canvasSelection.selectedLayers.forEach((layer: Layer, index: number) => {
            const relativeToOutput = {
                x: layer.x - bounds.x,
                y: layer.y - bounds.y
            };
            log.info(`Layer ${index + 1} "${layer.name}": world(${layer.x.toFixed(1)}, ${layer.y.toFixed(1)}) relative_to_output(${relativeToOutput.x.toFixed(1)}, ${relativeToOutput.y.toFixed(1)}) size(${layer.width.toFixed(1)}x${layer.height.toFixed(1)})`);
        });
        log.info("=== END LAYER DRAG ===");
    }

    handleMouseLeave(e: MouseEvent): void {
        const coords = this.getMouseCoordinates(e);
        if (this.canvas.maskTool.isActive) {
            this.canvas.maskTool.handleMouseLeave();
            if (this.canvas.maskTool.isDrawing) {
                this.canvas.maskTool.handleMouseUp(coords.view);
            }
            this.canvas.render();
            return;
        }
        if (this.interaction.mode !== 'none') {
            this.resetInteractionState();
            this.canvas.render();
        }

        if (this.canvas.canvasLayers.internalClipboard.length > 0) {
            this.canvas.canvasLayers.internalClipboard = [];
            log.info("Internal clipboard cleared - mouse left canvas");
        }
    }

    handleMouseEnter(e: MouseEvent): void {
        if (this.canvas.maskTool.isActive) {
            this.canvas.maskTool.handleMouseEnter();
        }
    }

    handleContextMenu(e: MouseEvent): void {
        // Always prevent browser context menu - we handle all right-click interactions ourselves
        e.preventDefault();
        e.stopPropagation();
    }

    handleWheel(e: WheelEvent): void {
        this.preventEventDefaults(e);
        const coords = this.getMouseCoordinates(e);
        
        if (this.canvas.maskTool.isActive || this.canvas.canvasSelection.selectedLayers.length === 0) {
            // Zoom operation for mask tool or when no layers selected
            const zoomFactor = e.deltaY < 0 ? 1.1 : 1 / 1.1;
            this.performZoomOperation(coords.world, zoomFactor);
        } else {
            // Check if mouse is over any selected layer
            const isOverSelectedLayer = this.isPointInSelectedLayers(coords.world.x, coords.world.y);
            
            if (isOverSelectedLayer) {
                // Layer transformation when layers are selected and mouse is over selected layer
                this.handleLayerWheelTransformation(e);
            } else {
                // Zoom operation when mouse is not over selected layers
                const zoomFactor = e.deltaY < 0 ? 1.1 : 1 / 1.1;
                this.performZoomOperation(coords.world, zoomFactor);
            }
        }
        
        this.canvas.render();
        if (!this.canvas.maskTool.isActive) {
            this.canvas.requestSaveState();
        }
    }

    private handleLayerWheelTransformation(e: WheelEvent): void {
        const mods = this.getModifierState(e);
        const rotationStep = 5 * (e.deltaY > 0 ? -1 : 1);
        const direction = e.deltaY < 0 ? 1 : -1;

        this.canvas.canvasSelection.selectedLayers.forEach((layer: Layer) => {
            if (mods.shift) {
                this.handleLayerRotation(layer, mods.ctrl, direction, rotationStep);
            } else {
                this.handleLayerScaling(layer, mods.ctrl, e.deltaY);
            }
        });
    }

    private handleLayerRotation(layer: Layer, isCtrlPressed: boolean, direction: number, rotationStep: number): void {
        if (isCtrlPressed) {
            // Snap to absolute values
            const snapAngle = 5;
            if (direction > 0) {
                layer.rotation = Math.ceil((layer.rotation + 0.1) / snapAngle) * snapAngle;
            } else {
                layer.rotation = Math.floor((layer.rotation - 0.1) / snapAngle) * snapAngle;
            }
        } else {
            // Fixed step rotation
            layer.rotation += rotationStep;
        }
    }

    private handleLayerScaling(layer: Layer, isCtrlPressed: boolean, deltaY: number): void {
        const oldWidth = layer.width;
        const oldHeight = layer.height;
        let scaleFactor;

        if (isCtrlPressed) {
            const direction = deltaY > 0 ? -1 : 1;
            const baseDimension = Math.max(layer.width, layer.height);
            const newBaseDimension = baseDimension + direction;
            if (newBaseDimension < 10) return;
            scaleFactor = newBaseDimension / baseDimension;
        } else {
            scaleFactor = this.calculateGridBasedScaling(oldHeight, deltaY);
        }

        if (scaleFactor && isFinite(scaleFactor)) {
            layer.width *= scaleFactor;
            layer.height *= scaleFactor;
            layer.x += (oldWidth - layer.width) / 2;
            layer.y += (oldHeight - layer.height) / 2;
            
            // Handle wheel scaling end for layers with blend area
            this.canvas.canvasLayers.handleWheelScalingEnd(layer);
        }
    }

    private calculateGridBasedScaling(oldHeight: number, deltaY: number): number {
        const gridSize = 64; // Grid size - could be made configurable in the future
        const direction = deltaY > 0 ? -1 : 1;
        let targetHeight;

        if (direction > 0) {
            targetHeight = (Math.floor(oldHeight / gridSize) + 1) * gridSize;
        } else {
            targetHeight = (Math.ceil(oldHeight / gridSize) - 1) * gridSize;
        }
        
        if (targetHeight < gridSize / 2) {
            targetHeight = gridSize / 2;
        }
        
        if (Math.abs(oldHeight - targetHeight) < 1) {
            if (direction > 0) targetHeight += gridSize;
            else targetHeight -= gridSize;
            if (targetHeight < gridSize / 2) return 0;
        }

        return targetHeight / oldHeight;
    }

    handleKeyDown(e: KeyboardEvent): void {
        if (e.key === 'Control') this.interaction.isCtrlPressed = true;
        if (e.key === 'Meta') this.interaction.isMetaPressed = true;
        if (e.key === 'Shift') this.interaction.isShiftPressed = true;
        if (e.key === 'Alt') {
            this.interaction.isAltPressed = true;
            e.preventDefault();
        }
        if (e.key.toLowerCase() === 's') {
            this.interaction.isSPressed = true;
            e.preventDefault();
            e.stopPropagation();
        }

        // Check if Shift+S is being held down
        if (this.interaction.isShiftPressed && this.interaction.isSPressed && !this.interaction.isCtrlPressed && !this.canvas.shapeTool.isActive) {
            this.canvas.shapeTool.activate();
            return;
        }
        
        // Globalne skróty (Undo/Redo/Copy/Paste)
        const mods = this.getModifierState(e);
        if (mods.ctrl || mods.meta) {
            let handled = true;
            switch (e.key.toLowerCase()) {
                case 'z':
                    if (mods.shift) {
                        this.canvas.redo();
                    } else {
                        this.canvas.undo();
                    }
                    break;
                case 'y':
                    this.canvas.redo();
                    break;
                case 'c':
                    if (this.canvas.canvasSelection.selectedLayers.length > 0) {
                        this.canvas.canvasLayers.copySelectedLayers();
                    }
                    break;
                default:
                    handled = false;
                    break;
            }
            if (handled) {
                e.preventDefault();
                e.stopPropagation();
                return;
            }
        }

        // Skróty kontekstowe (zależne od zaznaczenia)
        if (this.canvas.canvasSelection.selectedLayers.length > 0) {
            const step = mods.shift ? 10 : 1;
            let needsRender = false;
            
            // Używamy e.code dla spójności i niezależności od układu klawiatury
            const movementKeys = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'BracketLeft', 'BracketRight'];
            if (movementKeys.includes(e.code)) {
                e.preventDefault();
                e.stopPropagation();
                this.interaction.keyMovementInProgress = true;

                if (e.code === 'ArrowLeft') this.canvas.canvasSelection.selectedLayers.forEach((l: Layer) => l.x -= step);
                if (e.code === 'ArrowRight') this.canvas.canvasSelection.selectedLayers.forEach((l: Layer) => l.x += step);
                if (e.code === 'ArrowUp') this.canvas.canvasSelection.selectedLayers.forEach((l: Layer) => l.y -= step);
                if (e.code === 'ArrowDown') this.canvas.canvasSelection.selectedLayers.forEach((l: Layer) => l.y += step);
                if (e.code === 'BracketLeft') this.canvas.canvasSelection.selectedLayers.forEach((l: Layer) => l.rotation -= step);
                if (e.code === 'BracketRight') this.canvas.canvasSelection.selectedLayers.forEach((l: Layer) => l.rotation += step);

                needsRender = true;
            }

            if (e.key === 'Delete' || e.key === 'Backspace') {
                e.preventDefault();
                e.stopPropagation();
                this.canvas.canvasSelection.removeSelectedLayers();
                return;
            }
            
            if (needsRender) {
                this.canvas.render();
            }
        }
    }

    handleKeyUp(e: KeyboardEvent): void {
        if (e.key === 'Control') this.interaction.isCtrlPressed = false;
        if (e.key === 'Meta') this.interaction.isMetaPressed = false;
        if (e.key === 'Shift') this.interaction.isShiftPressed = false;
        if (e.key === 'Alt') this.interaction.isAltPressed = false;
        if (e.key.toLowerCase() === 's') this.interaction.isSPressed = false;

        // Deactivate shape tool when Shift or S is released
        if (this.canvas.shapeTool.isActive && (!this.interaction.isShiftPressed || !this.interaction.isSPressed)) {
            this.canvas.shapeTool.deactivate();
        }

        const movementKeys = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'BracketLeft', 'BracketRight'];
        if (movementKeys.includes(e.code) && this.interaction.keyMovementInProgress) {
            this.canvas.requestSaveState(); // Użyj opóźnionego zapisu
            this.interaction.keyMovementInProgress = false;
        }
    }

    handleBlur(): void {
        log.debug('Window lost focus, resetting key states.');
        this.interaction.isCtrlPressed = false;
        this.interaction.isMetaPressed = false;
        this.interaction.isAltPressed = false;
        this.interaction.isShiftPressed = false;
        this.interaction.isSPressed = false;
        this.interaction.keyMovementInProgress = false;

        // Deactivate shape tool when window loses focus
        if (this.canvas.shapeTool.isActive) {
            this.canvas.shapeTool.deactivate();
        }

        // Also reset any interaction that relies on a key being held down
        if (this.interaction.mode === 'dragging' && this.interaction.hasClonedInDrag) {
            // If we were in the middle of a cloning drag, finalize it
            this.canvas.saveState();
            this.canvas.canvasState.saveStateToDB();
        }
        
        // Reset interaction mode if it's something that can get "stuck"
        if (this.interaction.mode !== 'none' && this.interaction.mode !== 'drawingMask') {
            this.resetInteractionState();
            this.canvas.render();
        }
    }

    updateCursor(worldCoords: Point): void {
        // If actively rotating, show grabbing cursor
        if (this.interaction.mode === 'rotating') {
            this.canvas.canvas.style.cursor = 'grabbing';
            return;
        }

        const transformTarget = this.canvas.canvasLayers.getHandleAtPosition(worldCoords.x, worldCoords.y);

        if (transformTarget) {
            const handleName = transformTarget.handle;
            const cursorMap: { [key: string]: string } = {
                'n': 'ns-resize', 's': 'ns-resize', 'e': 'ew-resize', 'w': 'ew-resize',
                'nw': 'nwse-resize', 'se': 'nwse-resize', 'ne': 'nesw-resize', 'sw': 'nesw-resize',
                'rot': 'grab'
            };
            this.canvas.canvas.style.cursor = cursorMap[handleName];
        } else if (this.canvas.canvasLayers.getLayerAtPosition(worldCoords.x, worldCoords.y)) {
            this.canvas.canvas.style.cursor = 'move';
        } else {
            this.canvas.canvas.style.cursor = 'default';
        }
    }

    startLayerTransform(layer: Layer, handle: string, worldCoords: Point): void {
        this.interaction.transformingLayer = layer;
        this.interaction.transformOrigin = {
            x: layer.x, y: layer.y,
            width: layer.width, height: layer.height,
            rotation: layer.rotation,
            centerX: layer.x + layer.width / 2,
            centerY: layer.y + layer.height / 2,
            originalWidth: layer.originalWidth,
            originalHeight: layer.originalHeight,
            cropBounds: layer.cropBounds ? { ...layer.cropBounds } : undefined
        };
        this.interaction.dragStart = {...worldCoords};

        if (handle === 'rot') {
            this.interaction.mode = 'rotating';
        } else {
            this.interaction.mode = 'resizing';
            this.interaction.resizeHandle = handle;
            const handles = this.canvas.canvasLayers.getHandles(layer);
            const oppositeHandleKey: { [key: string]: string } = {
                'n': 's', 's': 'n', 'e': 'w', 'w': 'e',
                'nw': 'se', 'se': 'nw', 'ne': 'sw', 'sw': 'ne'
            };
            this.interaction.resizeAnchor = handles[oppositeHandleKey[handle]];
        }
        this.canvas.render();
    }

    prepareForDrag(layer: Layer, worldCoords: Point): void {
        // Zaktualizuj zaznaczenie, ale nie zapisuj stanu
        // Support both Ctrl (Windows/Linux) and Cmd (macOS) for multi-selection
        const mods = this.getModifierState();
        if (mods.ctrl || mods.meta) {
            const index = this.canvas.canvasSelection.selectedLayers.indexOf(layer);
            if (index === -1) {
                this.canvas.canvasSelection.updateSelection([...this.canvas.canvasSelection.selectedLayers, layer]);
            } else {
                const newSelection = this.canvas.canvasSelection.selectedLayers.filter((l: Layer) => l !== layer);
                this.canvas.canvasSelection.updateSelection(newSelection);
            }
        } else {
            if (!this.canvas.canvasSelection.selectedLayers.includes(layer)) {
                this.canvas.canvasSelection.updateSelection([layer]);
            }
        }
        
        this.interaction.mode = 'potential-drag';
        this.interaction.dragStart = {...worldCoords};
    }

    startPanning(e: MouseEvent, clearSelection: boolean = true): void {
        // Unified panning method - can optionally clear selection
        if (clearSelection && !this.interaction.isCtrlPressed) {
            this.canvas.canvasSelection.updateSelection([]);
        }
        this.interaction.mode = 'panning';
        this.interaction.panStart = { x: e.clientX, y: e.clientY };
    }

    startCanvasResize(worldCoords: Point): void {
        this.interaction.mode = 'resizingCanvas';
        const startX = snapToGrid(worldCoords.x);
        const startY = snapToGrid(worldCoords.y);
        this.interaction.canvasResizeStart = {x: startX, y: startY};
        this.interaction.canvasResizeRect = {x: startX, y: startY, width: 0, height: 0};
        this.canvas.render();
    }

    startCanvasMove(worldCoords: Point): void {
        this.interaction.mode = 'movingCanvas';
        this.interaction.dragStart = { ...worldCoords };
        this.canvas.canvas.style.cursor = 'grabbing';
        this.canvas.render();
    }

    updateCanvasMove(worldCoords: Point): void {
        const dx = worldCoords.x - this.interaction.dragStart.x;
        const dy = worldCoords.y - this.interaction.dragStart.y;
        
        // Po prostu przesuwamy outputAreaBounds
        const bounds = this.canvas.outputAreaBounds;
        this.interaction.canvasMoveRect = {
            x: snapToGrid(bounds.x + dx),
            y: snapToGrid(bounds.y + dy),
            width: bounds.width,
            height: bounds.height
        };

        this.canvas.render();
    }

    finalizeCanvasMove(): void {
        const moveRect = this.interaction.canvasMoveRect;

        if (moveRect) {
            // Po prostu aktualizujemy outputAreaBounds na nową pozycję
            this.canvas.outputAreaBounds = {
                x: moveRect.x,
                y: moveRect.y,
                width: moveRect.width,
                height: moveRect.height
            };
            
            // Update mask canvas to ensure it covers the new output area position
            this.canvas.maskTool.updateMaskCanvasForOutputArea();
        }
        
        this.canvas.render();
        this.canvas.saveState();
    }

    panViewport(e: MouseEvent): void {
        const dx = e.clientX - this.interaction.panStart.x;
        const dy = e.clientY - this.interaction.panStart.y;
        this.canvas.viewport.x -= dx / this.canvas.viewport.zoom;
        this.canvas.viewport.y -= dy / this.canvas.viewport.zoom;
        this.interaction.panStart = {x: e.clientX, y: e.clientY};
        
        // Update stroke overlay if mask tool is drawing during pan
        if (this.canvas.maskTool.isDrawing) {
            this.canvas.maskTool.handleViewportChange();
        }
        
        this.canvas.render();
        this.canvas.onViewportChange?.();
    }

    dragLayers(worldCoords: Point): void {
        if (this.interaction.isAltPressed && !this.interaction.hasClonedInDrag && this.canvas.canvasSelection.selectedLayers.length > 0) {
            // Scentralizowana logika duplikowania
            const newLayers = this.canvas.canvasSelection.duplicateSelectedLayers();

            // Zresetuj pozycje przeciągania dla nowych, zduplikowanych warstw
            this.originalLayerPositions.clear();
            newLayers.forEach((l: Layer) => {
                this.originalLayerPositions.set(l, { x: l.x, y: l.y });
            });
            this.interaction.hasClonedInDrag = true;
        }
        const totalDx = worldCoords.x - this.interaction.dragStart.x;
        const totalDy = worldCoords.y - this.interaction.dragStart.y;
        let finalDx = totalDx, finalDy = totalDy;

        if (this.interaction.isCtrlPressed && this.canvas.canvasSelection.selectedLayers.length > 0) {
            const firstLayer = this.canvas.canvasSelection.selectedLayers[0];
            const originalPos = this.originalLayerPositions.get(firstLayer);
            if (originalPos) {
                const tempLayerForSnap = {
                    ...firstLayer,
                    x: originalPos.x + totalDx,
                    y: originalPos.y + totalDy
                };
                const snapAdjustment = getSnapAdjustment(tempLayerForSnap);
                if (snapAdjustment) {
                    finalDx += snapAdjustment.x;
                    finalDy += snapAdjustment.y;
                }
            }
        }

        this.canvas.canvasSelection.selectedLayers.forEach((layer: Layer) => {
            const originalPos = this.originalLayerPositions.get(layer);
            if (originalPos) {
                layer.x = originalPos.x + finalDx;
                layer.y = originalPos.y + finalDy;
            }
        });
        this.canvas.render();
    }

    resizeLayerFromHandle(worldCoords: Point, isShiftPressed: boolean): void {
        const layer = this.interaction.transformingLayer;
        if (!layer) return;

        let mouseX = worldCoords.x;
        let mouseY = worldCoords.y;

        if (this.interaction.isCtrlPressed) {
            const snapThreshold = 10 / this.canvas.viewport.zoom;
            mouseX = Math.abs(mouseX - snapToGrid(mouseX)) < snapThreshold ? snapToGrid(mouseX) : mouseX;
            mouseY = Math.abs(mouseY - snapToGrid(mouseY)) < snapThreshold ? snapToGrid(mouseY) : mouseY;
        }

        const o = this.interaction.transformOrigin;
        if (!o) return;
        
        const handle = this.interaction.resizeHandle;
        const anchor = this.interaction.resizeAnchor;
        const rad = o.rotation * Math.PI / 180;
        const cos = Math.cos(rad);
        const sin = Math.sin(rad);

        // Vector from anchor to mouse
        const vecX = mouseX - anchor.x;
        const vecY = mouseY - anchor.y;

        // Rotate vector to align with layer's local coordinates
        let localVecX = vecX * cos + vecY * sin;
        let localVecY = vecY * cos - vecX * sin;

        // Determine sign based on handle
        const signX = handle?.includes('e') ? 1 : (handle?.includes('w') ? -1 : 0);
        const signY = handle?.includes('s') ? 1 : (handle?.includes('n') ? -1 : 0);
        
        localVecX *= signX;
        localVecY *= signY;

        // If not a corner handle, keep original dimension
        if (signX === 0) localVecX = o.width;
        if (signY === 0) localVecY = o.height;

        if (layer.cropMode && o.cropBounds && o.originalWidth && o.originalHeight) {
            // CROP MODE: Calculate delta based on mouse movement and apply to cropBounds.
            
            // Calculate mouse movement since drag start, in the layer's local coordinate system.
            const dragStartX_local = this.interaction.dragStart.x - (o.centerX ?? 0);
            const dragStartY_local = this.interaction.dragStart.y - (o.centerY ?? 0);
            const mouseX_local = mouseX - (o.centerX ?? 0);
            const mouseY_local = mouseY - (o.centerY ?? 0);
            
            // Rotate mouse delta into the layer's unrotated frame
            const deltaX_world = mouseX_local - dragStartX_local;
            const deltaY_world = mouseY_local - dragStartY_local;
            let mouseDeltaX_local = deltaX_world * cos + deltaY_world * sin;
            let mouseDeltaY_local = deltaY_world * cos - deltaX_world * sin;

            if (layer.flipH) {
                mouseDeltaX_local *= -1;
            }
            if (layer.flipV) {
                mouseDeltaY_local *= -1;
            }
            
            // Convert the on-screen mouse delta to an image-space delta.
            const screenToImageScaleX = o.originalWidth / o.width;
            const screenToImageScaleY = o.originalHeight / o.height;
            
            const delta_image_x = mouseDeltaX_local * screenToImageScaleX;
            const delta_image_y = mouseDeltaY_local * screenToImageScaleY;
            
            let newCropBounds = { ...o.cropBounds }; // Start with the bounds from the beginning of the drag

            // Apply the image-space delta to the appropriate edges of the crop bounds
            const isFlippedH = layer.flipH;
            const isFlippedV = layer.flipV;
            
            if (handle?.includes('w')) {
                if (isFlippedH) newCropBounds.width += delta_image_x;
                else {
                    newCropBounds.x += delta_image_x;
                    newCropBounds.width -= delta_image_x;
                }
            }
            if (handle?.includes('e')) {
                if (isFlippedH) {
                    newCropBounds.x += delta_image_x;
                    newCropBounds.width -= delta_image_x;
                } else newCropBounds.width += delta_image_x;
            }
            if (handle?.includes('n')) {
                if (isFlippedV) newCropBounds.height += delta_image_y;
                else {
                    newCropBounds.y += delta_image_y;
                    newCropBounds.height -= delta_image_y;
                }
            }
            if (handle?.includes('s')) {
                if (isFlippedV) {
                    newCropBounds.y += delta_image_y;
                    newCropBounds.height -= delta_image_y;
                } else newCropBounds.height += delta_image_y;
            }
			
			// Clamp crop bounds to stay within the original image and maintain minimum size
            if (newCropBounds.width < 1) {
                if (handle?.includes('w')) newCropBounds.x = o.cropBounds.x + o.cropBounds.width -1;
                newCropBounds.width = 1;
            }
            if (newCropBounds.height < 1) {
                if (handle?.includes('n')) newCropBounds.y = o.cropBounds.y + o.cropBounds.height - 1;
                newCropBounds.height = 1;
            }
            if (newCropBounds.x < 0) {
                newCropBounds.width += newCropBounds.x;
                newCropBounds.x = 0;
            }
            if (newCropBounds.y < 0) {
                newCropBounds.height += newCropBounds.y;
                newCropBounds.y = 0;
            }
            if (newCropBounds.x + newCropBounds.width > o.originalWidth) {
                newCropBounds.width = o.originalWidth - newCropBounds.x;
            }
            if (newCropBounds.y + newCropBounds.height > o.originalHeight) {
                newCropBounds.height = o.originalHeight - newCropBounds.y;
            }

            layer.cropBounds = newCropBounds;

        } else {
            // TRANSFORM MODE: Resize the layer's main transform frame
            let newWidth = localVecX;
            let newHeight = localVecY;
            
            if (isShiftPressed) {
                const originalAspectRatio = o.width / o.height;
                if (Math.abs(newWidth) > Math.abs(newHeight) * originalAspectRatio) {
                    newHeight = (Math.sign(newHeight) || 1) * Math.abs(newWidth) / originalAspectRatio;
                } else {
                    newWidth = (Math.sign(newWidth) || 1) * Math.abs(newHeight) * originalAspectRatio;
                }
            }

            if (newWidth < 10) newWidth = 10;
            if (newHeight < 10) newHeight = 10;
            
            layer.width = newWidth;
            layer.height = newHeight;
            
            // Update position to keep anchor point fixed
            const deltaW = layer.width - o.width;
            const deltaH = layer.height - o.height;
            const shiftX = (deltaW / 2) * signX;
            const shiftY = (deltaH / 2) * signY;
            const worldShiftX = shiftX * cos - shiftY * sin;
            const worldShiftY = shiftX * sin + shiftY * cos;
            const newCenterX = o.centerX + worldShiftX;
            const newCenterY = o.centerY + worldShiftY;
            layer.x = newCenterX - layer.width / 2;
            layer.y = newCenterY - layer.height / 2;
        }

        this.canvas.render();
    }

    rotateLayerFromHandle(worldCoords: Point, isShiftPressed: boolean): void {
        const layer = this.interaction.transformingLayer;
        if (!layer) return;

        const o = this.interaction.transformOrigin;
        if (!o) return;
        const startAngle = Math.atan2(this.interaction.dragStart.y - o.centerY, this.interaction.dragStart.x - o.centerX);
        const currentAngle = Math.atan2(worldCoords.y - o.centerY, worldCoords.x - o.centerX);
        let angleDiff = (currentAngle - startAngle) * 180 / Math.PI;
        let newRotation = o.rotation + angleDiff;

        if (isShiftPressed) {
            newRotation = Math.round(newRotation / 15) * 15;
        }

        layer.rotation = newRotation;
        this.canvas.render();
    }

    updateCanvasResize(worldCoords: Point): void {
        if (!this.interaction.canvasResizeRect) return;
        const snappedMouseX = snapToGrid(worldCoords.x);
        const snappedMouseY = snapToGrid(worldCoords.y);
        const start = this.interaction.canvasResizeStart;

        this.interaction.canvasResizeRect.x = Math.min(snappedMouseX, start.x);
        this.interaction.canvasResizeRect.y = Math.min(snappedMouseY, start.y);
        this.interaction.canvasResizeRect.width = Math.abs(snappedMouseX - start.x);
        this.interaction.canvasResizeRect.height = Math.abs(snappedMouseY - start.y);
        this.canvas.render();
    }

    finalizeCanvasResize(): void {
        if (this.interaction.canvasResizeRect && this.interaction.canvasResizeRect.width > 1 && this.interaction.canvasResizeRect.height > 1) {
            const newWidth = Math.round(this.interaction.canvasResizeRect.width);
            const newHeight = Math.round(this.interaction.canvasResizeRect.height);
            const finalX = this.interaction.canvasResizeRect.x;
            const finalY = this.interaction.canvasResizeRect.y;

            // Po prostu aktualizujemy outputAreaBounds na nowy obszar
            this.canvas.outputAreaBounds = {
                x: finalX,
                y: finalY,
                width: newWidth,
                height: newHeight
            };

            this.canvas.updateOutputAreaSize(newWidth, newHeight);
        }
        
        this.canvas.render();
        this.canvas.saveState();
    }

    handleDragOver(e: DragEvent): void {
        this.preventEventDefaults(e);
        if (e.dataTransfer) e.dataTransfer.dropEffect = 'copy';
    }

    handleDragEnter(e: DragEvent): void {
        this.preventEventDefaults(e);
        this.setDragDropStyling(true);
    }

    handleDragLeave(e: DragEvent): void {
        this.preventEventDefaults(e);
        if (!this.canvas.canvas.contains(e.relatedTarget as Node)) {
            this.setDragDropStyling(false);
        }
    }

    async handleDrop(e: DragEvent): Promise<void> {
        this.preventEventDefaults(e);
        log.info("Canvas drag & drop event intercepted - preventing ComfyUI workflow loading");

        this.setDragDropStyling(false);

        if (!e.dataTransfer) return;
        const files = Array.from(e.dataTransfer.files);
        const coords = this.getMouseCoordinates(e);

        log.info(`Dropped ${files.length} file(s) onto canvas at position (${coords.world.x}, ${coords.world.y})`);

        for (const file of files) {
            if (file.type.startsWith('image/')) {
                try {
                    await this.loadDroppedImageFile(file, coords.world);
                    log.info(`Successfully loaded dropped image: ${file.name}`);
                } catch (error) {
                    log.error(`Failed to load dropped image ${file.name}:`, error);
                }
            } else {
                log.warn(`Skipped non-image file: ${file.name} (${file.type})`);
            }
        }
    }

    async loadDroppedImageFile(file: File, worldCoords: Point): Promise<void> {
        const reader = new FileReader();
        reader.onload = async (e) => {
            const img = new Image();
            img.onload = async () => {

                const fitOnAddWidget = this.canvas.node.widgets.find((w: any) => w.name === "fit_on_add");
                const addMode = fitOnAddWidget && fitOnAddWidget.value ? 'fit' : 'center';

                await this.canvas.canvasLayers.addLayerWithImage(img, {}, addMode);
            };
            img.onerror = () => {
                log.error(`Failed to load dropped image: ${file.name}`);
            };
            if (e.target?.result) {
                img.src = e.target.result as string;
            }
        };
        reader.onerror = () => {
            log.error(`Failed to read dropped file: ${file.name}`);
        };
        reader.readAsDataURL(file);
    }

    defineOutputAreaWithShape(shape: any): void {
        const boundingBox = this.canvas.shapeTool.getBoundingBox();
        if (boundingBox && boundingBox.width > 1 && boundingBox.height > 1) {
            this.canvas.saveState();

            // If there's an existing custom shape and auto-apply shape mask is enabled, remove the previous mask
            if (this.canvas.outputAreaShape && this.canvas.autoApplyShapeMask) {
                log.info("Removing previous shape mask before defining new custom shape");
                this.canvas.maskTool.removeShapeMask();
            }

            this.canvas.outputAreaShape = {
                ...shape,
                points: shape.points.map((p: any) => ({
                    x: p.x - boundingBox.x,
                    y: p.y - boundingBox.y
                }))
            };

            const newWidth = Math.round(boundingBox.width);
            const newHeight = Math.round(boundingBox.height);
            const newX = Math.round(boundingBox.x);
            const newY = Math.round(boundingBox.y);

            // Store the original canvas size for extension calculations
            this.canvas.originalCanvasSize = { width: newWidth, height: newHeight };
            
            // Store the original position where custom shape was drawn for extension calculations
            this.canvas.originalOutputAreaPosition = { x: newX, y: newY };

            // If extensions are enabled, we need to recalculate outputAreaBounds with current extensions
            if (this.canvas.outputAreaExtensionEnabled) {
                const ext = this.canvas.outputAreaExtensions;
                const extendedWidth = newWidth + ext.left + ext.right;
                const extendedHeight = newHeight + ext.top + ext.bottom;
                
                // Update canvas size with extensions
                this.canvas.updateOutputAreaSize(extendedWidth, extendedHeight, false);
                
                // Set outputAreaBounds accounting for extensions
                this.canvas.outputAreaBounds = {
                    x: newX - ext.left,  // Adjust position by left extension
                    y: newY - ext.top,   // Adjust position by top extension
                    width: extendedWidth,
                    height: extendedHeight
                };
                
                log.info(`New custom shape with extensions: original(${newX}, ${newY}) extended(${newX - ext.left}, ${newY - ext.top}) size(${extendedWidth}x${extendedHeight})`);
            } else {
                // No extensions - use original size and position
                this.canvas.updateOutputAreaSize(newWidth, newHeight, false);
                
                this.canvas.outputAreaBounds = {
                    x: newX,
                    y: newY,
                    width: newWidth,
                    height: newHeight
                };
                
                log.info(`New custom shape without extensions: position(${newX}, ${newY}) size(${newWidth}x${newHeight})`);
            }

            // Update mask canvas to ensure it covers the new output area position
            this.canvas.maskTool.updateMaskCanvasForOutputArea();

            // If auto-apply shape mask is enabled, automatically apply the mask with current settings
            if (this.canvas.autoApplyShapeMask) {
                log.info("Auto-applying shape mask to new custom shape with current settings");
                this.canvas.maskTool.applyShapeMask();
            }

            this.canvas.saveState();
            this.canvas.render();
        }
    }

    async handlePasteEvent(e: ClipboardEvent): Promise<void> {

        const shouldHandle = this.canvas.isMouseOver ||
            this.canvas.canvas.contains(document.activeElement) ||
            document.activeElement === this.canvas.canvas ||
            document.activeElement === document.body;
        
        if (!shouldHandle) {
            log.debug("Paste event ignored - not focused on canvas");
            return;
        }

        log.info("Paste event detected, checking clipboard preference");

        const preference = this.canvas.canvasLayers.clipboardPreference;
        
        if (preference === 'clipspace') {

            log.info("Clipboard preference is clipspace, delegating to ClipboardManager");
            e.preventDefault();
            e.stopPropagation();
            await this.canvas.canvasLayers.clipboardManager.handlePaste('mouse', preference);
            return;
        }

        const clipboardData = e.clipboardData;
        if (clipboardData && clipboardData.items) {
            for (const item of clipboardData.items) {
                if (item.type.startsWith('image/')) {
                    e.preventDefault();
                    e.stopPropagation();

                    const file = item.getAsFile();
                    if (file) {
                        log.info("Found direct image data in paste event");
                        const reader = new FileReader();
                        reader.onload = async (event) => {
                            const img = new Image();
                            img.onload = async () => {
                                await this.canvas.canvasLayers.addLayerWithImage(img, {}, 'mouse');
                            };
                            if (event.target?.result) {
                                img.src = event.target.result as string;
                            }
                        };
                        reader.readAsDataURL(file);
                        return;
                    }
                }
            }
        }

        await this.canvas.canvasLayers.clipboardManager.handlePaste('mouse', preference);
    }

}
