import { DEFAULT_NODE_SIZE, DEFAULT_SOURCE_HANDLE_ID, GRID_SPACING, PREVIEW_NODE_ID } from "../constants.js";
import { clamp, getAbsolutePosition, getNonOverlappingPositionForDirection, snapToGrid, snapUpToGrid } from "./NodeUtils.js";
const DEFAULT_CONTAINER_WIDTH = 35 * GRID_SPACING;
const DEFAULT_CONTAINER_HEIGHT = 20 * GRID_SPACING;
const DEFAULT_CONTAINER_MIN_WIDTH = 25 * GRID_SPACING;
const DEFAULT_CONTAINER_MIN_HEIGHT = 14 * GRID_SPACING;
const CONTAINER_FRAME_INSET_PX = 10;
const CONTAINER_BODY_PADDING_PX = 2 * GRID_SPACING;
const CONTAINER_INNER_HANDLE_RAIL_WIDTH_PX = 5 * GRID_SPACING;
const CONTAINER_CHILD_SAFE_GAP_PX = GRID_SPACING;
const DEFAULT_CONTAINER_HEADER_HEIGHT_PX = 40;
const CONTAINER_SEQUENCE_GAP_PX = 3 * GRID_SPACING;
const PLACEMENT_DATA_KEY = 'placement';
function readNumericDimension(...values) {
    for (const value of values){
        if ('number' == typeof value && Number.isFinite(value)) return value;
        if ('string' == typeof value) {
            const parsedValue = Number.parseFloat(value);
            if (Number.isFinite(parsedValue)) return parsedValue;
        }
    }
}
function container_getNodeDimensions(node, fallback = {
    width: DEFAULT_NODE_SIZE,
    height: DEFAULT_NODE_SIZE
}) {
    return {
        width: readNumericDimension(node.width, node.measured?.width, node.style?.width) ?? fallback.width,
        height: readNumericDimension(node.height, node.measured?.height, node.style?.height) ?? fallback.height
    };
}
function container_getContainerSafeArea(containerNode, fallback = {
    width: DEFAULT_CONTAINER_WIDTH,
    height: DEFAULT_CONTAINER_HEIGHT
}) {
    const size = container_getNodeDimensions(containerNode, fallback);
    const horizontalPadding = snapUpToGrid(CONTAINER_FRAME_INSET_PX + CONTAINER_BODY_PADDING_PX + CONTAINER_INNER_HANDLE_RAIL_WIDTH_PX + CONTAINER_CHILD_SAFE_GAP_PX);
    const verticalPadding = snapUpToGrid(CONTAINER_FRAME_INSET_PX + CONTAINER_BODY_PADDING_PX);
    const padding = {
        left: horizontalPadding,
        right: horizontalPadding,
        top: snapUpToGrid(DEFAULT_CONTAINER_HEADER_HEIGHT_PX + verticalPadding),
        bottom: verticalPadding
    };
    return {
        x: padding.left,
        y: padding.top,
        width: Math.max(0, size.width - padding.left - padding.right),
        height: Math.max(0, size.height - padding.top - padding.bottom),
        padding
    };
}
function container_getContainerFitGeometry() {
    const padding = container_getContainerSafeArea({
        width: DEFAULT_CONTAINER_WIDTH,
        height: DEFAULT_CONTAINER_HEIGHT
    }).padding;
    return {
        minWidth: DEFAULT_CONTAINER_WIDTH,
        minHeight: DEFAULT_CONTAINER_HEIGHT,
        padding
    };
}
function resolveResizeMinimum(minSize, currentSize, requiredSize) {
    const requiredMinimum = Math.max(minSize, snapUpToGrid(requiredSize));
    const currentResizeFloor = Math.max(minSize, currentSize);
    return Math.min(currentResizeFloor, requiredMinimum);
}
function getContainerResizeMinimums(containerNode, nodes, { minWidth = DEFAULT_CONTAINER_MIN_WIDTH, minHeight = DEFAULT_CONTAINER_MIN_HEIGHT, ignoredNodeTypes = [] } = {}) {
    const currentSize = container_getNodeDimensions(containerNode, {
        width: DEFAULT_CONTAINER_WIDTH,
        height: DEFAULT_CONTAINER_HEIGHT
    });
    const padding = container_getContainerSafeArea(containerNode, currentSize).padding ?? {};
    const ignoredTypes = new Set(ignoredNodeTypes);
    let childBounds;
    for (const childNode of nodes){
        if (childNode.id === PREVIEW_NODE_ID || childNode.hidden || childNode.parentId !== containerNode.id || ignoredTypes.has(childNode.type ?? '')) continue;
        const childSize = container_getNodeDimensions(childNode);
        const nextBounds = {
            left: childNode.position.x,
            right: childNode.position.x + childSize.width,
            top: childNode.position.y,
            bottom: childNode.position.y + childSize.height
        };
        childBounds = childBounds ? {
            left: Math.min(childBounds.left, nextBounds.left),
            right: Math.max(childBounds.right, nextBounds.right),
            top: Math.min(childBounds.top, nextBounds.top),
            bottom: Math.max(childBounds.bottom, nextBounds.bottom)
        } : nextBounds;
    }
    if (!childBounds) return {
        left: minWidth,
        right: minWidth,
        top: minHeight,
        bottom: minHeight
    };
    const leftEdgeLimit = currentSize.width - childBounds.left + (padding.left ?? 0);
    const topEdgeLimit = currentSize.height - childBounds.top + (padding.top ?? 0);
    const rightEdgeLimit = childBounds.right + (padding.right ?? 0);
    const bottomEdgeLimit = childBounds.bottom + (padding.bottom ?? 0);
    return {
        left: resolveResizeMinimum(minWidth, currentSize.width, leftEdgeLimit),
        right: resolveResizeMinimum(minWidth, currentSize.width, rightEdgeLimit),
        top: resolveResizeMinimum(minHeight, currentSize.height, topEdgeLimit),
        bottom: resolveResizeMinimum(minHeight, currentSize.height, bottomEdgeLimit)
    };
}
function isContainerNodeManifest(manifest) {
    return manifest?.display.shape === 'container';
}
function getAncestorContainerIds(containerId, nodesById) {
    const ancestors = [];
    let parentId = nodesById.get(containerId)?.parentId;
    while(parentId){
        ancestors.push(parentId);
        parentId = nodesById.get(parentId)?.parentId;
    }
    return ancestors;
}
function getContainerDepth(containerId, nodesById) {
    let depth = 0;
    let parentId = nodesById.get(containerId)?.parentId;
    while(parentId){
        depth += 1;
        parentId = nodesById.get(parentId)?.parentId;
    }
    return depth;
}
function withNodeDimensions(node, size) {
    return {
        ...node,
        width: size.width,
        height: size.height,
        style: {
            ...node.style,
            width: size.width,
            height: size.height
        }
    };
}
function ensureContainersFitChildren(nodes, { containerIds, getContainerFitGeometry: resolveContainerFitGeometry, getNodeDimensions: resolveNodeDimensions = container_getNodeDimensions, ignoredNodeTypes = [], includeAncestors = true } = {}) {
    let nextNodes = nodes;
    const changes = [];
    const ignoredTypes = new Set(ignoredNodeTypes);
    const nodesById = new Map(nextNodes.map((node)=>[
            node.id,
            node
        ]));
    const idsToFit = new Set();
    if (containerIds) for (const id of containerIds)idsToFit.add(id);
    else for (const node of nextNodes)if (node.parentId) idsToFit.add(node.parentId);
    if (includeAncestors) for (const id of Array.from(idsToFit))for (const ancestorId of getAncestorContainerIds(id, nodesById))idsToFit.add(ancestorId);
    const orderedContainerIds = Array.from(idsToFit).sort((left, right)=>getContainerDepth(right, nodesById) - getContainerDepth(left, nodesById));
    for (const containerId of orderedContainerIds){
        const containerNode = nextNodes.find((node)=>node.id === containerId);
        if (!containerNode) continue;
        const geometry = resolveContainerFitGeometry?.(containerNode);
        if (!geometry) continue;
        const currentSize = resolveNodeDimensions(containerNode);
        const padding = geometry.padding ?? {};
        let requiredWidth = geometry.minWidth;
        let requiredHeight = geometry.minHeight;
        let leadingShiftX = 0;
        let leadingShiftY = 0;
        const childrenToFit = [];
        const childIdsToShift = new Set();
        for (const childNode of nextNodes){
            if (childNode.id === PREVIEW_NODE_ID || childNode.hidden || childNode.parentId !== containerId || ignoredTypes.has(childNode.type ?? '')) continue;
            const childSize = resolveNodeDimensions(childNode);
            childrenToFit.push({
                node: childNode,
                size: childSize
            });
            childIdsToShift.add(childNode.id);
            if (void 0 !== padding.left) leadingShiftX = Math.max(leadingShiftX, padding.left - childNode.position.x);
            if (void 0 !== padding.top) leadingShiftY = Math.max(leadingShiftY, padding.top - childNode.position.y);
        }
        leadingShiftX = leadingShiftX > 0 ? snapUpToGrid(leadingShiftX) : 0;
        leadingShiftY = leadingShiftY > 0 ? snapUpToGrid(leadingShiftY) : 0;
        for (const { node: childNode, size: childSize } of childrenToFit){
            requiredWidth = Math.max(requiredWidth, childNode.position.x + leadingShiftX + childSize.width + (padding.right ?? 0));
            requiredHeight = Math.max(requiredHeight, childNode.position.y + leadingShiftY + childSize.height + (padding.bottom ?? 0));
        }
        requiredWidth = Math.max(requiredWidth, currentSize.width + leadingShiftX);
        requiredHeight = Math.max(requiredHeight, currentSize.height + leadingShiftY);
        const nextSize = {
            width: Math.max(currentSize.width, snapUpToGrid(requiredWidth)),
            height: Math.max(currentSize.height, snapUpToGrid(requiredHeight))
        };
        if (nextSize.width === currentSize.width && nextSize.height === currentSize.height && 0 === leadingShiftX && 0 === leadingShiftY) continue;
        const shouldShiftChildren = 0 !== leadingShiftX || 0 !== leadingShiftY;
        nextNodes = nextNodes.map((node)=>{
            if (node.id === containerId) {
                const resized = withNodeDimensions(node, nextSize);
                if (!shouldShiftChildren) return resized;
                return {
                    ...resized,
                    position: {
                        x: (node.position?.x ?? 0) - leadingShiftX,
                        y: (node.position?.y ?? 0) - leadingShiftY
                    }
                };
            }
            if (shouldShiftChildren && childIdsToShift.has(node.id)) return {
                ...node,
                position: {
                    x: node.position.x + leadingShiftX,
                    y: node.position.y + leadingShiftY
                }
            };
            return node;
        });
        nodesById.set(containerId, nextNodes.find((node)=>node.id === containerId));
        const sizeChanged = nextSize.width !== currentSize.width || nextSize.height !== currentSize.height;
        if (sizeChanged || shouldShiftChildren) changes.push({
            containerId,
            previousSize: currentSize,
            nextSize,
            positionDelta: shouldShiftChildren ? {
                x: -leadingShiftX,
                y: -leadingShiftY
            } : void 0
        });
    }
    return {
        nodes: nextNodes,
        changes
    };
}
function getSafeArea(containerNode, safeArea) {
    return safeArea ?? container_getContainerSafeArea(containerNode);
}
function clampTopLeftToSafeArea(position, safeArea, nodeSize) {
    const maxX = safeArea.x + Math.max(0, safeArea.width - nodeSize.width);
    const maxY = safeArea.y + Math.max(0, safeArea.height - nodeSize.height);
    return {
        x: clamp(position.x, safeArea.x, maxX),
        y: clamp(position.y, safeArea.y, maxY)
    };
}
function rangesOverlap(startA, endA, startB, endB) {
    return startA < endB && endA > startB;
}
function centerOnContainerRail(containerSize, safeArea, nodeSize) {
    const railY = snapToGrid(containerSize.height / 2 - nodeSize.height / 2);
    const maxY = safeArea.y + Math.max(0, safeArea.height - nodeSize.height);
    return {
        x: Math.max(safeArea.x, snapToGrid(safeArea.x + (safeArea.width - nodeSize.width) / 2)),
        y: clamp(railY, safeArea.y, maxY)
    };
}
function getLocalNodePosition(node, containerNode, nodes) {
    if (!node || node.id === containerNode.id) return null;
    if (node.parentId === containerNode.id) return node.position;
    const containerPosition = getAbsolutePosition(containerNode, nodes);
    const nodePosition = node.parentId ? getAbsolutePosition(node, nodes) : node.position;
    return {
        x: nodePosition.x - containerPosition.x,
        y: nodePosition.y - containerPosition.y
    };
}
function getPlacementDirection(sourcePosition, targetPosition) {
    if (!sourcePosition || !targetPosition) return sourcePosition ? 'right' : 'left';
    return targetPosition.x >= sourcePosition.x ? 'right' : 'left';
}
function getSiblingNodes(containerId, nodes, insertedNodeId) {
    return nodes.filter((node)=>node.id !== PREVIEW_NODE_ID && node.id !== insertedNodeId && node.parentId === containerId);
}
function resolveSequenceTopLeft({ sourceNode, targetNode, containerNode, nodes, safeArea, previewNodeSize, gap, getNodeDimensions }) {
    const sourcePosition = getLocalNodePosition(sourceNode, containerNode, nodes);
    const targetPosition = getLocalNodePosition(targetNode, containerNode, nodes);
    const sourceSize = sourceNode ? getNodeDimensions(sourceNode) : void 0;
    const targetSize = targetNode ? getNodeDimensions(targetNode) : void 0;
    if (sourcePosition && sourceSize && targetPosition) {
        const sourceRight = sourcePosition.x + sourceSize.width;
        const targetLeft = targetPosition.x;
        const centerX = targetLeft - sourceRight >= previewNodeSize.width + 2 * gap ? sourceRight + (targetLeft - sourceRight) / 2 : sourceRight + gap + previewNodeSize.width / 2;
        return {
            x: centerX - previewNodeSize.width / 2,
            y: sourcePosition.y + sourceSize.height / 2 - previewNodeSize.height / 2
        };
    }
    if (sourcePosition && sourceSize) return {
        x: sourcePosition.x + sourceSize.width + gap,
        y: sourcePosition.y + sourceSize.height / 2 - previewNodeSize.height / 2
    };
    if (targetPosition && targetSize) return {
        x: targetPosition.x - previewNodeSize.width - gap,
        y: targetPosition.y + targetSize.height / 2 - previewNodeSize.height / 2
    };
    return {
        x: safeArea.x + safeArea.width / 2 - previewNodeSize.width / 2,
        y: safeArea.y + safeArea.height / 2 - previewNodeSize.height / 2
    };
}
function getContainerNodeForEdge(sourceNode, targetNode, nodes) {
    if (sourceNode.parentId === targetNode.id) return targetNode;
    if (targetNode.parentId === sourceNode.id) return sourceNode;
    if (sourceNode.parentId && sourceNode.parentId === targetNode.parentId) return nodes.find((node)=>node.id === sourceNode.parentId) ?? null;
    return null;
}
function handleMatches(edgeHandleId, handleId) {
    return (edgeHandleId ?? DEFAULT_SOURCE_HANDLE_ID) === handleId;
}
function getOutgoingEdges(sourceNodeId, sourceHandleId, edges) {
    return edges.filter((edge)=>edge.source !== PREVIEW_NODE_ID && edge.target !== PREVIEW_NODE_ID && edge.source === sourceNodeId && handleMatches(edge.sourceHandle, sourceHandleId));
}
function getSingleOutgoingEdge(sourceNodeId, sourceHandleId, edges) {
    const outgoingEdges = getOutgoingEdges(sourceNodeId, sourceHandleId, edges);
    return 1 === outgoingEdges.length ? outgoingEdges[0] : null;
}
function isPreviewGraphEdge(edge) {
    return edge.source === PREVIEW_NODE_ID || edge.target === PREVIEW_NODE_ID;
}
function bucketInScopeEdgesBySource({ edges, nodesById, parentId, isSequenceEdge }) {
    const bySource = new Map();
    for (const edge of edges){
        if (isPreviewGraphEdge(edge)) continue;
        if (isSequenceEdge && !isSequenceEdge(edge)) continue;
        const targetIsParent = void 0 !== parentId && edge.target === parentId;
        const targetIsSibling = nodesById.get(edge.target)?.parentId === parentId;
        if (!targetIsParent && !targetIsSibling) continue;
        const bucket = bySource.get(edge.source);
        if (bucket) bucket.push(edge);
        else bySource.set(edge.source, [
            edge
        ]);
    }
    return bySource;
}
function collectLinearDownstreamSiblings({ startNodeId, parentId, nodes, edges, isSequenceEdge, getNextNodeId }) {
    if (!startNodeId || startNodeId === parentId) return [];
    const nodesById = new Map(nodes.map((node)=>[
            node.id,
            node
        ]));
    const inScopeEdgesBySource = bucketInScopeEdgesBySource({
        edges,
        nodesById,
        parentId,
        isSequenceEdge
    });
    const collectedIds = [];
    const visitedIds = new Set();
    let currentNodeId = startNodeId;
    while(!visitedIds.has(currentNodeId)){
        const currentNode = nodesById.get(currentNodeId);
        if (!currentNode || currentNode.parentId !== parentId) break;
        collectedIds.push(currentNodeId);
        visitedIds.add(currentNodeId);
        let next = getNextNodeId ? getNextNodeId({
            nodeId: currentNodeId,
            edges,
            nodesById,
            parentId
        }) : void 0;
        if (void 0 === next) {
            const outgoing = inScopeEdgesBySource.get(currentNodeId) ?? [];
            next = 1 === outgoing.length ? outgoing[0].target : null;
        }
        if (!next || next === parentId) break;
        currentNodeId = next;
    }
    return collectedIds;
}
function resolveInsertPreview({ source, sourceHandleType, reactFlowInstance, isContainerNode, allowedContainerId, replacedEdge: exactReplacedEdge, getContainerSafeArea, previewNodeSize, gap, getNodeDimensions }) {
    if ('source' !== sourceHandleType || !source.handleId) return null;
    const replacedEdge = exactReplacedEdge ?? getSingleOutgoingEdge(source.nodeId, source.handleId, reactFlowInstance.getEdges());
    if (!replacedEdge) return null;
    const sourceNode = reactFlowInstance.getNode(replacedEdge.source);
    const targetNode = reactFlowInstance.getNode(replacedEdge.target);
    if (!sourceNode || !targetNode) return null;
    const nodes = reactFlowInstance.getNodes();
    const containerNode = getContainerNodeForEdge(sourceNode, targetNode, nodes);
    if (!containerNode) return null;
    if (allowedContainerId && containerNode.id !== allowedContainerId) return null;
    if (isContainerNode && !isContainerNode(containerNode)) return null;
    const containerPlacement = getPreviewPlacement({
        sourceNode,
        targetNode,
        containerNode,
        nodes,
        safeArea: getContainerSafeArea?.(containerNode),
        previewNodeSize,
        gap,
        getNodeDimensions
    });
    const placement = {
        containerId: containerPlacement.containerId,
        sourceNodeId: sourceNode.id,
        targetNodeId: targetNode.id,
        mode: 'sequence'
    };
    return {
        position: containerPlacement.centerPosition,
        positionMode: 'center',
        data: {
            originalEdge: replacedEdge,
            [PLACEMENT_DATA_KEY]: placement
        },
        target: {
            nodeId: replacedEdge.target,
            handleId: replacedEdge.targetHandle
        },
        containerId: containerPlacement.containerId
    };
}
function resolveAppendPreview({ source, sourceHandleType, reactFlowInstance, ...options }) {
    if ('source' !== sourceHandleType || !source.handleId) return null;
    const sourceNode = reactFlowInstance.getNode(source.nodeId);
    if (!sourceNode?.parentId) return null;
    const outgoingEdges = getOutgoingEdges(source.nodeId, source.handleId, reactFlowInstance.getEdges());
    if (outgoingEdges.length > 0) return null;
    const nodes = reactFlowInstance.getNodes();
    const containerNode = nodes.find((node)=>node.id === sourceNode.parentId);
    if (options.allowedContainerId && containerNode.id !== options.allowedContainerId) return null;
    if (options.isContainerNode && !options.isContainerNode(containerNode)) return null;
    const continuationTarget = options.getContainerContinuationTarget?.({
        containerNode,
        sourceNode,
        source,
        reactFlowInstance
    });
    if (!continuationTarget) return null;
    const placement = {
        containerId: containerNode.id,
        sourceNodeId: sourceNode.id,
        targetNodeId: continuationTarget.nodeId,
        mode: 'sequence'
    };
    return {
        data: {
            [PLACEMENT_DATA_KEY]: placement
        },
        target: continuationTarget,
        containerId: containerNode.id
    };
}
function resolveContainerPreview({ source, sourceHandleType, reactFlowInstance, ...options }) {
    return resolveInsertPreview({
        source,
        sourceHandleType,
        reactFlowInstance,
        ...options
    }) ?? resolveAppendPreview({
        source,
        sourceHandleType,
        reactFlowInstance,
        ...options
    });
}
function getPreviewPlacement({ sourceNode, targetNode, containerNode, nodes, containerAbsolutePosition = getAbsolutePosition(containerNode, nodes), safeArea, previewNodeSize = {
    width: DEFAULT_NODE_SIZE,
    height: DEFAULT_NODE_SIZE
}, gap = CONTAINER_SEQUENCE_GAP_PX, avoidSiblings = false, getNodeDimensions: resolveNodeDimensions = container_getNodeDimensions }) {
    const resolvedSafeArea = getSafeArea(containerNode, safeArea);
    const sourcePosition = getLocalNodePosition(sourceNode, containerNode, nodes);
    const targetPosition = getLocalNodePosition(targetNode, containerNode, nodes);
    const direction = getPlacementDirection(sourcePosition, targetPosition);
    const initialPosition = resolveSequenceTopLeft({
        sourceNode,
        targetNode,
        containerNode,
        nodes,
        safeArea: resolvedSafeArea,
        previewNodeSize,
        gap,
        getNodeDimensions: resolveNodeDimensions
    });
    const collisionPosition = avoidSiblings ? getNonOverlappingPositionForDirection(getSiblingNodes(containerNode.id, nodes), initialPosition, previewNodeSize, direction, gap) : initialPosition;
    const position = clampTopLeftToSafeArea(collisionPosition, resolvedSafeArea, previewNodeSize);
    return {
        containerId: containerNode.id,
        centerPosition: {
            x: containerAbsolutePosition.x + position.x + previewNodeSize.width / 2,
            y: containerAbsolutePosition.y + position.y + previewNodeSize.height / 2
        }
    };
}
function getNodeDepth(node, nodesById) {
    let depth = 0;
    let parentId = node?.parentId;
    while(parentId){
        depth += 1;
        parentId = nodesById.get(parentId)?.parentId;
    }
    return depth;
}
function sortContainerSizeChanges(changes, nodes) {
    const nodesById = new Map(nodes.map((node)=>[
            node.id,
            node
        ]));
    return [
        ...changes
    ].sort((left, right)=>{
        const leftNode = nodesById.get(left.containerId);
        const rightNode = nodesById.get(right.containerId);
        const depthDifference = getNodeDepth(rightNode, nodesById) - getNodeDepth(leftNode, nodesById);
        if (0 !== depthDifference) return depthDifference;
        if (leftNode.parentId === rightNode.parentId) return leftNode.position.x - rightNode.position.x;
        return 0;
    });
}
function pushSiblingsAfterContainerGrowth({ nodes, changes, getNodeDimensions, gap }) {
    let shifted = false;
    let nextNodes = nodes;
    for (const change of sortContainerSizeChanges(changes, nextNodes)){
        const containerNode = nextNodes.find((node)=>node.id === change.containerId);
        const positionDelta = change.positionDelta ?? {
            x: 0,
            y: 0
        };
        const prevPosition = {
            x: containerNode.position.x - positionDelta.x,
            y: containerNode.position.y - positionDelta.y
        };
        const oldRight = prevPosition.x + change.previousSize.width;
        const newRight = containerNode.position.x + change.nextSize.width;
        const oldBottom = prevPosition.y + change.previousSize.height;
        const newBottom = containerNode.position.y + change.nextSize.height;
        const rightDelta = newRight - oldRight;
        const bottomDelta = newBottom - oldBottom;
        if (rightDelta <= 0 && bottomDelta <= 0) continue;
        const containerLeft = Math.min(containerNode.position.x, prevPosition.x);
        const containerRight = Math.max(newRight, oldRight);
        const containerTop = Math.min(containerNode.position.y, prevPosition.y);
        const containerBottom = Math.max(newBottom, oldBottom);
        nextNodes = nextNodes.map((node)=>{
            if (node.id === containerNode.id || node.parentId !== containerNode.parentId) return node;
            const nodeSize = getNodeDimensions(node);
            let nextX = node.position.x;
            let nextY = node.position.y;
            if (rightDelta > 0 && node.position.x >= oldRight) {
                const verticallyOverlaps = rangesOverlap(node.position.y, node.position.y + nodeSize.height, containerTop, containerBottom);
                if (verticallyOverlaps) nextX = Math.max(node.position.x + rightDelta, snapUpToGrid(newRight + gap));
            }
            if (bottomDelta > 0 && node.position.y >= oldBottom) {
                const horizontallyOverlaps = rangesOverlap(node.position.x, node.position.x + nodeSize.width, containerLeft, containerRight);
                if (horizontallyOverlaps) nextY = Math.max(node.position.y + bottomDelta, snapUpToGrid(newBottom + gap));
            }
            if (nextX === node.position.x && nextY === node.position.y) return node;
            shifted = true;
            return {
                ...node,
                position: {
                    x: nextX,
                    y: nextY
                }
            };
        });
    }
    return {
        nodes: nextNodes,
        shifted
    };
}
function fitContainersAndPushSiblings({ nodes, containerIds, getContainerFitGeometry, getNodeDimensions, ignoredNodeTypes, gap }) {
    let nextNodes = nodes;
    for(let iteration = 0; iteration < 10; iteration += 1){
        const fitResult = ensureContainersFitChildren(nextNodes, {
            containerIds,
            getContainerFitGeometry,
            getNodeDimensions,
            ignoredNodeTypes,
            includeAncestors: true
        });
        nextNodes = fitResult.nodes;
        if (0 === fitResult.changes.length) break;
        const pushResult = pushSiblingsAfterContainerGrowth({
            nodes: nextNodes,
            changes: fitResult.changes,
            getNodeDimensions,
            gap
        });
        nextNodes = pushResult.nodes;
        if (!pushResult.shifted) break;
    }
    return nextNodes;
}
function getContainerPlacement(previewNode) {
    const placement = previewNode.data?.[PLACEMENT_DATA_KEY];
    if (!placement) return null;
    if (previewNode.parentId && placement.containerId !== previewNode.parentId) return null;
    return placement;
}
function placeContainerNode({ nodes, insertedNode, placement, safeArea, getContainerFitGeometry: resolveContainerFitGeometry = container_getContainerFitGeometry, getNodeDimensions: resolveNodeDimensions = container_getNodeDimensions, gap = CONTAINER_SEQUENCE_GAP_PX, downstreamNodeIds, ignoredNodeTypes, edges }) {
    const containerNode = nodes.find((node)=>node.id === placement.containerId);
    const nodesById = new Map(nodes.map((node)=>[
            node.id,
            node
        ]));
    const insertedSize = resolveNodeDimensions(insertedNode);
    const resolvedSafeArea = getSafeArea(containerNode, safeArea);
    const upstreamSource = placement.sourceNodeId && placement.sourceNodeId !== placement.containerId ? nodesById.get(placement.sourceNodeId) : void 0;
    const sequenceX = upstreamSource ? Math.max(insertedNode.position.x, snapUpToGrid(upstreamSource.position.x + resolveNodeDimensions(upstreamSource).width + gap)) : insertedNode.position.x;
    const positionedNode = 'first-child' === placement.mode ? {
        ...insertedNode,
        position: centerOnContainerRail(resolveNodeDimensions(containerNode), resolvedSafeArea, insertedSize)
    } : sequenceX !== insertedNode.position.x ? {
        ...insertedNode,
        position: {
            ...insertedNode.position,
            x: sequenceX
        }
    } : insertedNode;
    const targetNode = placement.targetNodeId && placement.targetNodeId !== placement.containerId ? nodesById.get(placement.targetNodeId) : void 0;
    const fallbackTargetIds = placement.targetNodeId && placement.targetNodeId !== placement.containerId ? [
        placement.targetNodeId
    ] : [];
    const idsToShift = new Set(downstreamNodeIds ?? (edges ? collectLinearDownstreamSiblings({
        startNodeId: placement.targetNodeId,
        parentId: placement.containerId,
        nodes,
        edges
    }) : fallbackTargetIds));
    if (placement.sourceNodeId) idsToShift.delete(placement.sourceNodeId);
    const sourceIsContainer = placement.sourceNodeId === placement.containerId;
    const isBackEdge = !sourceIsContainer && void 0 !== upstreamSource && void 0 !== targetNode && upstreamSource.position.x >= targetNode.position.x;
    const requiredTargetLeft = positionedNode.position.x + insertedSize.width + gap;
    const downstreamShift = !isBackEdge && targetNode && targetNode.position.x < requiredTargetLeft ? snapUpToGrid(requiredTargetLeft - targetNode.position.x) : 0;
    const positionedNodes = nodes.map((node)=>{
        if (node.id === insertedNode.id) return positionedNode;
        if (downstreamShift > 0 && idsToShift.has(node.id)) return {
            ...node,
            position: {
                ...node.position,
                x: node.position.x + downstreamShift
            }
        };
        return node;
    });
    return fitContainersAndPushSiblings({
        nodes: positionedNodes,
        containerIds: [
            placement.containerId
        ],
        getContainerFitGeometry: resolveContainerFitGeometry,
        getNodeDimensions: resolveNodeDimensions,
        ignoredNodeTypes,
        gap
    });
}
export { CONTAINER_FRAME_INSET_PX, CONTAINER_SEQUENCE_GAP_PX, DEFAULT_CONTAINER_HEIGHT, DEFAULT_CONTAINER_MIN_HEIGHT, DEFAULT_CONTAINER_MIN_WIDTH, DEFAULT_CONTAINER_WIDTH, collectLinearDownstreamSiblings, ensureContainersFitChildren, fitContainersAndPushSiblings, container_getContainerFitGeometry as getContainerFitGeometry, getContainerNodeForEdge, getContainerPlacement, getContainerResizeMinimums, container_getContainerSafeArea as getContainerSafeArea, container_getNodeDimensions as getNodeDimensions, isContainerNodeManifest, placeContainerNode, resolveContainerPreview };
