import React, {
  useCallback,
  useEffect,
  useLayoutEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import { resolveDocumentAssetUrl, createAssetView } from './asset_manager';
import { useAssetNavigator } from './hooks/useAssetNavigator';
import { ArrowLeftIcon, ArrowRightIcon } from './ui/icons';
import PreviewZoomOverlay from './detail/PreviewZoomOverlay';
import { getTagColorStyle } from './utils/colors';
import './skeuomorphic_ws.css';

const ITEM_WIDTH = 220;
const ITEM_HEIGHT = 260;
const CANVAS_PADDING = 24;
const ROTATION_RANGE = 7;
const JITTER_X = 26;
const JITTER_Y = 32;
const DEFAULT_CANVAS_WIDTH = 1024;
const DEFAULT_CANVAS_HEIGHT = 680;
const TAG_MIME_TYPES = ['application/x-papercrate-tag', 'text/papercrate-tag'];

const resolveSizeKey = (doc) =>
  doc?.id || doc?.document_id || doc?.uuid || doc?.original_name || doc?.title || 'doc';

const CARD_MIN = 240;
const CARD_MAX = 340;
const EMPTY_CARD_ASPECT = 1.4;
const TAG_REMOVE_DISTANCE = 160;

const DEBUG_DRAG = false;
const DEBUG_FOCUS = true;
const DEBUG_DROP = true;

const resolveTagKey = (tag) => {
  if (!tag) {
    return null;
  }
  const key = tag.id ?? tag.uuid ?? tag.slug ?? tag.label;
  return key != null ? String(key) : null;
};

const SkeuoPreviewCard = ({
  doc,
  title,
  ensureAssetUrl,
  getDocumentAsset,
  navScale = 1,
  prefetch = 3,
  onNavigatorSnapshot,
}) => {
  const navigator = useAssetNavigator({
    document: doc,
    assetType: 'preview',
    ensureAssetUrl,
    getAsset: getDocumentAsset,
    prefetch,
  });

  const { currentUrl, cardinality, canGoPrev, canGoNext, currentMetadata, ordinal } = navigator;
  const docId = doc?.id ?? null;

  const metadataWidth = Number(currentMetadata?.width);
  const metadataHeight = Number(currentMetadata?.height);

  useEffect(() => {
    if (!onNavigatorSnapshot || !docId) {
      return undefined;
    }
    const snapshot = {
      url: currentUrl || null,
      alt: title,
      canGoPrev,
      canGoNext,
      goPrev: navigator.goPrev,
      goNext: navigator.goNext,
      ordinal,
      width: Number.isFinite(metadataWidth) && metadataWidth > 0 ? metadataWidth : null,
      height: Number.isFinite(metadataHeight) && metadataHeight > 0 ? metadataHeight : null,
    };
    onNavigatorSnapshot(docId, snapshot);
    return () => onNavigatorSnapshot(docId, null);
  }, [
    docId,
    currentUrl,
    title,
    canGoPrev,
    canGoNext,
    ordinal,
    metadataWidth,
    metadataHeight,
    navigator.goPrev,
    navigator.goNext,
    onNavigatorSnapshot,
  ]);
  const hasPreview = Boolean(currentUrl);
  const cardClasses = ['skeuo-item__card'];
  if (!hasPreview) cardClasses.push('skeuo-item__card--empty');
  const showNav = hasPreview && (cardinality > 1 || canGoPrev || canGoNext);
  const navStyle = useMemo(() => ({ '--nav-scale': navScale }), [navScale]);

  return (
    <div className={cardClasses.join(' ')}>
      {hasPreview ? (
        <img src={currentUrl} alt={title} />
      ) : (
        <div className="skeuo-item__empty">
          <div className="skeuo-item__placeholder">DOC</div>
          <div className="skeuo-item__title" title={title}>
            {title}
          </div>
        </div>
      )}
      {showNav ? (
        <div className="skeuo-card__nav" style={navStyle}>
          <button
            type="button"
            className="skeuo-card__nav-button"
            onClick={(event) => {
              event.preventDefault();
              event.stopPropagation();
              navigator.goPrev();
            }}
            onPointerDown={(event) => {
              event.preventDefault();
              event.stopPropagation();
            }}
            onPointerUp={(event) => {
              event.preventDefault();
              event.stopPropagation();
            }}
            onMouseDown={(event) => {
              event.preventDefault();
              event.stopPropagation();
            }}
            onMouseUp={(event) => {
              event.preventDefault();
              event.stopPropagation();
            }}
            disabled={!canGoPrev}
            aria-label="Previous preview"
          >
            <ArrowLeftIcon />
          </button>
          <button
            type="button"
            className="skeuo-card__nav-button"
            onClick={(event) => {
              event.preventDefault();
              event.stopPropagation();
              navigator.goNext();
            }}
            onPointerDown={(event) => {
              event.preventDefault();
              event.stopPropagation();
            }}
            onPointerUp={(event) => {
              event.preventDefault();
              event.stopPropagation();
            }}
            onMouseDown={(event) => {
              event.preventDefault();
              event.stopPropagation();
            }}
            onMouseUp={(event) => {
              event.preventDefault();
              event.stopPropagation();
            }}
            disabled={!canGoNext}
            aria-label="Next preview"
          >
            <ArrowRightIcon />
          </button>
        </div>
      ) : null}
    </div>
  );
};

const generateInitialLayout = (
  entries,
  {
    canvasWidth,
    canvasHeight,
    padding,
    startZ = 0,
    rotationRange = ROTATION_RANGE,
    minSpacing = 48,
    shelfWidth = 0,
  },
) => {
  const layout = new Map();
  let currentZ = startZ;
  let maxZ = startZ;

  if (!entries.length) {
    return { layout, maxZ };
  }

  const shelfOffset = Math.max(shelfWidth, 0);
  const usableWidth = Math.max(canvasWidth - shelfOffset - padding * 2, 1);
  const usableHeight = Math.max(canvasHeight - padding * 2, 1);

  const spacingBuffer = Math.max(minSpacing, 0);
  const placed = [];

  const resolveBounds = (width, height) => {
    const halfWidth = width / 2;
    const halfHeight = height / 2;
    return {
      minCenterX: padding + halfWidth,
      maxCenterX: Math.max(
        padding + halfWidth,
        canvasWidth - shelfOffset - padding - halfWidth,
      ),
      minCenterY: padding + halfHeight,
      maxCenterY: Math.max(padding + halfHeight, canvasHeight - padding - halfHeight),
    };
  };

  const evaluateCandidateSpacing = (x, y, radius) => {
    if (!placed.length) {
      return Number.POSITIVE_INFINITY;
    }
    let best = Number.POSITIVE_INFINITY;
    for (let i = 0; i < placed.length; i += 1) {
      const item = placed[i];
      const dx = item.x - x;
      const dy = item.y - y;
      const distance = Math.sqrt(dx * dx + dy * dy) - item.radius - radius - spacingBuffer;
      if (distance < best) {
        best = distance;
      }
    }
    return best;
  };

  entries.forEach((entry, index) => {
    const width = Number(entry.width) || 0;
    const height = Number(entry.height) || 0;
    if (!entry.id || width <= 0 || height <= 0) {
      return;
    }

    const { minCenterX, maxCenterX, minCenterY, maxCenterY } = resolveBounds(width, height);

    const radius = Math.sqrt(width * width + height * height) / 2;

    let bestScore = -Infinity;
    let bestX = (minCenterX + maxCenterX) / 2;
    let bestY = (minCenterY + maxCenterY) / 2;
    const samplesPerAxis = 14;
    for (let gx = 0; gx < samplesPerAxis; gx += 1) {
      const fracX = (gx + 0.5) / samplesPerAxis;
      for (let gy = 0; gy < samplesPerAxis; gy += 1) {
        const fracY = (gy + 0.5) / samplesPerAxis;
        const candidateX = minCenterX + fracX * (maxCenterX - minCenterX);
        const candidateY = minCenterY + fracY * (maxCenterY - minCenterY);
        const edgeSpacing = Math.min(
          candidateX - minCenterX,
          maxCenterX - candidateX,
          candidateY - minCenterY,
          maxCenterY - candidateY,
        ) - spacingBuffer * 0.5;
        if (edgeSpacing <= 0) {
          continue;
        }
        const neighborSpacing = evaluateCandidateSpacing(candidateX, candidateY, radius);
        const score = Math.min(edgeSpacing, neighborSpacing);
        if (score > bestScore) {
          bestScore = score;
          bestX = candidateX;
          bestY = candidateY;
        }
      }
    }
    const centerX = clamp(bestX, minCenterX, maxCenterX);
    const centerY = clamp(bestY, minCenterY, maxCenterY);

    const rotation = randomRangeFromSeed(
      buildKey(entry.id, 'rotation'),
      -rotationRange,
      rotationRange,
    );

    currentZ += 1;
    layout.set(entry.id, {
      centerX,
      centerY,
      rotation,
      z: currentZ,
    });
    maxZ = Math.max(maxZ, currentZ);

    placed.push({ x: centerX, y: centerY, radius });
  });

  return { layout, maxZ };
};

const createDragPreview = (node, clientX, clientY) => {
  if (!(node instanceof HTMLElement)) {
    return null;
  }
  const rect = node.getBoundingClientRect();
  const safeClientX = Number.isFinite(clientX) ? clientX : rect.left + rect.width / 2;
  const safeClientY = Number.isFinite(clientY) ? clientY : rect.top + rect.height / 2;
  const offsetX = clamp(safeClientX - rect.left, 0, rect.width);
  const offsetY = clamp(safeClientY - rect.top, 0, rect.height);
  const clone = node.cloneNode(true);
  clone.style.position = 'absolute';
  clone.style.top = '-9999px';
  clone.style.left = '-9999px';
  clone.style.pointerEvents = 'none';
  clone.style.opacity = '1';
  clone.style.transform = 'none';
  document.body.appendChild(clone);
  return { clone, offsetX, offsetY };
};

const cleanupPreview = (previewNode) => {
  if (previewNode && previewNode.parentNode) {
    previewNode.parentNode.removeChild(previewNode);
  }
};

const useDocumentDrag = ({
  layoutRef,
  itemRefs,
  documentLookup,
  ensureDocumentSize,
  resolveBaseMetrics,
  bringToFront,
  setDraggingId,
  syncLayoutSnapshot,
  canvasSize,
  openOverlayForDoc,
}) => {
  const dragStateRef = useRef(null);

  const finishDrag = useCallback(
    (pointerId) => {
      const state = dragStateRef.current;
      if (!state || state.pointerId !== pointerId) {
        return;
      }
      const capturedTarget = state.capturedTarget;
      if (capturedTarget && typeof capturedTarget.releasePointerCapture === 'function') {
        try {
          capturedTarget.releasePointerCapture(pointerId);
        } catch (
          // eslint-disable-next-line no-empty
          error
        ) {}
      }
      dragStateRef.current = null;
      setDraggingId((current) => (current === state.docId ? null : current));
      syncLayoutSnapshot();
    },
    [setDraggingId, syncLayoutSnapshot],
  );

  const handlePointerDown = useCallback(
    (event, docId) => {
      if (DEBUG_DRAG) {
        console.log(
          '[skeuo] handlePointerDown fired for doc',
          docId,
          'button',
          event.button,
          'pointerType',
          event.pointerType,
          'pointerId',
          event.pointerId,
        );
      }
      event.preventDefault();
      const entry = layoutRef.current.get(docId) || null;
      const doc = documentLookup.get(docId) || null;
      const { width: docWidth, height: docHeight } = ensureDocumentSize(doc);
      const { baseScale } = resolveBaseMetrics(doc, docWidth, docHeight);
      const defaultCenterX = CANVAS_PADDING + docWidth / 2;
      const defaultCenterY = CANVAS_PADDING + docHeight / 2;
      const centerX = typeof entry?.centerX === 'number' ? entry.centerX : defaultCenterX;
      const centerY = typeof entry?.centerY === 'number' ? entry.centerY : defaultCenterY;

      if (entry && (entry.centerX !== centerX || entry.centerY !== centerY)) {
        layoutRef.current.set(docId, { ...entry, centerX, centerY });
      }

      bringToFront(docId);
      const capturedTarget = event.currentTarget instanceof HTMLElement ? event.currentTarget : null;
      if (capturedTarget && typeof capturedTarget.setPointerCapture === 'function') {
        try {
          capturedTarget.setPointerCapture(event.pointerId);
        } catch (
          // eslint-disable-next-line no-empty
          error
        ) {}
      }
      dragStateRef.current = {
        docId,
        pointerId: event.pointerId,
        originCenterX: centerX,
        originCenterY: centerY,
        startX: event.clientX,
        startY: event.clientY,
        rotation: entry?.rotation ?? 0,
        moved: false,
        locked: false,
        width: docWidth,
        height: docHeight,
        scale: baseScale,
        capturedTarget,
      };
      setDraggingId(docId);
    },
    [bringToFront, documentLookup, ensureDocumentSize, layoutRef, resolveBaseMetrics, setDraggingId],
  );

  const handlePointerMove = useCallback(
    (event) => {
      const state = dragStateRef.current;
      if (!state) {
        if (DEBUG_DRAG) {
          console.log('[skeuo] handlePointerMove: no drag state for pointer', event.pointerId);
        }
        return;
      }
      if (state.pointerId !== event.pointerId) {
        if (DEBUG_DRAG) {
          console.log(
            '[skeuo] handlePointerMove: pointer mismatch expected',
            state.pointerId,
            'got',
            event.pointerId,
          );
        }
        return;
      }
      event.preventDefault();
      if (state.locked) {
        if (DEBUG_DRAG) {
          console.log('[skeuo] handlePointerMove: locked drag for doc', state.docId);
        }
        return;
      }

      const entry = layoutRef.current.get(state.docId);
      if (!entry) {
        return;
      }

      const deltaX = event.clientX - state.startX;
      const deltaY = event.clientY - state.startY;
      const nextCenterX = state.originCenterX + deltaX;
      const nextCenterY = state.originCenterY + deltaY;

      const docWidth = state.width;
      const docHeight = state.height;
      const halfWidth = docWidth / 2;
      const halfHeight = docHeight / 2;
      const canvasWidth = canvasSize.width || DEFAULT_CANVAS_WIDTH;
      const canvasHeight = canvasSize.height || DEFAULT_CANVAS_HEIGHT;
      const minCenterX = CANVAS_PADDING + halfWidth;
      const maxCenterX = Math.max(minCenterX, canvasWidth - CANVAS_PADDING - halfWidth);
      const minCenterY = CANVAS_PADDING + halfHeight;
      const maxCenterY = Math.max(minCenterY, canvasHeight - CANVAS_PADDING - halfHeight);
      const clampedCenterX = clamp(nextCenterX, minCenterX, maxCenterX);
      const clampedCenterY = clamp(nextCenterY, minCenterY, maxCenterY);

      const prevCenterX = typeof entry.centerX === 'number' ? entry.centerX : state.originCenterX;
      const prevCenterY = typeof entry.centerY === 'number' ? entry.centerY : state.originCenterY;
      if (Math.abs(clampedCenterX - prevCenterX) < 0.5 && Math.abs(clampedCenterY - prevCenterY) < 0.5) {
        if (DEBUG_DRAG) {
          console.log('[skeuo] handlePointerMove: movement under threshold for doc', state.docId);
        }
        return;
      }

      const updated = { ...entry, centerX: clampedCenterX, centerY: clampedCenterY };
      layoutRef.current.set(state.docId, updated);

      const node = itemRefs.current.get(state.docId);
      if (node) {
        node.style.transform = formatTransform(
          clampedCenterX,
          clampedCenterY,
          state.rotation,
          state.scale || 1,
        );
      }
      state.moved = true;
      if (DEBUG_DRAG) {
        console.log('[skeuo] handlePointerMove: moved doc', state.docId, 'to', clampedCenterX, clampedCenterY);
      }
    },
    [canvasSize.height, canvasSize.width, itemRefs, layoutRef],
  );

  const handlePointerUp = useCallback(
    (event) => {
      const state = dragStateRef.current;
      if (state && state.pointerId === event.pointerId) {
        const moved = Boolean(state.moved);
        const docId = state.docId;
        finishDrag(event.pointerId);
        if (!moved) {
          const originInfo = {
            rotation: state.rotation || 0,
            scale: state.scale || 1,
            width: state.width,
            height: state.height,
          };
          openOverlayForDoc(docId, originInfo);
        }
        return;
      }
      finishDrag(event.pointerId);
    },
    [finishDrag, openOverlayForDoc],
  );

  const handlePointerCancel = useCallback(
    (event) => {
      finishDrag(event.pointerId);
    },
    [finishDrag],
  );

  return {
    handlePointerDown,
    handlePointerMove,
    handlePointerUp,
    handlePointerCancel,
  };
};

const seededRandom = (input) => {
  const text = String(input);
  let hash = 2166136261;
  for (let index = 0; index < text.length; index += 1) {
    hash ^= text.charCodeAt(index);
    hash = Math.imul(hash, 16777619);
  }
  return (hash >>> 0) / 4294967295;
};

const randomRangeFromSeed = (seedKey, min, max) => {
  const span = max - min;
  if (span <= 0) return min;
  const seed = seededRandom(seedKey);
  return min + seed * span;
};

const buildKey = (docId, suffix) => `${docId}::${suffix}`;

const clamp = (value, min, max) => {
  if (value < min) return min;
  if (value > max) return max;
  return value;
};

const clampCardDimensions = (width, height) => {
  let nextWidth = Number(width);
  let nextHeight = Number(height);
  if (!Number.isFinite(nextWidth) || nextWidth <= 0 || !Number.isFinite(nextHeight) || nextHeight <= 0) {
    return null;
  }

  const scaleDown = Math.min(1, CARD_MAX / nextWidth, CARD_MAX / nextHeight);
  nextWidth *= scaleDown;
  nextHeight *= scaleDown;

  const minDim = Math.min(nextWidth, nextHeight);
  if (minDim > 0 && minDim < CARD_MIN) {
    const scaleUp = CARD_MIN / minDim;
    nextWidth *= scaleUp;
    nextHeight *= scaleUp;

    const adjust = Math.min(1, CARD_MAX / nextWidth, CARD_MAX / nextHeight);
    nextWidth *= adjust;
    nextHeight *= adjust;
  }

  nextWidth = clamp(nextWidth, CARD_MIN, CARD_MAX);
  nextHeight = clamp(nextHeight, CARD_MIN, CARD_MAX);

  return {
    width: nextWidth,
    height: nextHeight,
  };
};

const formatTransform = (x, y, rotation = 0, scale = 1) =>
  `translate3d(${x}px, ${y}px, 0) rotate(${rotation}deg) scale(${scale})`;

const SkeuomorphicWorkspace = ({
  documents = [],
  searchResults = null,
  onDocumentOpen,
  resolveThumbnailUrl,
  onAssignTagToDocument = null,
  onRemoveTagFromDocument = null,
  ensureAssetUrl = null,
  getDocumentAsset = () => null,
  activeTagIds = [],
}) => {
  const items = useMemo(() => (searchResults ? searchResults : documents), [documents, searchResults]);


  const containerRef = useRef(null);
  const layoutRef = useRef(new Map());
  const itemRefs = useRef(new Map());
  const zCounterRef = useRef(10);

  const [layoutSnapshot, setLayoutSnapshot] = useState(() => new Map());

  const [canvasSize, setCanvasSize] = useState({ width: 0, height: 0 });
  const [draggingId, setDraggingId] = useState(null);
  const [overlayDocId, setOverlayDocId] = useState(null);
  const [overlayOriginRect, setOverlayOriginRect] = useState(null);
  const [overlayOriginTransform, setOverlayOriginTransform] = useState(null);
  const [previewSnapshots, setPreviewSnapshots] = useState(() => new Map());
  const [docSizeVersion, setDocSizeVersion] = useState(0);
  const [tagDropTargetId, setTagDropTargetId] = useState(null);
  const [pendingTagDocId, setPendingTagDocId] = useState(null);
  const [pendingRemovalTag, setPendingRemovalTag] = useState(null);
  const draggingTagRef = useRef(null);
  const pendingDocTagDragRef = useRef(null);
  const docSizeMapRef = useRef(new Map());
  const documentLookupRef = useRef(new Map());
  const removalCursorActiveRef = useRef(false);
  const applySnapshotDimensions = useCallback(
    (docId, snapshot) => {
      if (!docId || !snapshot) {
        return;
      }

      const doc = documentLookupRef.current.get(docId);
      if (!doc) {
        return;
      }

      const width = Number(snapshot.width);
      const height = Number(snapshot.height);
      if (!Number.isFinite(width) || width <= 0 || !Number.isFinite(height) || height <= 0) {
        return;
      }

      const normalized = clampCardDimensions(width, height);
      if (!normalized) {
        return;
      }

      const key = resolveSizeKey(doc);
      const existing = docSizeMapRef.current.get(key);
      if (existing && existing.width === normalized.width && existing.height === normalized.height) {
        return;
      }

      docSizeMapRef.current.set(key, normalized);
      setDocSizeVersion((value) => value + 1);
    },
    [],
  );
  const handleNavigatorSnapshot = useCallback(
    (docId, snapshot) => {
      if (!docId) {
        return;
      }

      setPreviewSnapshots((previous) => {
        const prevSnapshot = previous.get(docId);
        if (!snapshot) {
          if (!previous.has(docId)) {
            return previous;
          }
          const next = new Map(previous);
          next.delete(docId);
          return next;
        }

        const next = new Map(previous);
        const sameSnapshot =
          prevSnapshot &&
          prevSnapshot.url === snapshot.url &&
          prevSnapshot.alt === snapshot.alt &&
          prevSnapshot.canGoPrev === snapshot.canGoPrev &&
          prevSnapshot.canGoNext === snapshot.canGoNext &&
          prevSnapshot.goPrev === snapshot.goPrev &&
          prevSnapshot.goNext === snapshot.goNext &&
          prevSnapshot.ordinal === snapshot.ordinal &&
          prevSnapshot.width === snapshot.width &&
          prevSnapshot.height === snapshot.height;
        if (sameSnapshot) {
          return previous;
        }
        next.set(docId, snapshot);
        return next;
      });

      if (snapshot) {
        applySnapshotDimensions(docId, snapshot);
      }
    },
    [applySnapshotDimensions],
  );
  const activeTagSet = useMemo(() => {
    if (!Array.isArray(activeTagIds) || activeTagIds.length === 0) {
      return new Set();
    }
    const set = new Set();
    activeTagIds.forEach((id) => {
      if (id != null) {
        set.add(String(id));
      }
    });
    return set;
  }, [activeTagIds]);

  const resolvePreviewAsset = useCallback(
    (doc) => {
      if (!doc) return null;
      return getDocumentAsset(doc, 'preview');
    },
    [getDocumentAsset],
  );

  const resolvePreviewDimensions = useCallback(
    (doc) => {
      if (!doc) return null;

      const snapshot = previewSnapshots.get(doc.id);
      if (snapshot && snapshot.width && snapshot.height) {
        return {
          width: snapshot.width,
          height: snapshot.height,
        };
      }

      const asset = resolvePreviewAsset(doc);
      const view = createAssetView(asset);
      const metadata = view.getPrimaryMetadata() || {};
      const width = metadata?.width;
      const height = metadata?.height;
      if (typeof width === 'number' && typeof height === 'number') {
        return { width, height };
      }
      return null;
    },
    [previewSnapshots, resolvePreviewAsset],
  );

  useEffect(() => {
    if (!ensureAssetUrl) {
      return;
    }

    items.forEach((doc) => {
      resolveDocumentAssetUrl(doc, 'preview', {
        ensureAssetUrl,
        getAsset: getDocumentAsset,
      });
    });
  }, [items, ensureAssetUrl, getDocumentAsset]);

  const focusCanvas = useCallback(() => {
    const canvas = containerRef.current;
    if (canvas && typeof canvas.focus === 'function') {
      const focusTarget = () => {
        try {
          if (DEBUG_FOCUS) {
            console.log('[skeuo] focusCanvas -> attempting focus', canvas);
          }
          canvas.focus({ preventScroll: true });
          if (DEBUG_FOCUS) {
            console.log('[skeuo] focusCanvas: applied focus. activeElement:', document?.activeElement);
          }
        } catch (
          // eslint-disable-next-line no-empty
          error
        ) {}
      };
      if (typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function') {
        window.requestAnimationFrame(focusTarget);
      } else {
        setTimeout(focusTarget, 0);
      }
      if (DEBUG_FOCUS) {
        console.log('[skeuo] focusCanvas: scheduling focus. activeElement:', document?.activeElement);
      }
    }
  }, []);

  const queueFocusCanvas = useCallback(() => {
    if (typeof window === 'undefined') {
      focusCanvas();
      return;
    }
  if (DEBUG_FOCUS) {
    console.log('[skeuo] queueFocusCanvas -> scheduling deferred focus');
  }
  setTimeout(() => {
    if (DEBUG_FOCUS) {
      console.log('[skeuo] queueFocusCanvas -> executing deferred focus');
    }
      focusCanvas();
    }, 0);
  }, [focusCanvas]);

  const updateRemovalCursor = useCallback((active) => {
    if (typeof document === 'undefined') {
      return;
    }
    if (removalCursorActiveRef.current === active) {
      return;
    }
    const body = document.body;
    if (!body) {
      return;
    }
    removalCursorActiveRef.current = active;
    if (active) {
      body.classList.add('skeuo-cursor-remove');
    } else {
      body.classList.remove('skeuo-cursor-remove');
    }
  }, []);

  useEffect(
    () => () => {
      updateRemovalCursor(false);
    },
    [updateRemovalCursor],
  );

  const isTagTransfer = useCallback((event) => {
    const types = event.dataTransfer?.types;
    if (!types) return false;
    return TAG_MIME_TYPES.some((type) =>
      typeof types.includes === 'function'
        ? types.includes(type)
        : Array.from(types).includes(type),
    );
  }, []);

  const handleTagDragEnd = useCallback(() => {
    updateRemovalCursor(false);
    setTagDropTargetId(null);
  }, [updateRemovalCursor]);

  const ensureDocumentSize = useCallback((doc) => {
    const key = resolveSizeKey(doc);
    const cache = docSizeMapRef.current.get(key);
    if (cache) {
      return cache;
    }
    const intrinsic = resolvePreviewDimensions(doc);
    let normalized = null;
    if (intrinsic?.width && intrinsic?.height) {
      normalized = clampCardDimensions(intrinsic.width, intrinsic.height);
    }

    if (!normalized) {
      const seed = seededRandom(`${key}:size`);
      let width = CARD_MIN + seed * (Math.min(CARD_MAX, CARD_MAX / EMPTY_CARD_ASPECT) - CARD_MIN);
      width = clamp(width, CARD_MIN, Math.min(CARD_MAX, CARD_MAX / EMPTY_CARD_ASPECT));
      const height = width * EMPTY_CARD_ASPECT;
      normalized = clampCardDimensions(width, height);
    }

    if (!normalized) {
      normalized = { width: CARD_MIN, height: CARD_MIN };
    }

    docSizeMapRef.current.set(key, normalized);
    return normalized;
  }, [resolvePreviewDimensions]);

  const documentLookup = useMemo(() => {
    const map = new Map();
    items.forEach((doc) => {
      if (doc?.id) {
        map.set(doc.id, doc);
      }
    });
    return map;
  }, [items]);

  useEffect(() => {
    documentLookupRef.current = documentLookup;
  }, [documentLookup]);

  useEffect(() => {
    docSizeMapRef.current = new Map();
    setDocSizeVersion((value) => value + 1);
  }, [items]);

  const overlayDisplay = useMemo(() => {
    if (!overlayDocId) {
      return null;
    }
    const snapshot = previewSnapshots.get(overlayDocId);
    if (!snapshot || !snapshot.url) {
      return null;
    }
    const doc = documentLookup.get(overlayDocId);
    const alt = snapshot.alt || doc?.title || doc?.original_name || 'Document preview';
    return {
      url: snapshot.url,
      alt,
      canGoPrev: snapshot.canGoPrev,
      canGoNext: snapshot.canGoNext,
      goPrev: snapshot.goPrev,
      goNext: snapshot.goNext,
    };
  }, [overlayDocId, previewSnapshots, documentLookup]);

  const closeOverlay = useCallback(() => {
    setOverlayDocId(null);
    setOverlayOriginRect(null);
    setOverlayOriginTransform(null);
  }, []);

  useEffect(() => {
    if (overlayDocId && !documentLookup.has(overlayDocId)) {
      setOverlayDocId(null);
      setOverlayOriginRect(null);
      setOverlayOriginTransform(null);
    }
  }, [overlayDocId, documentLookup]);

  const resolveBaseMetrics = useCallback(
    (doc, cardWidth, cardHeight) => {
      const previewDims = doc ? resolvePreviewDimensions(doc) : null;
      if (previewDims?.width && previewDims?.height) {
        const baseWidth = Math.max(previewDims.width, cardWidth);
        const baseHeight = Math.max(previewDims.height, cardHeight);
        const scaleX = cardWidth / baseWidth;
        const scaleY = cardHeight / baseHeight;
        const baseScale = Math.min(scaleX, scaleY, 1);
        return {
          baseWidth,
          baseHeight,
          baseScale: Number.isFinite(baseScale) && baseScale > 0 ? baseScale : 1,
        };
      }
      return {
        baseWidth: cardWidth,
        baseHeight: cardHeight,
        baseScale: 1,
      };
    },
    [resolvePreviewDimensions],
  );

  const syncLayoutSnapshot = useCallback(() => {
    setLayoutSnapshot(new Map(layoutRef.current));
  }, []);

  useLayoutEffect(() => {
    const container = containerRef.current;
    if (!container) return () => {};

    if (process.env.NODE_ENV !== 'production') {
      console.log('[skeuo] canvas element', container);
    }

    const commitSize = () => {
      const rect = container.getBoundingClientRect();
      const width = Math.floor(rect.width) || 0;
      const height = Math.floor(rect.height) || 0;
      setCanvasSize((prev) => {
        if (prev.width === width && prev.height === height) {
          return prev;
        }
        return { width, height };
      });
    };

    commitSize();

    if (typeof ResizeObserver === 'undefined') {
      window.addEventListener('resize', commitSize);
      return () => {
        window.removeEventListener('resize', commitSize);
      };
    }

    const observer = new ResizeObserver(() => {
      commitSize();
    });

    observer.observe(container);
    return () => observer.disconnect();
  }, []);
  useLayoutEffect(() => {
    const container = containerRef.current;
    if (!container || !canvasSize.width || !canvasSize.height) {
      return () => {};
    }

    const rafId = requestAnimationFrame(() => {
      if (!items.length) {
        layoutRef.current = new Map();
        syncLayoutSnapshot();
        return;
      }

      const previous = layoutRef.current;
      const next = new Map();
      let maxZ = zCounterRef.current;
      const canvasWidth = canvasSize.width || DEFAULT_CANVAS_WIDTH;
      const canvasHeight = canvasSize.height || DEFAULT_CANVAS_HEIGHT;
      const docsNeedingLayout = [];

      items.forEach((doc) => {
        const { width: docWidth, height: docHeight } = ensureDocumentSize(doc);
        const halfWidth = docWidth / 2;
        const halfHeight = docHeight / 2;

        const minCenterX = CANVAS_PADDING + halfWidth;
        const maxCenterX = Math.max(minCenterX, canvasWidth - CANVAS_PADDING - halfWidth);
        const minCenterY = CANVAS_PADDING + halfHeight;
        const maxCenterY = Math.max(minCenterY, canvasHeight - CANVAS_PADDING - halfHeight);

        const existing = previous.get(doc.id);
        if (existing) {
          const defaultCenterX = (minCenterX + maxCenterX) / 2;
          const defaultCenterY = (minCenterY + maxCenterY) / 2;
          const prevCenterX =
            typeof existing.centerX === 'number' ? existing.centerX : defaultCenterX;
          const prevCenterY =
            typeof existing.centerY === 'number' ? existing.centerY : defaultCenterY;
          const centerX = clamp(prevCenterX, minCenterX, maxCenterX);
          const centerY = clamp(prevCenterY, minCenterY, maxCenterY);
          const rotation = existing.rotation ?? 0;
          const z = existing.z ?? maxZ;
          maxZ = Math.max(maxZ, z);
          next.set(doc.id, { centerX, centerY, rotation, z });
          return;
        }

        docsNeedingLayout.push({
          id: doc.id,
          width: docWidth,
          height: docHeight,
          seedKey: resolveSizeKey(doc),
        });
      });

      if (docsNeedingLayout.length) {
        const { layout: generatedLayout, maxZ: updatedMaxZ } = generateInitialLayout(
          docsNeedingLayout,
          {
            canvasWidth,
            canvasHeight,
            padding: CANVAS_PADDING,
            startZ: maxZ,
            rotationRange: ROTATION_RANGE,
            minSpacing: 48,
            shelfWidth: 0,
          },
        );
        generatedLayout.forEach((entry, docId) => {
          next.set(docId, entry);
        });
        maxZ = Math.max(maxZ, updatedMaxZ);
      }

      layoutRef.current = next;
      zCounterRef.current = Math.max(zCounterRef.current, maxZ);
      syncLayoutSnapshot();
    });

    return () => cancelAnimationFrame(rafId);
  }, [
    items,
    canvasSize.width,
    canvasSize.height,
    docSizeVersion,
    ensureDocumentSize,
    syncLayoutSnapshot,
  ]);

  useEffect(() => {
    if (draggingId && !items.some((doc) => doc.id === draggingId)) {
      setDraggingId(null);
    }
  }, [draggingId, items]);

  const bringToFront = useCallback(
    (docId) => {
      zCounterRef.current += 1;
      const entry = layoutRef.current.get(docId);
      if (!entry) return;
      const updated = { ...entry, z: zCounterRef.current };
      layoutRef.current.set(docId, updated);
      syncLayoutSnapshot();
    },
    [syncLayoutSnapshot],
  );

  const openOverlayForDoc = useCallback(
    (docId, originInfo = null) => {
      if (!docId) {
        return;
      }
      const snapshot = previewSnapshots.get(docId);
      if (!snapshot || !snapshot.url) {
        return;
      }
      const container = itemRefs.current.get(docId);
      const imageNode = container?.querySelector?.('.skeuo-item__card img');
      if (!container || !imageNode) {
        return;
      }
      const rect = imageNode.getBoundingClientRect();
      let originTransform = null;
      if (originInfo) {
        const { rotation = 0, scale = 1, width: originWidth, height: originHeight } = originInfo;
        originTransform = {
          rotation,
          scaleX: scale,
          scaleY: scale,
          baseWidth: originWidth,
          baseHeight: originHeight,
        };
      }
      if (!originTransform) {
        const entry = layoutRef.current.get(docId) || null;
        const doc = documentLookup.get(docId) || null;
        const { width: cardWidth, height: cardHeight } = ensureDocumentSize(doc);
        const { baseWidth, baseHeight, baseScale } = resolveBaseMetrics(doc, cardWidth, cardHeight);
        const effectiveWidth = baseWidth * baseScale;
        const effectiveHeight = baseHeight * baseScale;
        originTransform = {
          rotation: entry?.rotation ?? 0,
          scaleX: baseScale,
          scaleY: baseScale,
          baseWidth: Number.isFinite(effectiveWidth) && effectiveWidth > 0 ? effectiveWidth : cardWidth,
          baseHeight: Number.isFinite(effectiveHeight) && effectiveHeight > 0 ? effectiveHeight : cardHeight,
        };
      }
      bringToFront(docId);
      setOverlayOriginRect(rect);
      setOverlayOriginTransform(originTransform);
      setOverlayDocId(docId);
    },
    [
      bringToFront,
      previewSnapshots,
      itemRefs,
      setOverlayOriginTransform,
      ensureDocumentSize,
      resolveBaseMetrics,
      documentLookup,
      layoutRef,
    ],
  );
  const { handlePointerDown, handlePointerMove, handlePointerUp, handlePointerCancel } = useDocumentDrag({
    layoutRef,
    itemRefs,
    documentLookup,
    ensureDocumentSize,
    resolveBaseMetrics,
    bringToFront,
    setDraggingId,
    syncLayoutSnapshot,
    canvasSize,
    openOverlayForDoc,
  });

  const handleTagDragEnterDoc = useCallback(
    (event, docId) => {
      if (!isTagTransfer(event)) {
        return;
      }
      event.preventDefault();
      event.stopPropagation();
      updateRemovalCursor(false);
      if (tagDropTargetId !== docId) {
        setTagDropTargetId(docId);
      }
    },
    [isTagTransfer, tagDropTargetId, updateRemovalCursor],
  );

  const handleTagDragOverDoc = useCallback(
    (event, docId) => {
      if (!isTagTransfer(event)) {
        return;
      }
      event.preventDefault();
      event.stopPropagation();
      updateRemovalCursor(false);
      const activeDrag = draggingTagRef.current;
      event.dataTransfer.dropEffect = activeDrag?.sourceDocId ? 'move' : 'copy';
      if (tagDropTargetId !== docId) {
        setTagDropTargetId(docId);
      }
    },
    [isTagTransfer, tagDropTargetId, updateRemovalCursor],
  );

  const handleTagDragLeaveDoc = useCallback((event, docId) => {
    if (!isTagTransfer(event)) {
      return;
    }
    event.preventDefault();
    event.stopPropagation();
    if (
      event.currentTarget instanceof HTMLElement &&
      event.relatedTarget instanceof Node &&
      event.currentTarget.contains(event.relatedTarget)
    ) {
      return;
    }
    setTagDropTargetId((current) => (current === docId ? null : current));
    updateRemovalCursor(false);
  }, [isTagTransfer, updateRemovalCursor]);

  const markActiveTagDropHandled = useCallback((tagId, sourceDocId = null) => {
    const state = draggingTagRef.current;
    if (!state) {
      return;
    }
    if (state.tagId !== tagId) {
      return;
    }
    if (sourceDocId && state.sourceDocId !== sourceDocId) {
      return;
    }
    state.dropHandled = true;
  }, []);

  const handleTagDropOnDoc = useCallback(
    async (event, doc) => {
      if (!doc || !isTagTransfer(event) || typeof onAssignTagToDocument !== 'function') {
        if (DEBUG_DROP) {
          console.log('[skeuo] handleTagDropOnDoc: drop ignored', { doc, hasTransfer: isTagTransfer(event) });
        }
        return;
      }
      event.preventDefault();
      event.stopPropagation();
      if (DEBUG_DROP) {
        console.log('[skeuo] handleTagDropOnDoc: drop accepted for doc', doc.id, 'event', event);
      }
      setTagDropTargetId(null);

      let payload = null;
      try {
        const raw =
          event.dataTransfer.getData('application/x-papercrate-tag') ||
          event.dataTransfer.getData('text/papercrate-tag');
        payload = raw ? JSON.parse(raw) : null;
      } catch (error) {
        if (DEBUG_DROP) {
          console.warn('[skeuo] handleTagDropOnDoc: failed to parse payload', error);
        }
        payload = null;
      }

      if (!payload?.id) {
        if (DEBUG_DROP) {
          console.log('[skeuo] handleTagDropOnDoc: missing tag id payload', payload);
        }
        queueFocusCanvas();
        return;
      }

      const tagId = payload.id;
      const sourceDocId = payload.sourceDocId || null;
      if (DEBUG_DROP) {
        console.log('[skeuo] handleTagDropOnDoc: parsed payload', { tagId, sourceDocId });
      }

      if (sourceDocId && sourceDocId === doc.id) {
        markActiveTagDropHandled(tagId, sourceDocId);
        if (DEBUG_DROP) {
          console.log('[skeuo] handleTagDropOnDoc: drop from same doc ignored', tagId);
        }
        queueFocusCanvas();
        return;
      }

      const alreadyAssigned = Array.isArray(doc.tags)
        ? doc.tags.some((tag) => tag.id === tagId)
        : false;
      if (alreadyAssigned) {
        markActiveTagDropHandled(tagId, sourceDocId);
        if (DEBUG_DROP) {
          console.log('[skeuo] handleTagDropOnDoc: tag already assigned', tagId);
        }
        queueFocusCanvas();
        return;
      }

      const movingBetweenDocuments = Boolean(sourceDocId && sourceDocId !== doc.id);
      if (DEBUG_DROP) {
        console.log('[skeuo] handleTagDropOnDoc: movingBetweenDocuments', movingBetweenDocuments);
      }

      setPendingTagDocId(doc.id);
      try {
        await onAssignTagToDocument({ documentId: doc.id, tagId, tag: payload });
        markActiveTagDropHandled(tagId, sourceDocId);
        if (DEBUG_DROP) {
          console.log('[skeuo] handleTagDropOnDoc: assigned tag', tagId, 'to doc', doc.id);
        }
        if (movingBetweenDocuments && typeof onRemoveTagFromDocument === 'function') {
          setPendingRemovalTag({ docId: sourceDocId, tagId });
          try {
            await onRemoveTagFromDocument(sourceDocId, tagId);
            if (DEBUG_DROP) {
              console.log('[skeuo] handleTagDropOnDoc: removed tag from source doc', sourceDocId);
            }
          } finally {
            setPendingRemovalTag(null);
          }
        }
      } finally {
        setPendingTagDocId(null);
        if (DEBUG_DROP) {
          console.log('[skeuo] handleTagDropOnDoc: finalizing drop for tag', tagId);
        }
        queueFocusCanvas();
      }
    },
    [
      isTagTransfer,
      markActiveTagDropHandled,
      onAssignTagToDocument,
      onRemoveTagFromDocument,
      queueFocusCanvas,
    ],
  );

  const handleCanvasDragOver = useCallback(
    (event) => {
      if (!isTagTransfer(event)) {
        return;
      }
      event.preventDefault();
      event.stopPropagation();
      event.dataTransfer.dropEffect = 'move';
      updateRemovalCursor(true);
      setTagDropTargetId(null);
    },
    [isTagTransfer, updateRemovalCursor],
  );

  const handleCanvasDragLeave = useCallback(
    (event) => {
      if (!isTagTransfer(event)) {
        return;
      }
      event.preventDefault();
      event.stopPropagation();
      if (
        event.currentTarget instanceof HTMLElement &&
        event.relatedTarget instanceof Node &&
        event.currentTarget.contains(event.relatedTarget)
      ) {
        return;
      }
      updateRemovalCursor(false);
    },
    [isTagTransfer, updateRemovalCursor],
  );

  const handleCanvasDrop = useCallback(
    async (event) => {
      if (!isTagTransfer(event)) {
        return;
      }
      event.preventDefault();
      event.stopPropagation();
      updateRemovalCursor(false);
      queueFocusCanvas();
      setTagDropTargetId(null);

      let payload = null;
      try {
        const raw =
          event.dataTransfer.getData('application/x-papercrate-tag') ||
          event.dataTransfer.getData('text/papercrate-tag');
        payload = raw ? JSON.parse(raw) : null;
      } catch (error) {
        payload = null;
      }

      if (!payload?.id) {
        return;
      }

      const tagId = payload.id;
      const sourceDocId = payload.sourceDocId || null;
      markActiveTagDropHandled(tagId, sourceDocId);

      if (!sourceDocId || typeof onRemoveTagFromDocument !== 'function') {
        return;
      }

      setPendingRemovalTag({ docId: sourceDocId, tagId });
      try {
        await onRemoveTagFromDocument(sourceDocId, tagId);
      } catch (error) {
        console.error('Failed to remove tag via canvas drop', error);
      } finally {
        setPendingRemovalTag(null);
      }
    },
    [
      isTagTransfer,
      markActiveTagDropHandled,
      onRemoveTagFromDocument,
      queueFocusCanvas,
      updateRemovalCursor,
    ],
  );

  const handleDocTagPointerDown = useCallback((event, doc, tag) => {
    event.stopPropagation();
    if (!doc || !tag) {
      pendingDocTagDragRef.current = null;
      return;
    }
    const startX = Number.isFinite(event.clientX)
      ? event.clientX
      : Number.isFinite(event.pageX)
      ? event.pageX
      : 0;
    const startY = Number.isFinite(event.clientY)
      ? event.clientY
      : Number.isFinite(event.pageY)
      ? event.pageY
      : 0;
    pendingDocTagDragRef.current = {
      docId: doc.id,
      tagId: tag.id,
      startX,
      startY,
    };
    updateRemovalCursor(false);
  }, [updateRemovalCursor]);

  const handleDocTagDragStart = useCallback(
    (event, doc, tag) => {
      if (!doc || !tag) {
        return;
      }
      event.stopPropagation();
      try {
        if (event.dataTransfer) {
          event.dataTransfer.effectAllowed = 'copyMove';
        }
      } catch (
        // eslint-disable-next-line no-empty
        error
      ) {}

      const payload = JSON.stringify({ id: tag.id, label: tag.label, sourceDocId: doc.id });
      try {
        event.dataTransfer?.setData('application/x-papercrate-tag', payload);
        event.dataTransfer?.setData('text/papercrate-tag', payload);
        event.dataTransfer?.setData('text/plain', tag.label || 'Tag');
      } catch (
        // eslint-disable-next-line no-empty
        error
      ) {}

      const pending = pendingDocTagDragRef.current;
      const node = event.currentTarget;
      const startX = Number.isFinite(event.clientX)
        ? event.clientX
        : Number.isFinite(event.pageX)
        ? event.pageX
        : 0;
      const startY = Number.isFinite(event.clientY)
        ? event.clientY
        : Number.isFinite(event.pageY)
        ? event.pageY
        : 0;
      const initialX =
        pending && pending.docId === doc.id && pending.tagId === tag.id && Number.isFinite(pending.startX)
          ? pending.startX
          : startX;
      const initialY =
        pending && pending.docId === doc.id && pending.tagId === tag.id && Number.isFinite(pending.startY)
          ? pending.startY
          : startY;
      pendingDocTagDragRef.current = null;
      let preview = null;
      if (node instanceof HTMLElement) {
        preview = createDragPreview(node, event.clientX, event.clientY);
        if (preview && event.dataTransfer) {
          try {
            event.dataTransfer.setDragImage(preview.clone, preview.offsetX, preview.offsetY);
          } catch (
            // eslint-disable-next-line no-empty
            error
          ) {}
        }
      }
      draggingTagRef.current = {
        sourceDocId: doc.id,
        tagId: tag.id,
        tagLabel: tag.label || 'Tag',
        startX: initialX,
        startY: initialY,
        distance: 0,
        element: node instanceof HTMLElement ? node : null,
        dropHandled: false,
        hasPosition: Number.isFinite(initialX) && Number.isFinite(initialY),
        previewClone: preview?.clone || null,
      };

      const hideNode = () => {
        if (draggingTagRef.current?.element === node) {
          node.classList.add('is-drag-hidden');
        }
      };
      if (typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function') {
        window.requestAnimationFrame(hideNode);
      } else {
        setTimeout(hideNode, 0);
      }
    },
    [],
  );

  const handleDocTagDrag = useCallback(
    (event) => {
      const state = draggingTagRef.current;
      if (!state) {
        updateRemovalCursor(false);
        return;
      }
      if (!state.hasPosition && Number.isFinite(event.clientX) && Number.isFinite(event.clientY)) {
        state.startX = event.clientX;
        state.startY = event.clientY;
        state.hasPosition = true;
      }
      const clientX = Number.isFinite(event.clientX) ? event.clientX : state.startX;
      const clientY = Number.isFinite(event.clientY) ? event.clientY : state.startY;
      const deltaX = clientX - state.startX;
      const deltaY = clientY - state.startY;
      const distance = Math.hypot(deltaX, deltaY);
      if (Number.isFinite(distance)) {
        state.distance = distance;
      }
      const removalActive =
        Boolean(state.sourceDocId) &&
        !tagDropTargetId &&
        Number.isFinite(state.distance) &&
        state.distance >= TAG_REMOVE_DISTANCE;
      updateRemovalCursor(removalActive);
    },
    [tagDropTargetId, updateRemovalCursor],
  );

  const handleDocTagDragEnd = useCallback(
    (event) => {
      handleTagDragEnd();
      const state = draggingTagRef.current;
      if (!state) {
        return;
      }
      draggingTagRef.current = null;

      const node = state.element;
      const showNode = () => {
        if (node instanceof HTMLElement) {
          node.classList.remove('is-drag-hidden');
        }
      };
      cleanupPreview(state.previewClone);

      const scheduleShowNode = () => {
        if (typeof window !== 'undefined' && typeof window.requestAnimationFrame === 'function') {
          window.requestAnimationFrame(showNode);
        } else {
          setTimeout(showNode, 0);
        }
      };

      const dropEffect = event?.dataTransfer?.dropEffect || 'none';
      console.log('[skeuo] dragEnd dropEffect', dropEffect, 'dropHandled', state.dropHandled);
      const shouldRemove =
        !state.dropHandled &&
        dropEffect === 'none' &&
        state.sourceDocId &&
        typeof onRemoveTagFromDocument === 'function' &&
        (state.distance || 0) >= TAG_REMOVE_DISTANCE;

      if (!shouldRemove) {
        console.log('[skeuo] dragEnd -> no removal. distance:', state.distance);
        scheduleShowNode();
        queueFocusCanvas();
        updateRemovalCursor(false);
        return;
      }

      queueFocusCanvas();
      updateRemovalCursor(false);
      setPendingRemovalTag({ docId: state.sourceDocId, tagId: state.tagId });
      void (async () => {
        try {
          await onRemoveTagFromDocument(state.sourceDocId, state.tagId);
          console.log('[skeuo] dragEnd -> removed tag due to fling');
        } catch (error) {
          console.error('Failed to remove tag after drag', error);
          scheduleShowNode();
        } finally {
          setPendingRemovalTag(null);
        }
      })();
    },
    [queueFocusCanvas, handleTagDragEnd, onRemoveTagFromDocument, updateRemovalCursor],
  );

  return (
    <>
      <div className="skeuo-shell">
        <div
          className="skeuo-canvas"
          ref={containerRef}
          onDragOver={handleCanvasDragOver}
          onDragLeave={handleCanvasDragLeave}
          onDrop={handleCanvasDrop}
        >
        {items.length === 0 ? (
          <div className="skeuo-empty">
            <p>No documents to show here yet. Drop files to make this space come alive.</p>
          </div>
        ) : (
          items.map((doc) => {
            const { width: cardWidth, height: cardHeight } = ensureDocumentSize(doc);
            const { baseWidth, baseHeight, baseScale } = resolveBaseMetrics(doc, cardWidth, cardHeight);
            const fallbackLayout = {
              centerX: CANVAS_PADDING + cardWidth / 2,
              centerY: CANVAS_PADDING + cardHeight / 2,
              rotation: 0,
              z: 1,
            };
            const storedLayout = layoutSnapshot.get(doc.id) ?? layoutRef.current.get(doc.id);
            const layout = storedLayout || fallbackLayout;
            const layoutCenterX =
              typeof layout.centerX === 'number' ? layout.centerX : fallbackLayout.centerX;
            const layoutCenterY =
              typeof layout.centerY === 'number' ? layout.centerY : fallbackLayout.centerY;
            const rotation = layout.rotation || 0;
            const zoomScale = baseScale;
            const transform = formatTransform(
              Math.round(layoutCenterX),
              Math.round(layoutCenterY),
              rotation,
              zoomScale,
            );
            const style = {
              transform,
              zIndex: layout.z ?? 1,
            };
            const bodyStyle = {
              width: Math.round(baseWidth),
              height: Math.round(baseHeight),
            };
            const totalScale = zoomScale > 0 ? zoomScale : 1;
            const inverseTagScale = totalScale > 0 ? 1 / totalScale : 1;
            const tagsStyle = { '--tag-scale': inverseTagScale };
            const title = doc.title || doc.original_name || 'Document';
            const dragging = draggingId === doc.id;
            const tags = Array.isArray(doc.tags) ? doc.tags : [];
            const docTagKeys = tags
              .map((tag) => resolveTagKey(tag))
              .filter(Boolean);
            const matchesFilter =
              activeTagSet.size === 0 || docTagKeys.some((key) => activeTagSet.has(key));
            const dropActive = tagDropTargetId === doc.id;
            const dropPending = pendingTagDocId === doc.id;
            const itemClasses = ['skeuo-item'];
            if (dragging) itemClasses.push('is-dragging');
            if (dropActive) itemClasses.push('is-tag-target');
            if (dropPending) itemClasses.push('is-tag-pending');
            if (!matchesFilter) itemClasses.push('is-filtered-out');
            const docTagTokens = docTagKeys.join(' ');
            return (
              <div
                key={doc.id}
                className={itemClasses.join(' ')}
                style={style}
                role="button"
                data-doc-id={doc.id}
                data-tag-ids={docTagTokens || undefined}
                aria-hidden={matchesFilter ? undefined : 'true'}
                ref={(node) => {
                  if (node) {
                    itemRefs.current.set(doc.id, node);
                  } else {
                    itemRefs.current.delete(doc.id);
                  }
                }}
                onPointerDown={(event) => handlePointerDown(event, doc.id)}
                onPointerMove={handlePointerMove}
                onPointerUp={handlePointerUp}
                onPointerCancel={handlePointerCancel}
                onDragEnter={(event) => handleTagDragEnterDoc(event, doc.id)}
                onDragOver={(event) => handleTagDragOverDoc(event, doc.id)}
                onDragLeave={(event) => handleTagDragLeaveDoc(event, doc.id)}
                onDrop={(event) => handleTagDropOnDoc(event, doc)}
                onKeyDown={(event) => {
                  if (event.key === 'Enter' || event.key === ' ') {
                    event.preventDefault();
                    onDocumentOpen?.(doc.id);
                  }
                }}
              >
                <div className="skeuo-item__body" style={bodyStyle}>
                  <SkeuoPreviewCard
                    doc={doc}
                    title={title}
                    ensureAssetUrl={ensureAssetUrl}
                    getDocumentAsset={getDocumentAsset}
                    navScale={inverseTagScale}
                    onNavigatorSnapshot={handleNavigatorSnapshot}
                  />
                  {tags.length > 0 && (
                    <div className="skeuo-item__tags" aria-hidden="true" style={tagsStyle}>
                      {tags.map((tag) => {
                        const key = tag.id || tag.label || String(tag);
                        if (
                          pendingRemovalTag &&
                          pendingRemovalTag.docId === doc.id &&
                          pendingRemovalTag.tagId === tag.id
                        ) {
                          return null;
                        }
                        const colorStyle = getTagColorStyle(tag.color);
                        const pendingRemoval =
                          pendingRemovalTag &&
                          pendingRemovalTag.docId === doc.id &&
                          pendingRemovalTag.tagId === tag.id;
                        const tagClasses = ['badge', 'tag-chip', 'tag-chip--draggable'];
                        if (pendingRemoval) tagClasses.push('tag-chip--tear-pending');
                        return (
                          <span
                            key={key}
                            className={tagClasses.join(' ')}
                            style={colorStyle || undefined}
                            title={tag.label || 'Tag'}
                            draggable
                            onPointerDown={(event) => handleDocTagPointerDown(event, doc, tag)}
                            onDragStart={(event) => handleDocTagDragStart(event, doc, tag)}
                            onDrag={handleDocTagDrag}
                            onDragEnd={(event) => handleDocTagDragEnd(event)}
                          >
                            <span className="tag-chip__label">{tag.label || 'Tag'}</span>
                          </span>
                        );
                      })}
                    </div>
                  )}
                </div>
              </div>
            );
          })
        )}
        </div>
      </div>
      <PreviewZoomOverlay
        open={Boolean(overlayDisplay?.url)}
        display={overlayDisplay}
        onClose={closeOverlay}
        originRect={overlayOriginRect}
        originTransform={overlayOriginTransform}
      />
    </>
  );
};

export default SkeuomorphicWorkspace;
