/**
 * Copyright IBM Corp. 2020, 2026
 *
 * This source code is licensed under the Apache-2.0 license found in the
 * LICENSE file in the root directory of this source tree.
 */

//#region src/global/js/utils/makeDraggable/makeDraggable.ts
/**
* Makes a given element draggable using a handle element.
*@param draggable - object which accepts el and optional attributes handle,focusableInHandle,dragStep and shiftDragStep
*/
const makeDraggable = ({ el, dragHandle, focusableDragHandle, dragStep, shiftDragStep }) => {
	if (dragHandle) {
		dragHandle.style.cursor = "move";
		el.style.cursor = "default";
	} else el.style.cursor = "move";
	let isDragging = false;
	let currentX = 0;
	let currentY = 0;
	let initialMouseX = 0;
	let initialMouseY = 0;
	const dispatch = (type, detail) => {
		const eventInit = {
			detail,
			bubbles: true
		};
		el.dispatchEvent(new CustomEvent(type, eventInit));
	};
	const onKeyDown = (e) => {
		if (e.key === "Enter") {
			isDragging = !isDragging;
			if (isDragging) {
				const style = window.getComputedStyle(el);
				const matrix = new DOMMatrix(style.transform);
				currentX = matrix.m41;
				currentY = matrix.m42;
				dispatch("dragstart", { keyboard: true });
			} else dispatch("dragend", { keyboard: true });
		}
		if (!isDragging) return;
		const distance = e.shiftKey ? shiftDragStep ?? 32 : dragStep ?? 8;
		switch (e.key) {
			case "Enter":
			case " ":
				e.preventDefault();
				break;
			case "ArrowLeft":
				currentX -= distance;
				el.style.transform = `translate(${currentX}px, ${currentY}px)`;
				break;
			case "ArrowRight":
				currentX += distance;
				el.style.transform = `translate(${currentX}px, ${currentY}px)`;
				break;
			case "ArrowUp":
				currentY -= distance;
				el.style.transform = `translate(${currentX}px, ${currentY}px)`;
				break;
			case "ArrowDown":
				currentY += distance;
				el.style.transform = `translate(${currentX}px, ${currentY}px)`;
				break;
		}
	};
	const onMouseDown = (e) => {
		const target = e.target;
		if (!(target instanceof Node)) return;
		if (!(dragHandle ? dragHandle.contains(target) : el.contains(target))) return;
		const style = window.getComputedStyle(el);
		const matrix = new DOMMatrix(style.transform);
		currentX = matrix.m41;
		currentY = matrix.m42;
		initialMouseX = e.clientX;
		initialMouseY = e.clientY;
		isDragging = true;
		dispatch("dragstart", { mouse: true });
		document.addEventListener("mousemove", onMouseMove);
		document.addEventListener("mouseup", onMouseUp, { once: true });
	};
	const onMouseMove = (e) => {
		if (!isDragging) return;
		const dx = e.clientX - initialMouseX;
		const dy = e.clientY - initialMouseY;
		el.style.transform = `translate(${currentX + dx}px, ${currentY + dy}px)`;
	};
	const onMouseUp = () => {
		if (!isDragging) return;
		isDragging = false;
		dispatch("dragend", { mouse: true });
		document.removeEventListener("mousemove", onMouseMove);
	};
	if (dragHandle) dragHandle.addEventListener("mousedown", onMouseDown);
	else el.addEventListener("mousedown", onMouseDown);
	focusableDragHandle?.addEventListener("keydown", onKeyDown);
	const draggableCleanup = () => {
		if (dragHandle) dragHandle.removeEventListener("mousedown", onMouseDown);
		else el.removeEventListener("mousedown", onMouseDown);
		focusableDragHandle?.removeEventListener("keydown", onKeyDown);
		document.removeEventListener("mousemove", onMouseMove);
		document.removeEventListener("mouseup", onMouseUp);
	};
	return { cleanup: draggableCleanup };
};

//#endregion
export { makeDraggable };