import { getResolvedRefDeep } from "../blocks/operation-code-sample/helpers/get-resolved-ref-deep.js";
import { toValue } from "vue";
import { unpackProxyObject } from "@scalar/workspace-store/helpers/unpack-proxy";
import { sortByOrder } from "@scalar/helpers/array/sort-by-order";
import { getOpenapiObject, getParentEntry } from "@scalar/workspace-store/navigation";
import { toJsonCompatible } from "@scalar/helpers/object/to-json-compatible";
import { escapeJsonPointer } from "@scalar/json-magic/helpers/escape-json-pointer";
//#region src/v2/helpers/drag-handle-factory.ts
/**
* Reorders items in an array by moving an item from one index to another,
* adjusting insertion point based on offset.
* Returns the new order array, or null if indices are invalid.
*/
var reorderArray = (array, fromIndex, toIndex, offset) => {
	if (fromIndex === -1 || toIndex === -1 || fromIndex === toIndex) return null;
	const newArray = [...array];
	const [removed] = newArray.splice(fromIndex, 1);
	if (removed === void 0) return null;
	const insertIndex = calculateInsertIndex(fromIndex, toIndex, offset, newArray.length);
	newArray.splice(insertIndex, 0, removed);
	return unpackProxyObject(newArray, { depth: 1 });
};
/**
* Calculates the insertion index based on drag offset and direction.
*/
var calculateInsertIndex = (fromIndex, toIndex, offset, arrayLength) => {
	const isMovingDown = fromIndex < toIndex;
	if (offset === "after") return isMovingDown ? toIndex : toIndex + 1;
	if (offset === "before") return isMovingDown ? toIndex - 1 : toIndex;
	if (offset === "into") return arrayLength;
	return toIndex;
};
/**
* Type guard to check if an entry's type matches any of the provided types.
*
* @template Entry - The traversed entry type (must extend TraversedEntry).
* @template Type - An array of allowed entry types to check against.
* @param entry - The sidebar entry to check.
* @param type - Array of entry types to match (e.g., ['document', 'tag']).
* @returns {boolean} True if entry.type is included in the provided type array; otherwise, false.
*
* @example
* isEntryType(someEntry, ['document', 'tag']) // true if someEntry.type === 'document' or 'tag'
*/
var isEntryType = (entry, type) => {
	return type.includes(entry.type);
};
/**
* Determines if the hovered item qualifies as a "reorder" operation.
* Reorder means moving before or after another item, not dropping
* into a parent container.
*/
var isReorder = (hoveredItem) => {
	return hoveredItem.offset === "before" || hoveredItem.offset === "after";
};
/**
* Determines if the hovered item represents a "drop into parent" operation.
*/
var isDropIntoParent = (hoveredItem) => {
	return hoveredItem.offset === "into";
};
/**
* Checks if two sidebar entries (dragging and hovered) share the same parent.
*
* This is useful for determining if items can be reordered within the same context,
* such as reordering tags or operations under the same tag or document, during drag-and-drop operations.
*
* @param draggingItem - The entry currently being dragged (must have a parent to compare).
* @param hoveredItem - The entry currently hovered over (must have a parent to compare).
* @returns {boolean} True if both entries have the same parent (by id), false otherwise.
*
* @example
* const parent = { id: 'doc-1', type: 'document' };
* const tagA = { id: 'tag-1', type: 'tag', parent };
* const tagB = { id: 'tag-2', type: 'tag', parent };
* itemsShareParent(tagA, tagB); // true
*
* const otherParent = { id: 'doc-2', type: 'document' };
* const tagC = { id: 'tag-3', type: 'tag', parent: otherParent };
* itemsShareParent(tagA, tagC); // false
*/
var itemsShareParent = (draggingItem, hoveredItem) => {
	if (!draggingItem.parent || !hoveredItem.parent) return false;
	return draggingItem.parent.id === hoveredItem.parent.id;
};
/**
* Triggers rebuilding of the sidebar for a specific document entry.
* This is called after operations that alter the sidebar structure.
*/
var rebuildSidebar = ({ store, entry }) => {
	const documentEntry = getParentEntry("document", entry);
	if (documentEntry) store.buildSidebar(documentEntry.name);
};
/**
* Gets the current order array for a parent entry.
* Falls back to children IDs if no explicit order exists.
*/
var getCurrentOrder = (parent, parentEntry) => {
	return parent["x-scalar-order"] ?? parentEntry.children?.map((child) => child.id) ?? [];
};
/**
* Handles reordering of entries (tags or operations) within the same parent.
*/
var handleReorderWithinParent = (store, draggingItem, hoveredItem, offset) => {
	const parentEntry = draggingItem.parent;
	if (!parentEntry || !isEntryType(parentEntry, ["tag", "document"])) return false;
	const parent = getOpenapiObject({
		store,
		entry: parentEntry
	});
	if (!parent) return false;
	const currentOrder = getCurrentOrder(parent, parentEntry);
	const newOrder = reorderArray(currentOrder, currentOrder.findIndex((id) => id === draggingItem.id), currentOrder.findIndex((id) => id === hoveredItem.id), offset);
	if (!newOrder) return false;
	parent["x-scalar-order"] = newOrder;
	rebuildSidebar({
		store,
		entry: parentEntry
	});
	return true;
};
/**
* Updates operation tags when moving between different parents.
*/
var updateOperationTags = (operation, draggedTag, hoveredTag) => {
	const tags = new Set(operation.tags ?? []);
	if (hoveredTag) tags.add(hoveredTag.name);
	if (draggedTag) tags.delete(draggedTag.name);
	operation.tags = Array.from(tags);
};
/**
* Moves an operation from one document to another.
*/
var moveOperationBetweenDocuments = (draggingDocument, hoveredDocument, draggingItem, operationCopy) => {
	if (draggingDocument.paths?.[draggingItem.path]?.[draggingItem.method]) delete draggingDocument.paths[draggingItem.path][draggingItem.method];
	if (!hoveredDocument.paths) hoveredDocument.paths = { [draggingItem.path]: { [draggingItem.method]: operationCopy } };
	else {
		if (!hoveredDocument.paths[draggingItem.path]) hoveredDocument.paths[draggingItem.path] = {};
		hoveredDocument.paths[draggingItem.path][draggingItem.method] = operationCopy;
	}
};
/**
* Retrieves the dereferenced operation object for a given path and method.
* This resolves all $ref references so you always get the full, dereferenced operation.
*/
var getDereferencedOperation = (document, path, method) => {
	const operation = document.paths?.[path]?.[method];
	if (!operation) return;
	return toJsonCompatible(getResolvedRefDeep(operation), { prefix: `#/paths/${escapeJsonPointer(path)}/${method}` });
};
/**
* Handles moving an operation between different documents/tags.
*/
var handleMoveOperation = (store, draggingItem, hoveredItem) => {
	const draggingDocumentEntry = getParentEntry("document", draggingItem);
	const hoveredDocumentEntry = getParentEntry("document", hoveredItem);
	if (!draggingDocumentEntry || !hoveredDocumentEntry) return false;
	const draggingDocument = getOpenapiObject({
		store,
		entry: draggingDocumentEntry
	});
	const hoveredDocument = getOpenapiObject({
		store,
		entry: hoveredDocumentEntry
	});
	if (!draggingDocument || !hoveredDocument) return false;
	const currentOperation = getDereferencedOperation(draggingDocument, draggingItem.path, draggingItem.method);
	if (!currentOperation) return false;
	updateOperationTags(currentOperation, getParentEntry("tag", draggingItem) ?? null, getParentEntry("tag", hoveredItem) ?? null);
	moveOperationBetweenDocuments(draggingDocument, hoveredDocument, draggingItem, currentOperation);
	rebuildSidebar({
		store,
		entry: draggingItem
	});
	rebuildSidebar({
		store,
		entry: hoveredItem
	});
	return true;
};
/**
* Handles document reordering.
*/
var handleDocumentReorder = (store, draggingItem, hoveredItem, offset) => {
	const order = store.workspace["x-scalar-order"] ?? [];
	const currentOrder = sortByOrder(Object.keys(store.workspace.documents), order, (item) => item);
	const newOrder = reorderArray(currentOrder, currentOrder.findIndex((id) => id === draggingItem.id), currentOrder.findIndex((id) => id === hoveredItem.id), offset);
	if (!newOrder) return false;
	store.update("x-scalar-order", newOrder);
	return true;
};
/**
* Factory function that creates drag and drop handlers for sidebar items.
* Handles reordering and moving of documents, tags, and operations.
*/
var dragHandleFactory = ({ store: _store, sidebarState }) => {
	/**
	* Handles the end of a drag operation.
	* Returns true if the drag and drop was successful, false otherwise.
	*/
	const handleDragEnd = (_draggingItem, _hoveredItem) => {
		const store = toValue(_store);
		if (!store) return false;
		const draggingItem = sidebarState.getEntryById(_draggingItem.id);
		const hoveredItem = sidebarState.getEntryById(_hoveredItem.id);
		if (!draggingItem || !hoveredItem) return false;
		if (draggingItem.type === "document") {
			if (hoveredItem.type !== "document" || !isReorder(_hoveredItem)) return false;
			return handleDocumentReorder(store, draggingItem, hoveredItem, _hoveredItem.offset);
		}
		if (draggingItem.type === "tag") {
			if (hoveredItem.type !== "tag" || !isReorder(_hoveredItem) || !itemsShareParent(draggingItem, hoveredItem)) return false;
			return handleReorderWithinParent(store, draggingItem, hoveredItem, _hoveredItem.offset);
		}
		if (draggingItem.type === "operation") {
			if (isReorder(_hoveredItem) && itemsShareParent(draggingItem, hoveredItem)) return handleReorderWithinParent(store, draggingItem, hoveredItem, _hoveredItem.offset);
			if (isEntryType(hoveredItem, ["tag", "document"]) && isDropIntoParent(_hoveredItem)) return handleMoveOperation(store, draggingItem, hoveredItem);
		}
		return false;
	};
	/**
	* Determines if an item can be dropped on another item.
	* Returns true if the drop operation is allowed, false otherwise.
	*/
	const isDroppable = (_draggingItem, _hoveredItem) => {
		const store = toValue(_store);
		if (!store) return false;
		const draggingItem = sidebarState.getEntryById(_draggingItem.id);
		const hoveredItem = sidebarState.getEntryById(_hoveredItem.id);
		if (!draggingItem || !hoveredItem) return false;
		if (draggingItem.type === "example") return false;
		if (draggingItem.type === "document") return hoveredItem.type === "document" && isReorder(_hoveredItem);
		if (draggingItem.type === "tag") return isReorder(_hoveredItem) && itemsShareParent(draggingItem, hoveredItem);
		if (draggingItem.type === "operation") {
			const hoveredDocumentEntry = getParentEntry("document", hoveredItem);
			const draggingDocumentEntry = getParentEntry("document", draggingItem);
			if (!hoveredDocumentEntry || !draggingDocumentEntry) return false;
			const hoveredDocument = getOpenapiObject({
				store,
				entry: hoveredDocumentEntry
			});
			if (!hoveredDocument) return false;
			const sameParent = itemsShareParent(draggingItem, hoveredItem);
			if (isReorder(_hoveredItem) && !sameParent) return false;
			if (sameParent) return isReorder(_hoveredItem) || isDropIntoParent(_hoveredItem) && isEntryType(hoveredItem, ["tag", "document"]);
			return isDropIntoParent(_hoveredItem) && isEntryType(hoveredItem, ["tag", "document"]) && (draggingDocumentEntry.id === hoveredDocumentEntry.id || hoveredDocument.paths?.[draggingItem.path]?.[draggingItem.method] === void 0);
		}
		return false;
	};
	return {
		handleDragEnd,
		isDroppable
	};
};
//#endregion
export { dragHandleFactory };

//# sourceMappingURL=drag-handle-factory.js.map