import { _objectSpread2 } from "../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectSpread2.js";
import { applySourceStub, applyTargetStub, getAnchorPoint, pathFromPoints, pickAutoAnchor, resolveDrawingTargetAtPoint, routeOrthogonal, simplifyCollinearPath } from "../utils/edgeRouting.js";
import { useFloorMapEdgeViewModel } from "./useFloorMapEdgeViewModel.js";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
//#region src/components/pisellFloorMapLayout/hooks/useFloorMapEdgeEditing.ts
/**
* 平面图编辑态：连线选中、拉线、草稿 edges 更新
*/
function newEdgeId() {
	return `edge_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
}
function useFloorMapEdgeEditing(params) {
	var _config$edges, _rectsById$get;
	const { enabled, config, items, mergedItems, cellSize, itemUnit, itemGap, contentOffsetRef, selectedId, setSelectedId, commitDraft, mapContentRef, scale = 1 } = params;
	const [selectedEdgeId, setSelectedEdgeId] = useState(null);
	const [drawing, setDrawing] = useState(null);
	const [ghostEnd, setGhostEnd] = useState(null);
	/** 拉线时当前悬停的目标图元与吸附端口 */
	const [drawingTarget, setDrawingTarget] = useState(null);
	const [pendingRectsById, setPendingRectsById] = useState({});
	/** 拖动中段手柄时的临时 routing 覆盖（pointerup 后写入 edge.routing） */
	const [pendingRoutingByEdge, setPendingRoutingByEdge] = useState({});
	const edges = (_config$edges = config === null || config === void 0 ? void 0 : config.edges) !== null && _config$edges !== void 0 ? _config$edges : [];
	const { routes, rectsById } = useFloorMapEdgeViewModel({
		edges,
		items,
		cellSize,
		itemUnit,
		itemGap,
		pendingRectsById,
		pendingRoutingByEdge
	});
	const clearEdgeSelection = useCallback(() => {
		setSelectedEdgeId(null);
	}, []);
	const selectEdge = useCallback((edgeId) => {
		if (drawing) return;
		setSelectedEdgeId(edgeId);
		if (edgeId) setSelectedId(null);
	}, [drawing, setSelectedId]);
	const selectNode = useCallback((id) => {
		if (drawing) return;
		setSelectedId(id);
		if (id) setSelectedEdgeId(null);
	}, [drawing, setSelectedId]);
	const clientToContent = useCallback((clientX, clientY) => {
		const el = mapContentRef.current;
		if (!el) return null;
		const rect = el.getBoundingClientRect();
		const off = contentOffsetRef.current;
		return {
			x: (clientX - rect.left) / scale - off.x,
			y: (clientY - rect.top) / scale - off.y
		};
	}, [
		mapContentRef,
		contentOffsetRef,
		scale
	]);
	const ghostPathD = useMemo(() => {
		var _drawingTarget$anchor;
		if (!drawing || !ghostEnd) return null;
		const srcRect = rectsById.get(drawing.fromInstanceId);
		if (!srcRect) return null;
		const sourcePt = getAnchorPoint(srcRect, drawing.fromAnchor);
		const tgtRect = drawingTarget ? rectsById.get(drawingTarget.instanceId) : null;
		const targetSide = (_drawingTarget$anchor = drawingTarget === null || drawingTarget === void 0 ? void 0 : drawingTarget.anchor) !== null && _drawingTarget$anchor !== void 0 ? _drawingTarget$anchor : "left";
		const targetPt = tgtRect ? getAnchorPoint(tgtRect, targetSide) : ghostEnd;
		return pathFromPoints(routeOrthogonal(sourcePt, drawing.fromAnchor, targetPt, targetSide, void 0, srcRect && tgtRect ? {
			sourceRect: srcRect,
			targetRect: tgtRect
		} : void 0));
	}, [
		drawing,
		ghostEnd,
		drawingTarget,
		rectsById
	]);
	const drawingTargetPreview = useMemo(() => {
		if (!drawing || !drawingTarget) return null;
		const rect = rectsById.get(drawingTarget.instanceId);
		if (!rect) return null;
		return {
			instanceId: drawingTarget.instanceId,
			rect,
			anchor: drawingTarget.anchor
		};
	}, [
		drawing,
		drawingTarget,
		rectsById
	]);
	const startDrawingFromAnchor = useCallback((instanceId, anchor) => {
		setDrawing({
			fromInstanceId: instanceId,
			fromAnchor: anchor
		});
		setDrawingTarget(null);
		setGhostEnd(null);
		setSelectedEdgeId(null);
		setSelectedId(instanceId);
	}, [setSelectedId]);
	const finishDrawing = useCallback((clientX, clientY) => {
		var _resolved$instanceId;
		if (!drawing || !config) {
			setDrawing(null);
			setGhostEnd(null);
			setDrawingTarget(null);
			return;
		}
		const pt = clientToContent(clientX, clientY);
		if (!pt) {
			setDrawing(null);
			setGhostEnd(null);
			setDrawingTarget(null);
			return;
		}
		const resolved = drawingTarget !== null && drawingTarget !== void 0 ? drawingTarget : resolveDrawingTargetAtPoint(pt, mergedItems.map((m) => m.id), rectsById, drawing.fromInstanceId);
		const targetId = (_resolved$instanceId = resolved === null || resolved === void 0 ? void 0 : resolved.instanceId) !== null && _resolved$instanceId !== void 0 ? _resolved$instanceId : null;
		const targetAnchor = resolved === null || resolved === void 0 ? void 0 : resolved.anchor;
		if (targetId && targetId !== drawing.fromInstanceId) {
			const edge = {
				id: newEdgeId(),
				sourceId: drawing.fromInstanceId,
				targetId,
				sourceAnchor: drawing.fromAnchor,
				targetAnchor,
				status: "online"
			};
			commitDraft((prev) => {
				var _prev$edges;
				return _objectSpread2(_objectSpread2({}, prev), {}, { edges: [...(_prev$edges = prev.edges) !== null && _prev$edges !== void 0 ? _prev$edges : [], edge] });
			});
			setSelectedEdgeId(edge.id);
			setSelectedId(null);
		}
		setDrawing(null);
		setGhostEnd(null);
		setDrawingTarget(null);
	}, [
		drawing,
		drawingTarget,
		config,
		clientToContent,
		mergedItems,
		rectsById,
		commitDraft,
		setSelectedId
	]);
	useEffect(() => {
		if (!drawing) return;
		const onMove = (ev) => {
			const pt = clientToContent(ev.clientX, ev.clientY);
			if (!pt) return;
			const hit = resolveDrawingTargetAtPoint(pt, mergedItems.map((m) => m.id), rectsById, drawing.fromInstanceId);
			setDrawingTarget(hit);
			if (hit) {
				const rect = rectsById.get(hit.instanceId);
				if (rect) {
					setGhostEnd(getAnchorPoint(rect, hit.anchor));
					return;
				}
			}
			setGhostEnd(pt);
		};
		const onUp = (ev) => {
			finishDrawing(ev.clientX, ev.clientY);
		};
		window.addEventListener("pointermove", onMove);
		window.addEventListener("pointerup", onUp);
		return () => {
			window.removeEventListener("pointermove", onMove);
			window.removeEventListener("pointerup", onUp);
		};
	}, [
		drawing,
		clientToContent,
		finishDrawing,
		mergedItems,
		rectsById
	]);
	const updateEdge = useCallback((edgeId, patch) => {
		commitDraft((prev) => {
			var _prev$edges2;
			return _objectSpread2(_objectSpread2({}, prev), {}, { edges: ((_prev$edges2 = prev.edges) !== null && _prev$edges2 !== void 0 ? _prev$edges2 : []).map((e) => e.id === edgeId ? _objectSpread2(_objectSpread2({}, e), patch) : e) });
		});
	}, [commitDraft]);
	/**
	* 拖动连线段手柄：所有拖动都直接对「path snapshot」做形变。
	* - `role='inner'`：把指定 segmentIndex 的主轴坐标平移到鼠标位置。
	* - `role='first'`：在 snapshot 前面插入一对新点（短 stub + 新被拖动段），
	*   随后拖动改新被拖动段的主轴。即「拖首段 = 新增一个角」，可以无限叠加。
	* - `role='last'`：对称地在末尾追加一对新点。
	* 释放后把「working snapshot」一次性写入 edge.routing.pathSnapshot。
	*/
	const draggingSegmentRef = useRef(null);
	const startDraggingSegment = useCallback((params) => {
		const edge = edges.find((e) => e.id === params.edgeId);
		if (!edge) return;
		const currentRoute = routes.find((r) => r.edgeId === params.edgeId);
		if (!currentRoute) return;
		/** 始终用「当前 view-model 渲染的 path」（已对齐到最新图元锚点）作为 working 起点。
		*  再做一次共线化简，确保 applySourceStub/Target 不会被冗余共线点干扰
		*  （否则插入 stub 后改 y/x，后续共线点未跟随会出斜段）。 */
		const baseSnapshot = simplifyCollinearPath(currentRoute.points);
		let workingSnapshot = baseSnapshot;
		let targetSegIndex;
		if (params.role === "first") {
			var _edge$sourceAnchor, _params$segmentLength;
			const srcRect = rectsById.get(edge.sourceId);
			const tgtRect = rectsById.get(edge.targetId);
			const sourceAnchor = (_edge$sourceAnchor = edge.sourceAnchor) !== null && _edge$sourceAnchor !== void 0 ? _edge$sourceAnchor : srcRect && tgtRect ? pickAutoAnchor(srcRect, tgtRect) : "right";
			const segLen = (_params$segmentLength = params.segmentLength) !== null && _params$segmentLength !== void 0 ? _params$segmentLength : 48;
			workingSnapshot = applySourceStub(baseSnapshot, Math.min(24, segLen / 2), sourceAnchor);
			/** stub 后被拖动段 = path 的 segment 2（即 snapshot[2]→snapshot[3]） */
			targetSegIndex = 2;
		} else if (params.role === "last") {
			var _edge$targetAnchor, _params$segmentLength2;
			const srcRect = rectsById.get(edge.sourceId);
			const tgtRect = rectsById.get(edge.targetId);
			const targetAnchor = (_edge$targetAnchor = edge.targetAnchor) !== null && _edge$targetAnchor !== void 0 ? _edge$targetAnchor : srcRect && tgtRect ? pickAutoAnchor(tgtRect, srcRect) : "left";
			const segLen = (_params$segmentLength2 = params.segmentLength) !== null && _params$segmentLength2 !== void 0 ? _params$segmentLength2 : 48;
			workingSnapshot = applyTargetStub(baseSnapshot, Math.min(24, segLen / 2), targetAnchor);
			/** stub 后被拖动段 = path 的 segment (n-3)（即 snapshot[n-4]→snapshot[n-3]） */
			targetSegIndex = workingSnapshot.length - 4;
		} else targetSegIndex = params.segmentIndex;
		draggingSegmentRef.current = {
			edgeId: params.edgeId,
			workingSnapshot,
			targetSegIndex,
			axis: params.axis,
			startClientX: params.clientX,
			startClientY: params.clientY,
			startPosition: params.startPosition
		};
		setPendingRoutingByEdge((prev) => _objectSpread2(_objectSpread2({}, prev), {}, { [params.edgeId]: { pathSnapshot: workingSnapshot } }));
		setSelectedEdgeId(params.edgeId);
	}, [
		edges,
		routes,
		rectsById
	]);
	useEffect(() => {
		const onMove = (ev) => {
			const drag = draggingSegmentRef.current;
			if (!drag) return;
			const dx = (ev.clientX - drag.startClientX) / scale;
			const dy = (ev.clientY - drag.startClientY) / scale;
			const delta = drag.axis === "h" ? dy : dx;
			const nextPos = drag.startPosition + delta;
			const snap = drag.workingSnapshot.map((p) => [p[0], p[1]]);
			const i = drag.targetSegIndex;
			if (i < 0 || i + 1 >= snap.length) return;
			if (drag.axis === "h") {
				snap[i] = [snap[i][0], nextPos];
				snap[i + 1] = [snap[i + 1][0], nextPos];
			} else {
				snap[i] = [nextPos, snap[i][1]];
				snap[i + 1] = [nextPos, snap[i + 1][1]];
			}
			setPendingRoutingByEdge((prev) => _objectSpread2(_objectSpread2({}, prev), {}, { [drag.edgeId]: { pathSnapshot: snap } }));
		};
		const onUp = () => {
			const drag = draggingSegmentRef.current;
			draggingSegmentRef.current = null;
			if (!drag) return;
			setPendingRoutingByEdge((prev) => {
				const next = _objectSpread2({}, prev);
				const finalRouting = next[drag.edgeId];
				delete next[drag.edgeId];
				if (finalRouting) updateEdge(drag.edgeId, { routing: finalRouting });
				return next;
			});
		};
		window.addEventListener("pointermove", onMove);
		window.addEventListener("pointerup", onUp);
		return () => {
			window.removeEventListener("pointermove", onMove);
			window.removeEventListener("pointerup", onUp);
		};
	}, [scale, updateEdge]);
	const deleteEdge = useCallback((edgeId) => {
		commitDraft((prev) => {
			var _prev$edges3;
			return _objectSpread2(_objectSpread2({}, prev), {}, { edges: ((_prev$edges3 = prev.edges) !== null && _prev$edges3 !== void 0 ? _prev$edges3 : []).filter((e) => e.id !== edgeId) });
		});
		setSelectedEdgeId((id) => id === edgeId ? null : id);
	}, [commitDraft]);
	const handleEdgeClick = useCallback((edgeId) => {
		selectEdge(edgeId);
	}, [selectEdge]);
	const handleMapBackgroundClick = useCallback(() => {
		if (drawing) return;
		setSelectedEdgeId(null);
		setSelectedId(null);
	}, [drawing, setSelectedId]);
	useEffect(() => {
		if (!enabled) return;
		const onKey = (ev) => {
			if (ev.key !== "Delete" && ev.key !== "Backspace") return;
			if (!selectedEdgeId) return;
			const t = ev.target;
			if (t && (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.isContentEditable)) return;
			ev.preventDefault();
			deleteEdge(selectedEdgeId);
		};
		window.addEventListener("keydown", onKey);
		return () => window.removeEventListener("keydown", onKey);
	}, [
		enabled,
		selectedEdgeId,
		deleteEdge
	]);
	return {
		edges,
		routes,
		rectsById,
		selectedEdgeId,
		selectedEdge: useMemo(() => {
			var _edges$find;
			return (_edges$find = edges.find((e) => e.id === selectedEdgeId)) !== null && _edges$find !== void 0 ? _edges$find : null;
		}, [edges, selectedEdgeId]),
		selectedNodeRect: selectedId ? (_rectsById$get = rectsById.get(selectedId)) !== null && _rectsById$get !== void 0 ? _rectsById$get : null : null,
		drawing,
		drawingTargetPreview,
		ghostPathD,
		selectNode,
		selectEdge,
		clearEdgeSelection,
		startDrawingFromAnchor,
		handleEdgeClick,
		handleMapBackgroundClick,
		updateEdge,
		deleteEdge,
		setPendingRectsById,
		startDraggingSegment
	};
}
//#endregion
export { useFloorMapEdgeEditing };
