import { createGutterUtilityElement } from "../utils/createGutterUtilityElement.js";
import { toHtml } from "hast-util-to-html";

//#region src/managers/MouseEventManager.ts
function isLineEventData(data, mode) {
	if (data == null) return false;
	if (mode === "file") return data.type === "line";
	else return data.type === "diff-line";
}
function isExpandoEventData(data) {
	return data?.type === "line-info";
}
var MouseEventManager = class {
	hoveredLine;
	pre;
	gutterUtilityContainer;
	gutterUtilityButton;
	gutterUtilitySlot;
	interactiveLinesAttr = false;
	interactiveLineNumbersAttr = false;
	hasEventListeners = false;
	constructor(mode, options) {
		this.mode = mode;
		this.options = options;
	}
	setOptions(options) {
		this.options = options;
	}
	cleanUp() {
		this.pre?.removeEventListener("click", this.handleMouseClick);
		this.pre?.removeEventListener("pointermove", this.handleMouseMove);
		this.pre?.removeEventListener("pointerleave", this.handleMouseLeave);
		this.pre?.removeAttribute("data-interactive-lines");
		this.pre?.removeAttribute("data-interactive-line-numbers");
		this.gutterUtilityContainer?.remove();
		this.gutterUtilityContainer = void 0;
		this.gutterUtilityButton?.removeEventListener("pointerdown", this.handleGutterUtilityPointerDown);
		this.gutterUtilityButton = void 0;
		this.gutterUtilitySlot = void 0;
		this.clearHoveredLine();
		this.interactiveLinesAttr = false;
		this.interactiveLineNumbersAttr = false;
		this.hasEventListeners = false;
		this.pre = void 0;
	}
	setup(pre) {
		const { __debugMouseEvents, onLineClick, onLineNumberClick, onLineEnter, onLineLeave, usesCustomGutterUtility = false, onHunkExpand, enableGutterUtility = false, lineHoverHighlight = "disabled" } = this.options;
		const newContainer = this.pre !== pre;
		if (newContainer) {
			this.cleanUp();
			this.pre = pre;
			this.hasEventListeners = false;
		}
		if (enableGutterUtility) this.ensureGutterUtilityNode(usesCustomGutterUtility);
		else if (this.gutterUtilityContainer != null) {
			this.gutterUtilityContainer.remove();
			this.gutterUtilityContainer = void 0;
			this.gutterUtilityButton?.removeEventListener("pointerdown", this.handleGutterUtilityPointerDown);
			this.gutterUtilityButton = void 0;
			this.gutterUtilitySlot = void 0;
		}
		const requiresEventListeners = lineHoverHighlight !== "disabled" || onLineClick != null || onLineNumberClick != null || onHunkExpand != null || onLineEnter != null || onLineLeave != null || enableGutterUtility;
		if ((newContainer || !this.hasEventListeners) && requiresEventListeners) {
			this.hasEventListeners = true;
			pre.addEventListener("click", this.handleMouseClick);
			if (onLineClick != null) {
				pre.setAttribute("data-interactive-lines", "");
				this.interactiveLinesAttr = true;
				this.interactiveLineNumbersAttr = false;
			} else if (onLineNumberClick != null) {
				pre.setAttribute("data-interactive-line-numbers", "");
				this.interactiveLinesAttr = false;
				this.interactiveLineNumbersAttr = true;
			}
			debugLogIfEnabled(__debugMouseEvents, "click", "FileDiff.DEBUG.attachEventListeners: Attaching click events for:", (() => {
				const reasons = [];
				if (__debugMouseEvents === "both" || __debugMouseEvents === "click") {
					if (onLineClick != null) reasons.push("onLineClick");
					if (onLineNumberClick != null) reasons.push("onLineNumberClick");
					if (onHunkExpand != null) reasons.push("expandable hunk separators");
				}
				return reasons;
			})());
			pre.addEventListener("pointermove", this.handleMouseMove);
			debugLogIfEnabled(__debugMouseEvents, "move", "FileDiff.DEBUG.attachEventListeners: Attaching pointer move event");
			pre.addEventListener("pointerleave", this.handleMouseLeave);
			debugLogIfEnabled(__debugMouseEvents, "move", "FileDiff.DEBUG.attachEventListeners: Attaching pointer leave event");
		} else if (!requiresEventListeners && this.hasEventListeners) {
			this.pre?.removeEventListener("click", this.handleMouseClick);
			this.pre?.removeEventListener("pointermove", this.handleMouseMove);
			this.pre?.removeEventListener("pointerleave", this.handleMouseLeave);
			this.hasEventListeners = false;
		}
		if (!newContainer) if (onLineClick != null) {
			if (this.interactiveLineNumbersAttr) {
				pre.removeAttribute("data-interactive-line-numbers");
				this.interactiveLineNumbersAttr = false;
			}
			if (!this.interactiveLinesAttr) {
				pre.setAttribute("data-interactive-lines", "");
				this.interactiveLinesAttr = true;
			}
		} else if (onLineNumberClick != null) {
			if (this.interactiveLinesAttr) {
				pre.removeAttribute("data-interactive-lines");
				this.interactiveLinesAttr = false;
			}
			if (!this.interactiveLineNumbersAttr) {
				pre.setAttribute("data-interactive-line-numbers", "");
				this.interactiveLineNumbersAttr = true;
			}
		} else {
			if (this.interactiveLinesAttr) {
				pre.removeAttribute("data-interactive-lines");
				this.interactiveLinesAttr = false;
			}
			if (this.interactiveLineNumbersAttr) {
				pre.removeAttribute("data-interactive-line-numbers");
				this.interactiveLineNumbersAttr = false;
			}
		}
	}
	getHoveredLine = () => {
		if (this.hoveredLine != null) {
			if (this.mode === "diff" && this.hoveredLine.type === "diff-line") return {
				lineNumber: this.hoveredLine.lineNumber,
				side: this.hoveredLine.annotationSide
			};
			if (this.mode === "file" && this.hoveredLine.type === "line") return { lineNumber: this.hoveredLine.lineNumber };
		}
	};
	handleMouseClick = (event) => {
		const { onGutterUtilityClick, onHunkExpand, onLineClick, onLineNumberClick } = this.options;
		if (onGutterUtilityClick == null && onHunkExpand == null && onLineClick == null && onLineNumberClick == null) return;
		debugLogIfEnabled(this.options.__debugMouseEvents, "click", "FileDiff.DEBUG.handleMouseClick:", event);
		this.handleMouseEvent({
			eventType: "click",
			event
		});
	};
	handleMouseMove = (event) => {
		const { lineHoverHighlight = "disabled", onLineEnter, onLineLeave, enableGutterUtility = false } = this.options;
		if (lineHoverHighlight === "disabled" && !enableGutterUtility && onLineEnter == null && onLineLeave == null) return;
		debugLogIfEnabled(this.options.__debugMouseEvents, "move", "FileDiff.DEBUG.handleMouseMove:", event);
		this.handleMouseEvent({
			eventType: "move",
			event
		});
	};
	handleMouseLeave = (event) => {
		const { __debugMouseEvents } = this.options;
		debugLogIfEnabled(__debugMouseEvents, "move", "FileDiff.DEBUG.handleMouseLeave: no event");
		if (this.hoveredLine == null) {
			debugLogIfEnabled(__debugMouseEvents, "move", "FileDiff.DEBUG.handleMouseLeave: returned early, no .hoveredLine");
			return;
		}
		this.gutterUtilityContainer?.remove();
		this.options.onLineLeave?.({
			...this.hoveredLine,
			event
		});
		this.clearHoveredLine();
	};
	handleMouseEvent({ eventType, event }) {
		const { __debugMouseEvents } = this.options;
		const composedPath = event.composedPath();
		debugLogIfEnabled(__debugMouseEvents, eventType, "FileDiff.DEBUG.handleMouseEvent:", {
			eventType,
			composedPath
		});
		const data = this.getLineData(composedPath);
		debugLogIfEnabled(__debugMouseEvents, eventType, "FileDiff.DEBUG.handleMouseEvent: getLineData result:", data);
		const { onLineClick, onLineNumberClick, onLineEnter, onLineLeave, onGutterUtilityClick, onHunkExpand } = this.options;
		switch (eventType) {
			case "move":
				if (isLineEventData(data, this.mode) && this.hoveredLine?.lineElement === data.lineElement) {
					debugLogIfEnabled(__debugMouseEvents, "move", "FileDiff.DEBUG.handleMouseEvent: switch, 'move', returned early because same line");
					break;
				}
				if (this.hoveredLine != null) {
					debugLogIfEnabled(__debugMouseEvents, "move", "FileDiff.DEBUG.handleMouseEvent: switch, 'move', clearing an existing hovered line and firing onLineLeave");
					this.gutterUtilityContainer?.remove();
					onLineLeave?.({
						...this.hoveredLine,
						event
					});
					this.clearHoveredLine();
				}
				if (isLineEventData(data, this.mode)) {
					debugLogIfEnabled(__debugMouseEvents, "move", "FileDiff.DEBUG.handleMouseEvent: switch, 'move', setting up a new hoveredLine and firing onLineEnter");
					this.setHoveredLine(data);
					if (this.gutterUtilityContainer != null) data.numberElement.appendChild(this.gutterUtilityContainer);
					onLineEnter?.({
						...this.hoveredLine,
						event
					});
				}
				break;
			case "click":
				debugLogIfEnabled(__debugMouseEvents, "click", "FileDiff.DEBUG.handleMouseEvent: switch, 'click', with data:", data);
				if (onGutterUtilityClick != null && this.gutterUtilityButton != null && composedPathIncludesElement(composedPath, this.gutterUtilityButton)) {
					event.stopPropagation();
					const hoveredLine = this.getHoveredLine();
					if (hoveredLine != null) onGutterUtilityClick(hoveredLine);
					break;
				}
				if (data == null) break;
				if (isExpandoEventData(data) && onHunkExpand != null) {
					debugLogIfEnabled(__debugMouseEvents, "click", "FileDiff.DEBUG.handleMouseEvent: switch, 'click', expanding a hunk");
					onHunkExpand(data.hunkIndex, data.direction);
					break;
				}
				if (isLineEventData(data, this.mode)) if (onLineNumberClick != null && data.numberColumn) {
					debugLogIfEnabled(__debugMouseEvents, "click", "FileDiff.DEBUG.handleMouseEvent: switch, 'click', firing 'onLineNumberClick'");
					onLineNumberClick({
						...data,
						event
					});
				} else if (onLineClick != null) {
					debugLogIfEnabled(__debugMouseEvents, "click", "FileDiff.DEBUG.handleMouseEvent: switch, 'click', firing 'onLineClick'");
					onLineClick({
						...data,
						event
					});
				} else debugLogIfEnabled(__debugMouseEvents, "click", "FileDiff.DEBUG.handleMouseEvent: switch, 'click', fell through, no event to fire");
				break;
		}
	}
	clearHoveredLine() {
		if (this.hoveredLine == null) return;
		this.hoveredLine.lineElement.removeAttribute("data-hovered");
		this.hoveredLine.numberElement.removeAttribute("data-hovered");
		this.hoveredLine = void 0;
	}
	setHoveredLine(hoveredLine) {
		const { lineHoverHighlight = "disabled" } = this.options;
		if (this.hoveredLine != null) this.clearHoveredLine();
		this.hoveredLine = hoveredLine;
		if (lineHoverHighlight !== "disabled") {
			if (lineHoverHighlight === "both" || lineHoverHighlight === "line") this.hoveredLine.lineElement.setAttribute("data-hovered", "");
			if (lineHoverHighlight === "both" || lineHoverHighlight === "number") this.hoveredLine.numberElement.setAttribute("data-hovered", "");
		}
	}
	ensureGutterUtilityNode(useCustomGutterUtility) {
		if (this.gutterUtilityContainer == null) {
			this.gutterUtilityContainer = document.createElement("div");
			this.gutterUtilityContainer.setAttribute("data-gutter-utility-slot", "");
		}
		if (useCustomGutterUtility) {
			if (this.gutterUtilityButton != null) {
				this.gutterUtilityButton.removeEventListener("pointerdown", this.handleGutterUtilityPointerDown);
				this.gutterUtilityButton.remove();
				this.gutterUtilityButton = void 0;
			}
			if (this.gutterUtilitySlot == null) {
				this.gutterUtilitySlot = document.createElement("slot");
				this.gutterUtilitySlot.name = "gutter-utility-slot";
			}
			if (this.gutterUtilitySlot.parentNode !== this.gutterUtilityContainer) this.gutterUtilityContainer.replaceChildren(this.gutterUtilitySlot);
		} else {
			this.gutterUtilitySlot?.remove();
			this.gutterUtilitySlot = void 0;
			if (this.gutterUtilityButton == null) {
				const tempDiv = document.createElement("div");
				tempDiv.innerHTML = toHtml(createGutterUtilityElement());
				const utilityButton = tempDiv.firstElementChild;
				if (!(utilityButton instanceof HTMLButtonElement)) throw new Error("MouseEventManager.ensureGutterUtilityNode: Node element should be a button");
				utilityButton.remove();
				this.gutterUtilityButton = utilityButton;
				this.gutterUtilityButton.addEventListener("pointerdown", this.handleGutterUtilityPointerDown);
			}
			if (this.gutterUtilityButton.parentNode !== this.gutterUtilityContainer) this.gutterUtilityContainer.replaceChildren(this.gutterUtilityButton);
		}
	}
	handleGutterUtilityPointerDown = (event) => {
		if (this.options.onGutterUtilityClick != null) event.stopPropagation();
	};
	getLineData(path) {
		let numberColumn = false;
		let lineType;
		let codeElement;
		let lineElement;
		let lineIndex;
		let numberElement;
		let expandInfo;
		let lineNumber;
		for (const element of path) {
			if (!(element instanceof HTMLElement)) continue;
			const _columnNumber = numberElement == null ? element.getAttribute("data-column-number") ?? void 0 : void 0;
			if (_columnNumber) {
				numberElement = element;
				lineNumber = Number.parseInt(_columnNumber, 10);
				numberColumn = true;
				lineType = getLineTypeFromElement(element);
				lineIndex = element.getAttribute("data-line-index") ?? void 0;
				continue;
			}
			const _lineNumber = lineElement == null ? element.getAttribute("data-line") ?? void 0 : void 0;
			if (_lineNumber) {
				lineElement = element;
				lineNumber = Number.parseInt(_lineNumber, 10);
				lineType = getLineTypeFromElement(element);
				lineIndex = element.getAttribute("data-line-index") ?? void 0;
				continue;
			}
			if (expandInfo == null && element.hasAttribute("data-expand-button")) {
				expandInfo = {
					hunkIndex: void 0,
					direction: (() => {
						if (element.hasAttribute("data-expand-up")) return "up";
						if (element.hasAttribute("data-expand-down")) return "down";
						return "both";
					})()
				};
				continue;
			}
			const _expandIndex = expandInfo != null ? element.getAttribute("data-expand-index") ?? void 0 : void 0;
			if (expandInfo != null && _expandIndex != null) {
				const expandIndex = Number.parseInt(_expandIndex, 10);
				if (!Number.isNaN(expandIndex)) expandInfo.hunkIndex = expandIndex;
				continue;
			}
			if (codeElement == null && element.hasAttribute("data-code")) {
				codeElement = element;
				break;
			}
		}
		if (expandInfo?.hunkIndex != null) {
			const { hunkIndex, direction } = expandInfo;
			return {
				type: "line-info",
				hunkIndex,
				direction
			};
		}
		lineElement ??= lineIndex != null ? queryHTMLElement(codeElement, `[data-line][data-line-index="${lineIndex}"]`) : void 0;
		numberElement ??= lineIndex != null ? queryHTMLElement(codeElement, `[data-column-number][data-line-index="${lineIndex}"]`) : void 0;
		if (codeElement == null || lineElement == null || numberElement == null || lineType == null) return;
		if (this.mode === "file") return {
			type: "line",
			lineElement,
			lineNumber,
			numberElement,
			numberColumn
		};
		return {
			type: "diff-line",
			annotationSide: (() => {
				switch (lineType) {
					case "change-deletion": return "deletions";
					case "change-addition": return "additions";
					default: return codeElement.hasAttribute("data-deletions") ? "deletions" : "additions";
				}
			})(),
			lineType,
			lineElement,
			numberElement,
			lineNumber,
			numberColumn
		};
	}
};
function debugLogIfEnabled(debugLogType = "none", logIfType, ...args) {
	switch (debugLogType) {
		case "none": return;
		case "both": break;
		case "click":
			if (logIfType !== "click") return;
			break;
		case "move":
			if (logIfType !== "move") return;
			break;
	}
	console.log(...args);
}
function pluckMouseEventOptions({ enableGutterUtility, enableHoverUtility, lineHoverHighlight, onGutterUtilityClick, onLineClick, onLineEnter, onLineLeave, onLineNumberClick, renderGutterUtility, renderHoverUtility, __debugMouseEvents }, onHunkExpand) {
	return {
		enableGutterUtility: resolveEnableGutterUtilityOption({
			enableGutterUtility,
			enableHoverUtility,
			renderGutterUtility,
			renderHoverUtility,
			onGutterUtilityClick
		}),
		lineHoverHighlight,
		onGutterUtilityClick,
		usesCustomGutterUtility: renderGutterUtility != null || renderHoverUtility != null,
		onHunkExpand,
		onLineClick,
		onLineEnter,
		onLineLeave,
		onLineNumberClick,
		__debugMouseEvents
	};
}
function resolveEnableGutterUtilityOption({ enableGutterUtility, enableHoverUtility, renderGutterUtility, renderHoverUtility, onGutterUtilityClick }) {
	if (enableGutterUtility !== void 0 && enableHoverUtility !== void 0) throw new Error("Cannot use both 'enableGutterUtility' and deprecated 'enableHoverUtility'. Use only 'enableGutterUtility'.");
	if (renderGutterUtility != null && renderHoverUtility != null) throw new Error("Cannot use both 'renderGutterUtility' and deprecated 'renderHoverUtility'. Use only 'renderGutterUtility'.");
	if (onGutterUtilityClick != null && (renderGutterUtility != null || renderHoverUtility != null)) throw new Error("Cannot use both 'onGutterUtilityClick' and render utility callbacks ('renderGutterUtility'/'renderHoverUtility'). Use only one gutter utility API.");
	return enableGutterUtility ?? enableHoverUtility ?? false;
}
function queryHTMLElement(parent, query) {
	const element = parent?.querySelector(query);
	return element instanceof HTMLElement ? element : void 0;
}
function composedPathIncludesElement(path, element) {
	for (const pathElement of path) if (pathElement === element) return true;
	return false;
}
function getLineTypeFromElement(element) {
	const lineType = element.getAttribute("data-line-type");
	if (lineType == null) return;
	switch (lineType) {
		case "change-deletion":
		case "change-addition":
		case "context":
		case "context-expanded": return lineType;
		default: return;
	}
}

//#endregion
export { MouseEventManager, pluckMouseEventOptions };
//# sourceMappingURL=MouseEventManager.js.map