import { _objectSpread2 } from "../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectSpread2.js";
import { _objectWithoutProperties } from "../../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectWithoutProperties.js";
import { FLOOR_MAP_SELECTION_ZONE_ELEMENT_KIND, FLOOR_MAP_STAGE_ELEMENT_KIND } from "../types.js";
import { STAGE_DECO_DEFAULT } from "../utils/floorMapStageDefaults.js";
import { FLOOR_MAP_BUILTIN_SHAPES, getFloorMapBuiltinShapeDef } from "../utils/floorMapBuiltinShapes.js";
import { FLOOR_MAP_RESOURCE_PLACE_DEFAULT, getElementKindConfig, isFloorMapBuiltinShapeElementKind, isFloorMapImageElementKind, resolveDataBindingPolicy } from "../utils/floorMapElementKindUtils.js";
import useEngineContext from "../../../hooks/useEngineContext.js";
import { createTranslationText } from "../../translation/utils.js";
import { getFloorMapDisplayLocale, initFloorMapLayoutLocales } from "../utils/floorMapCanvasDisplay.js";
import { gapToPx } from "../utils/layoutMeasurements.js";
import { mergeCanvasItems } from "../utils/floorMapConfigMerge.js";
import { useDraftConfigHistory } from "./useDraftConfigHistory.js";
import { floorMapViewConfigsEqual, normalizeFloorMapViewConfigFromProp } from "../utils/floorMapViewConfigDiff.js";
import { computeFloorMapContentStage } from "../utils/floorMapCanvasStage.js";
import { computeBatchScenePlacementPositions, getPlacementBoundingBox } from "../utils/batchScenePlacementLayout.js";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { locales } from "@pisell/utils";
import { message } from "antd";
//#region src/components/pisellFloorMapLayout/hooks/useFloorMapEditState.ts
/**
* 平面图编辑态：草稿配置、画布 Tab、选中、放置模式、落点、侧栏更新/删除/保存记录
*/
const _excluded = [
	"x",
	"y",
	"zIndex",
	"width",
	"height"
];
const IMAGE_PLACE_DEFAULT = {
	width: 400,
	height: 240
};
/** 内置图形落点缺省尺寸（落点时若已选 builtinShapeKey 会按预设 defaultSize 替换） */
const BUILTIN_SHAPE_PLACE_DEFAULT = {
	width: 96,
	height: 96
};
const I18N_PLACEMENT_FALLBACK_IMAGE = "pisell-floor-map-layout.placement.fallback-image-name";
const I18N_PLACEMENT_SELECTION_ZONE_NAME = "pisell-floor-map-layout.selection-zone.unnamed";
const SELECTION_ZONE_PLACE_DEFAULT = {
	width: 320,
	height: 240
};
const GRID_VIS_STORAGE_PREFIX = "pisell-floor-map-layout.grid-visible";
function getGridVisStorageKey(canvasId) {
	return `${GRID_VIS_STORAGE_PREFIX}:${canvasId}`;
}
function readStoredGridVisible(canvasId) {
	try {
		const raw = localStorage.getItem(getGridVisStorageKey(canvasId));
		if (raw == null) return null;
		if (raw === "1") return true;
		if (raw === "0") return false;
		return null;
	} catch (_unused) {
		return null;
	}
}
function writeStoredGridVisible(canvasId, visible) {
	try {
		localStorage.setItem(getGridVisStorageKey(canvasId), visible ? "1" : "0");
	} catch (_unused2) {}
}
/** 与 props 同步 / dirty 基线一致：合并 localStorage 中的网格显隐偏好 */
function normalizeFloorMapConfigWithStoredGrid(floorMapConfigProp) {
	const baseConfig = normalizeFloorMapViewConfigFromProp(floorMapConfigProp);
	return _objectSpread2(_objectSpread2({}, baseConfig), {}, { canvases: baseConfig.canvases.map((canvas) => {
		var _canvas$mapLayer;
		const stored = readStoredGridVisible(canvas.id);
		if (stored == null) return canvas;
		return _objectSpread2(_objectSpread2({}, canvas), {}, { mapLayer: _objectSpread2(_objectSpread2({}, (_canvas$mapLayer = canvas.mapLayer) !== null && _canvas$mapLayer !== void 0 ? _canvas$mapLayer : {}), {}, { showGrid: stored }) });
	}) });
}
/** 复制单个图元时的错位偏移（内容坐标） */
const DUPLICATE_PLACE_OFFSET = 16;
function newInstanceId() {
	return `elm_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
}
function duplicateBinding(elements, binding, exceptInstanceId) {
	return (elements !== null && elements !== void 0 ? elements : []).some((e) => {
		var _e$dataBinding, _e$dataBinding2;
		return e.instanceId !== exceptInstanceId && ((_e$dataBinding = e.dataBinding) === null || _e$dataBinding === void 0 ? void 0 : _e$dataBinding.dataSourceKey) === binding.dataSourceKey && ((_e$dataBinding2 = e.dataBinding) === null || _e$dataBinding2 === void 0 ? void 0 : _e$dataBinding2.recordId) === binding.recordId;
	});
}
/** 解析落点图元的默认像素尺寸（与 applyScenePlacementAtCoords 一致） */
function resolveScenePlacementDimensions(config, scene) {
	var _scene$builtinShapeKe, _FLOOR_MAP_BUILTIN_SH, _builtinDef$defaultSi, _builtinDef$defaultSi2;
	const defaultPlacementWidth = FLOOR_MAP_RESOURCE_PLACE_DEFAULT.width;
	const defaultPlacementHeight = FLOOR_MAP_RESOURCE_PLACE_DEFAULT.height;
	const defaultDecoSize = 48;
	const kindConfig = getElementKindConfig(config, scene.elementKind);
	const policy = resolveDataBindingPolicy(kindConfig);
	const isStage = scene.elementKind === FLOOR_MAP_STAGE_ELEMENT_KIND;
	const isSelectionZone = scene.elementKind === FLOOR_MAP_SELECTION_ZONE_ELEMENT_KIND;
	const isImage = isFloorMapImageElementKind(scene.elementKind);
	const isBuiltinShape = isFloorMapBuiltinShapeElementKind(scene.elementKind);
	const initialBuiltinShapeKey = isBuiltinShape ? (_scene$builtinShapeKe = scene.builtinShapeKey) !== null && _scene$builtinShapeKe !== void 0 ? _scene$builtinShapeKe : (_FLOOR_MAP_BUILTIN_SH = FLOOR_MAP_BUILTIN_SHAPES[0]) === null || _FLOOR_MAP_BUILTIN_SH === void 0 ? void 0 : _FLOOR_MAP_BUILTIN_SH.key : void 0;
	const builtinDef = isBuiltinShape ? getFloorMapBuiltinShapeDef(initialBuiltinShapeKey) : void 0;
	const width = isImage ? IMAGE_PLACE_DEFAULT.width : isSelectionZone ? SELECTION_ZONE_PLACE_DEFAULT.width : isStage ? STAGE_DECO_DEFAULT.width : isBuiltinShape ? (_builtinDef$defaultSi = builtinDef === null || builtinDef === void 0 ? void 0 : builtinDef.defaultSize.width) !== null && _builtinDef$defaultSi !== void 0 ? _builtinDef$defaultSi : BUILTIN_SHAPE_PLACE_DEFAULT.width : defaultPlacementWidth;
	const height = isImage ? IMAGE_PLACE_DEFAULT.height : isSelectionZone ? SELECTION_ZONE_PLACE_DEFAULT.height : isStage ? STAGE_DECO_DEFAULT.height : isBuiltinShape ? (_builtinDef$defaultSi2 = builtinDef === null || builtinDef === void 0 ? void 0 : builtinDef.defaultSize.height) !== null && _builtinDef$defaultSi2 !== void 0 ? _builtinDef$defaultSi2 : BUILTIN_SHAPE_PLACE_DEFAULT.height : defaultPlacementHeight;
	const useDecoSize = policy === "none" && !isStage && !isSelectionZone && !isImage && !isBuiltinShape;
	return {
		width: useDecoSize ? defaultDecoSize : width,
		height: useDecoSize ? defaultDecoSize : height,
		useDecoSize,
		isStage,
		isSelectionZone,
		isImage,
		isBuiltinShape,
		initialBuiltinShapeKey,
		kindConfig
	};
}
function buildPlacedSceneElement(params, dims) {
	const { canvasId, scene, x, y, name, dataBinding } = params;
	const { width, height, isStage, isSelectionZone, isImage, isBuiltinShape, initialBuiltinShapeKey, kindConfig } = dims;
	return _objectSpread2(_objectSpread2({
		instanceId: newInstanceId(),
		canvasId,
		x: Math.round(x),
		y: Math.round(y),
		name,
		zIndex: isSelectionZone ? -50 : isImage || isBuiltinShape ? -5 : 0,
		width: Math.round(width),
		height: Math.round(height),
		elementKind: scene.elementKind,
		shape: isStage || isSelectionZone || isImage || isBuiltinShape ? "rect" : kindConfig === null || kindConfig === void 0 ? void 0 : kindConfig.defaultShape,
		dataBinding
	}, isImage ? { imageUrl: "" } : {}), isBuiltinShape ? { builtinShapeKey: initialBuiltinShapeKey } : {});
}
function useFloorMapEditState(params) {
	var _selectedItem$_sceneE;
	const { floorMapConfigProp, isFloorMapView, itemsProp, cellSizeProp, dataSources, onSave, onDataSourceRecordSave, dataSourceFormColumns, dataSourceRecordForm, dataSourceLabels, layoutPrefix, contentOffsetRef, showDataSourceRecordFormBlock, focusViewportOnBoundsRef } = params;
	const scheduleFocusOnPlacements = useCallback((positions, sizes) => {
		var _focusViewportOnBound;
		const box = getPlacementBoundingBox(positions, sizes);
		if (!box) return;
		focusViewportOnBoundsRef === null || focusViewportOnBoundsRef === void 0 || (_focusViewportOnBound = focusViewportOnBoundsRef.current) === null || _focusViewportOnBound === void 0 || _focusViewportOnBound.call(focusViewportOnBoundsRef, box);
	}, [focusViewportOnBoundsRef]);
	const engineLocale = getFloorMapDisplayLocale(useEngineContext());
	/**
	* 草稿历史栈：所有可撤销的编辑操作都通过 `commitDraft` 写入；
	* 上游 props 同步走 `replaceDraft`（不入栈、清空历史）。
	*/
	const draftHistory = useDraftConfigHistory();
	const draftConfig = draftHistory.present;
	const commitDraft = draftHistory.commit;
	const replaceDraft = draftHistory.replace;
	const undoDraft = draftHistory.undo;
	const redoDraft = draftHistory.redo;
	const canUndo = draftHistory.canUndo;
	const canRedo = draftHistory.canRedo;
	const [currentCanvasId, setCurrentCanvasId] = useState(null);
	const [selectedId, setSelectedId] = useState(null);
	const [placeMode, setPlaceMode] = useState(null);
	const [saving, setSaving] = useState(false);
	const [savingRecord, setSavingRecord] = useState(false);
	/**
	* 本地“已提交保存”的基线快照：
	* - 点击「完成编辑/保存」后立即置为当前 draft，避免等待上游 props 回写期间仍显示“未持久化”提示；
	* - 用户后续再改动 draft 时，dirty 会再次为 true。
	*/
	const [savedBaselineConfig, setSavedBaselineConfig] = useState(null);
	const config = isFloorMapView ? draftConfig !== null && draftConfig !== void 0 ? draftConfig : floorMapConfigProp : null;
	const configRef = useRef(config);
	configRef.current = config;
	/** 上一次已从 props 写入草稿的配置；用于区分「接口刚下发」与「用户本地改过」 */
	const lastSyncedPropConfigRef = useRef(null);
	useEffect(() => {
		if (!floorMapConfigProp) {
			lastSyncedPropConfigRef.current = null;
			return;
		}
		const nextConfig = normalizeFloorMapConfigWithStoredGrid(floorMapConfigProp);
		const lastSynced = lastSyncedPropConfigRef.current;
		/**
		* 仅当「自上次 props 同步后用户又改过草稿」时拒绝覆盖：
		* - 接口/订阅首次带回 layout：草稿仍等于上次 props → 必须 replace，否则会空白；
		* - 编辑中落了新图元：草稿已与 lastSynced 不同 → 拒绝旧 props 覆盖。
		*/
		if (draftConfig && lastSynced != null) {
			const propMatchesDraft = floorMapViewConfigsEqual(draftConfig, nextConfig);
			const draftMatchesLastSynced = floorMapViewConfigsEqual(draftConfig, lastSynced);
			if (!propMatchesDraft && !draftMatchesLastSynced) return;
		}
		/**
		* 上游配置变更：直接 replace（不进入历史栈），并清空 undo/redo；
		* 同时清空本地临时保存基线，让 dirty/未保存提示与上游对齐。
		*/
		replaceDraft(nextConfig);
		lastSyncedPropConfigRef.current = nextConfig;
		setSavedBaselineConfig((prev) => prev != null && floorMapViewConfigsEqual(prev, nextConfig) ? prev : null);
		const enabled = nextConfig.canvases.filter((c) => c.enabled).sort((a, b) => a.order - b.order);
		setCurrentCanvasId((prev) => {
			var _enabled$0$id, _enabled$;
			return prev && enabled.some((c) => c.id === prev) ? prev : (_enabled$0$id = (_enabled$ = enabled[0]) === null || _enabled$ === void 0 ? void 0 : _enabled$.id) !== null && _enabled$0$id !== void 0 ? _enabled$0$id : null;
		});
	}, [floorMapConfigProp]);
	/** 当前草稿与上游持久化 `floorMapConfig`（经同等规范化）是否不一致 */
	const isLayoutDirty = useMemo(() => {
		if (!isFloorMapView || !draftConfig || !floorMapConfigProp) return false;
		return !floorMapViewConfigsEqual(draftConfig, savedBaselineConfig !== null && savedBaselineConfig !== void 0 ? savedBaselineConfig : normalizeFloorMapConfigWithStoredGrid(floorMapConfigProp));
	}, [
		isFloorMapView,
		draftConfig,
		floorMapConfigProp,
		savedBaselineConfig
	]);
	/**
	* 撤销/重做后：若选中的图元已不存在于新草稿（如撤回了刚复制/新建的元素），
	* 自动清理 selectedId，避免侧栏/快捷栏停留在「悬空选中」状态。
	*/
	useEffect(() => {
		var _draftConfig$sceneEle;
		if (!selectedId || !draftConfig) return;
		if (!((_draftConfig$sceneEle = draftConfig.sceneElements) !== null && _draftConfig$sceneEle !== void 0 ? _draftConfig$sceneEle : []).some((e) => e.instanceId === selectedId)) setSelectedId(null);
	}, [selectedId, draftConfig]);
	/**
	* 撤销/重做后：若当前画布在新草稿中已被删除或禁用，回退到第一张启用画布。
	*/
	useEffect(() => {
		var _enabled$0$id2, _enabled$2;
		if (!draftConfig) return;
		if (!currentCanvasId) return;
		if (draftConfig.canvases.some((c) => c.id === currentCanvasId && c.enabled)) return;
		setCurrentCanvasId((_enabled$0$id2 = (_enabled$2 = draftConfig.canvases.filter((c) => c.enabled).sort((a, b) => a.order - b.order)[0]) === null || _enabled$2 === void 0 ? void 0 : _enabled$2.id) !== null && _enabled$0$id2 !== void 0 ? _enabled$0$id2 : null);
	}, [currentCanvasId, draftConfig]);
	const handleAddCanvas = useCallback(() => {
		initFloorMapLayoutLocales(engineLocale);
		const defaultName = createTranslationText(locales.getText("pisell-floor-map-layout.canvas-tabs.default-new-canvas-name"), true);
		const newId = `floor_${Date.now()}`;
		const newCanvas = {
			id: newId,
			name: defaultName,
			order: 0,
			enabled: true
		};
		commitDraft((prev) => {
			if (!prev) return prev;
			const list = prev.canvases.filter((c) => c.enabled).sort((a, b) => a.order - b.order);
			newCanvas.order = list.length > 0 ? Math.max(...list.map((c) => c.order)) + 1 : 0;
			return _objectSpread2(_objectSpread2({}, prev), {}, { canvases: [...prev.canvases, newCanvas] });
		});
		setCurrentCanvasId(newId);
	}, [engineLocale]);
	const handleRemoveCanvas = useCallback((canvasId) => {
		commitDraft((prev) => {
			var _prev$sceneElements;
			if (!prev) return prev;
			const nextCanvases = prev.canvases.filter((c) => c.id !== canvasId);
			if (nextCanvases.length === 0) return prev;
			return _objectSpread2(_objectSpread2({}, prev), {}, {
				canvases: nextCanvases,
				sceneElements: ((_prev$sceneElements = prev.sceneElements) !== null && _prev$sceneElements !== void 0 ? _prev$sceneElements : []).filter((e) => e.canvasId !== canvasId)
			});
		});
		setCurrentCanvasId((prev) => {
			var _enabled$0$id3, _enabled$3;
			if (prev !== canvasId) return prev;
			const cfg = configRef.current;
			if (!cfg) return prev;
			return (_enabled$0$id3 = (_enabled$3 = cfg.canvases.filter((c) => c.id !== canvasId && c.enabled).sort((a, b) => a.order - b.order)[0]) === null || _enabled$3 === void 0 ? void 0 : _enabled$3.id) !== null && _enabled$0$id3 !== void 0 ? _enabled$0$id3 : null;
		});
	}, []);
	const handleRenameCanvas = useCallback((canvasId, name) => {
		if (typeof name === "string") {
			const trimmed = name.trim();
			if (!trimmed) return;
			commitDraft((prev) => {
				if (!prev) return prev;
				return _objectSpread2(_objectSpread2({}, prev), {}, { canvases: prev.canvases.map((c) => c.id === canvasId ? _objectSpread2(_objectSpread2({}, c), {}, {
					name: trimmed,
					nameKey: void 0
				}) : c) });
			});
			return;
		}
		if (!Object.values(name).some((s) => typeof s === "string" && s.trim())) return;
		commitDraft((prev) => {
			if (!prev) return prev;
			return _objectSpread2(_objectSpread2({}, prev), {}, { canvases: prev.canvases.map((c) => c.id === canvasId ? _objectSpread2(_objectSpread2({}, c), {}, {
				name,
				nameKey: void 0
			}) : c) });
		});
	}, []);
	const mergedItems = useMemo(() => {
		if (!isFloorMapView || !config || !currentCanvasId) return [];
		return mergeCanvasItems(currentCanvasId, config, cellSizeProp !== null && cellSizeProp !== void 0 ? cellSizeProp : 64);
	}, [
		isFloorMapView,
		config,
		currentCanvasId,
		cellSizeProp
	]);
	const items = isFloorMapView ? mergedItems : itemsProp;
	const getBatchPlacementLayoutContext = useCallback(() => {
		var _config$sceneElements, _config$mapLayer, _canvas$mapLayer2;
		const existingElements = ((_config$sceneElements = config === null || config === void 0 ? void 0 : config.sceneElements) !== null && _config$sceneElements !== void 0 ? _config$sceneElements : []).filter((e) => e.canvasId === currentCanvasId).map((e) => {
			var _e$x, _e$y, _e$width, _e$height;
			return {
				x: Math.round((_e$x = e.x) !== null && _e$x !== void 0 ? _e$x : 0),
				y: Math.round((_e$y = e.y) !== null && _e$y !== void 0 ? _e$y : 0),
				width: Math.round((_e$width = e.width) !== null && _e$width !== void 0 ? _e$width : FLOOR_MAP_RESOURCE_PLACE_DEFAULT.width),
				height: Math.round((_e$height = e.height) !== null && _e$height !== void 0 ? _e$height : FLOOR_MAP_RESOURCE_PLACE_DEFAULT.height)
			};
		});
		const canvas = config === null || config === void 0 ? void 0 : config.canvases.find((c) => c.id === currentCanvasId);
		const mapLayer = _objectSpread2(_objectSpread2({}, (_config$mapLayer = config === null || config === void 0 ? void 0 : config.mapLayer) !== null && _config$mapLayer !== void 0 ? _config$mapLayer : {}), (_canvas$mapLayer2 = canvas === null || canvas === void 0 ? void 0 : canvas.mapLayer) !== null && _canvas$mapLayer2 !== void 0 ? _canvas$mapLayer2 : {});
		const padSingle = typeof mapLayer.boundingPadding === "number" ? mapLayer.boundingPadding : 56;
		const stage = computeFloorMapContentStage({
			items: mergedItems,
			cellSize: cellSizeProp !== null && cellSizeProp !== void 0 ? cellSizeProp : 64,
			itemGap: gapToPx(mapLayer.gap),
			itemUnit: "pixel",
			boundingPadding: padSingle,
			preferPixelBoundingBox: true
		});
		return {
			existingElements,
			contentWidth: stage.contentWidth,
			contentHeight: stage.contentHeight,
			boundingPadding: padSingle
		};
	}, [
		config,
		currentCanvasId,
		mergedItems,
		cellSizeProp
	]);
	const updateDraftSceneElement = useCallback((instanceId, patch) => {
		commitDraft((prev) => {
			var _prev$sceneElements2, _prev$sceneElements3;
			if (!prev) return prev;
			const current = ((_prev$sceneElements2 = prev.sceneElements) !== null && _prev$sceneElements2 !== void 0 ? _prev$sceneElements2 : []).find((e) => e.instanceId === instanceId);
			let normalized = _objectSpread2({}, patch);
			const unlocking = patch.layoutTransformLocked === false;
			if ((current === null || current === void 0 ? void 0 : current.layoutTransformLocked) && !unlocking) {
				const { x: _x, y: _y, zIndex: _z, width: _w, height: _h } = normalized;
				normalized = _objectWithoutProperties(normalized, _excluded);
			}
			if (normalized.x !== void 0) normalized.x = Math.round(normalized.x);
			if (normalized.y !== void 0) normalized.y = Math.round(normalized.y);
			if (normalized.width !== void 0) normalized.width = Math.round(normalized.width);
			if (normalized.height !== void 0) normalized.height = Math.round(normalized.height);
			const list = ((_prev$sceneElements3 = prev.sceneElements) !== null && _prev$sceneElements3 !== void 0 ? _prev$sceneElements3 : []).map((e) => e.instanceId === instanceId ? _objectSpread2(_objectSpread2({}, e), normalized) : e);
			return _objectSpread2(_objectSpread2({}, prev), {}, { sceneElements: list });
		});
	}, []);
	const handleUpdateItem = useCallback((itemId, patch) => {
		updateDraftSceneElement(itemId, patch);
	}, [updateDraftSceneElement]);
	/**
	* 一次提交多个图元的坐标/尺寸变更（选区分组拖拽时整组落库），单次 `commitDraft`，
	* 一步可整体撤销。锁定图元（layoutTransformLocked）被跳过。
	*/
	const handleUpdateItemsBatch = useCallback((patches) => {
		if (!patches.length) return;
		commitDraft((prev) => {
			var _prev$sceneElements4;
			if (!prev) return prev;
			const byId = new Map(patches.map((p) => [p.id, p.patch]));
			const list = ((_prev$sceneElements4 = prev.sceneElements) !== null && _prev$sceneElements4 !== void 0 ? _prev$sceneElements4 : []).map((e) => {
				const patch = byId.get(e.instanceId);
				if (!patch) return e;
				if (e.layoutTransformLocked) return e;
				const np = _objectSpread2({}, patch);
				if (np.x !== void 0) np.x = Math.round(np.x);
				if (np.y !== void 0) np.y = Math.round(np.y);
				if (np.width !== void 0) np.width = Math.round(np.width);
				if (np.height !== void 0) np.height = Math.round(np.height);
				return _objectSpread2(_objectSpread2({}, e), np);
			});
			return _objectSpread2(_objectSpread2({}, prev), {}, { sceneElements: list });
		});
	}, [commitDraft]);
	const placeOverlayClass = `${layoutPrefix}-place-overlay`;
	/**
	* 在内容坐标系下按「新建图元」模式落 sceneElement（支持 recordIds 批量；点击与拖拽共用）
	* @returns 是否已成功写入草稿（校验失败时为 false）
	*/
	const applyScenePlacementAtCoords = useCallback((contentX, contentY, scene) => {
		var _kindConfig$allowedDa, _ref, _scene$dataSourceKey;
		if (!config || !currentCanvasId) return false;
		const dims = resolveScenePlacementDimensions(config, scene);
		const kindConfig = dims.kindConfig;
		const policy = resolveDataBindingPolicy(kindConfig);
		const allKeys = Object.keys(dataSources !== null && dataSources !== void 0 ? dataSources : {});
		const allowedKeys = (kindConfig === null || kindConfig === void 0 || (_kindConfig$allowedDa = kindConfig.allowedDataSourceKeys) === null || _kindConfig$allowedDa === void 0 ? void 0 : _kindConfig$allowedDa.length) ? kindConfig.allowedDataSourceKeys.filter((k) => allKeys.includes(k)) : allKeys;
		const pickKey = (_ref = (_scene$dataSourceKey = scene.dataSourceKey) !== null && _scene$dataSourceKey !== void 0 ? _scene$dataSourceKey : allowedKeys[0]) !== null && _ref !== void 0 ? _ref : allKeys[0];
		const recordIdsToPlace = scene.recordIds && scene.recordIds.length > 0 ? scene.recordIds : scene.recordId ? [scene.recordId] : [];
		let dataBinding;
		if (policy !== "none" && pickKey) {
			var _dataSources$pickKey;
			const dsList = (_dataSources$pickKey = dataSources === null || dataSources === void 0 ? void 0 : dataSources[pickKey]) !== null && _dataSources$pickKey !== void 0 ? _dataSources$pickKey : [];
			if (dsList.length === 0) dataBinding = {
				dataSourceKey: pickKey,
				recordId: `new_${Date.now()}`
			};
			else if (recordIdsToPlace.length === 0) {
				if (policy === "required") {
					message.warning("请先在侧栏选择数据源与数据行，再落位");
					return false;
				}
			} else if (recordIdsToPlace.length === 1) {
				const rid = recordIdsToPlace[0];
				if (!dsList.find((r) => r.id === rid)) {
					message.error("所选记录不存在，请重新选择数据行");
					return false;
				}
				const cand = {
					dataSourceKey: pickKey,
					recordId: rid
				};
				if (duplicateBinding(config.sceneElements, cand)) {
					message.warning("该数据记录已在平面图中绑定，请勿重复添加");
					return false;
				}
				dataBinding = cand;
			} else {
				const pending = [];
				let skippedDup = 0;
				let skippedMissing = 0;
				const seenInBatch = /* @__PURE__ */ new Set();
				recordIdsToPlace.forEach((rid) => {
					var _row$name;
					const record = dsList.find((r) => r.id === rid);
					if (!record) {
						skippedMissing += 1;
						return;
					}
					const cand = {
						dataSourceKey: pickKey,
						recordId: rid
					};
					if (seenInBatch.has(rid) || duplicateBinding(config.sceneElements, cand)) {
						skippedDup += 1;
						return;
					}
					seenInBatch.add(rid);
					const row = record;
					pending.push({
						binding: cand,
						name: String((_row$name = row === null || row === void 0 ? void 0 : row.name) !== null && _row$name !== void 0 ? _row$name : rid)
					});
				});
				if (skippedDup > 0) message.warning(`已跳过 ${skippedDup} 条已在平面图中绑定的记录`);
				if (skippedMissing > 0) message.warning(`有 ${skippedMissing} 条记录不存在，已跳过`);
				if (pending.length === 0) {
					message.warning("没有可落位的记录");
					return false;
				}
				const sizes = pending.map(() => ({
					width: dims.width,
					height: dims.height
				}));
				const positions = computeBatchScenePlacementPositions(sizes, getBatchPlacementLayoutContext());
				const newEls = pending.map((p, i) => buildPlacedSceneElement({
					canvasId: currentCanvasId,
					scene,
					x: positions[i].x,
					y: positions[i].y,
					name: p.name,
					dataBinding: p.binding
				}, dims));
				commitDraft((prev) => {
					var _prev$sceneElements5;
					return prev ? _objectSpread2(_objectSpread2({}, prev), {}, { sceneElements: [...(_prev$sceneElements5 = prev.sceneElements) !== null && _prev$sceneElements5 !== void 0 ? _prev$sceneElements5 : [], ...newEls] }) : prev;
				});
				scheduleFocusOnPlacements(positions, sizes);
				return true;
			}
		}
		const newEl = buildPlacedSceneElement({
			canvasId: currentCanvasId,
			scene,
			x: contentX,
			y: contentY,
			name: (() => {
				var _kindConfig$defaultPr, _kindConfig$defaultPr2;
				if (dims.isStage) return STAGE_DECO_DEFAULT.name;
				if (dims.isSelectionZone) return locales.getText(I18N_PLACEMENT_SELECTION_ZONE_NAME);
				if (dims.isImage) return locales.getText(I18N_PLACEMENT_FALLBACK_IMAGE);
				const db = dataBinding;
				const rid = recordIdsToPlace[0];
				if (db && rid) {
					var _dataSources$db$dataS, _row$name2;
					const row = dataSources === null || dataSources === void 0 || (_dataSources$db$dataS = dataSources[db.dataSourceKey]) === null || _dataSources$db$dataS === void 0 ? void 0 : _dataSources$db$dataS.find((r) => r.id === rid);
					return String((_row$name2 = row === null || row === void 0 ? void 0 : row.name) !== null && _row$name2 !== void 0 ? _row$name2 : rid);
				}
				return (_kindConfig$defaultPr = kindConfig === null || kindConfig === void 0 || (_kindConfig$defaultPr2 = kindConfig.defaultPresentation) === null || _kindConfig$defaultPr2 === void 0 ? void 0 : _kindConfig$defaultPr2.title) !== null && _kindConfig$defaultPr !== void 0 ? _kindConfig$defaultPr : "";
			})(),
			dataBinding
		}, dims);
		commitDraft((prev) => {
			var _prev$sceneElements6;
			return prev ? _objectSpread2(_objectSpread2({}, prev), {}, { sceneElements: [...(_prev$sceneElements6 = prev.sceneElements) !== null && _prev$sceneElements6 !== void 0 ? _prev$sceneElements6 : [], newEl] }) : prev;
		});
		scheduleFocusOnPlacements([{
			x: contentX,
			y: contentY
		}], [{
			width: dims.width,
			height: dims.height
		}]);
		return true;
	}, [
		config,
		currentCanvasId,
		dataSources,
		getBatchPlacementLayoutContext,
		commitDraft,
		scheduleFocusOnPlacements
	]);
	const handleCanvasPlaceClick = useCallback((e) => {
		if (!placeMode || placeMode.type !== "scene" || !config || !currentCanvasId) return;
		if (!e.target.classList.contains(placeOverlayClass)) return;
		const { x: ox, y: oy } = contentOffsetRef.current;
		if (applyScenePlacementAtCoords(Math.round(e.nativeEvent.offsetX - ox), Math.round(e.nativeEvent.offsetY - oy), placeMode)) setPlaceMode(null);
	}, [
		placeMode,
		config,
		currentCanvasId,
		applyScenePlacementAtCoords,
		placeOverlayClass
	]);
	/** 从左侧图元栏拖拽到画布落位（与点击落点规则一致） */
	const placeSceneElementFromPaletteDrop = useCallback((contentX, contentY, scene) => {
		const ok = applyScenePlacementAtCoords(contentX, contentY, scene);
		if (ok) setPlaceMode(null);
		return ok;
	}, [applyScenePlacementAtCoords]);
	/**
	* 批量落点（弹窗多选、侧栏多选数据行等）：在画布右侧空白区按近方阵网格排列。
	*/
	const bulkPlacePaletteItems = useCallback((items) => {
		if (!items.length || !config || !currentCanvasId) return;
		const scenes = items.map((item) => _objectSpread2({
			type: "scene",
			elementKind: item.elementKind
		}, item.builtinShapeKey ? { builtinShapeKey: item.builtinShapeKey } : {}));
		const dimsList = scenes.map((scene) => resolveScenePlacementDimensions(config, scene));
		const sizes = dimsList.map((d) => ({
			width: d.width,
			height: d.height
		}));
		const positions = computeBatchScenePlacementPositions(sizes, getBatchPlacementLayoutContext());
		const newEls = scenes.map((scene, i) => {
			var _ref2, _dimsList$i$kindConfi, _dimsList$i$kindConfi2, _dimsList$i$kindConfi3;
			return buildPlacedSceneElement({
				canvasId: currentCanvasId,
				scene,
				x: positions[i].x,
				y: positions[i].y,
				name: (_ref2 = (_dimsList$i$kindConfi = (_dimsList$i$kindConfi2 = dimsList[i].kindConfig) === null || _dimsList$i$kindConfi2 === void 0 ? void 0 : _dimsList$i$kindConfi2.label) !== null && _dimsList$i$kindConfi !== void 0 ? _dimsList$i$kindConfi : (_dimsList$i$kindConfi3 = dimsList[i].kindConfig) === null || _dimsList$i$kindConfi3 === void 0 || (_dimsList$i$kindConfi3 = _dimsList$i$kindConfi3.defaultPresentation) === null || _dimsList$i$kindConfi3 === void 0 ? void 0 : _dimsList$i$kindConfi3.title) !== null && _ref2 !== void 0 ? _ref2 : scene.elementKind
			}, dimsList[i]);
		});
		commitDraft((prev) => {
			var _prev$sceneElements7;
			return prev ? _objectSpread2(_objectSpread2({}, prev), {}, { sceneElements: [...(_prev$sceneElements7 = prev.sceneElements) !== null && _prev$sceneElements7 !== void 0 ? _prev$sceneElements7 : [], ...newEls] }) : prev;
		});
		scheduleFocusOnPlacements(positions, sizes);
		setPlaceMode(null);
	}, [
		config,
		currentCanvasId,
		getBatchPlacementLayoutContext,
		commitDraft,
		scheduleFocusOnPlacements
	]);
	const handleSave = useCallback(() => {
		if (!draftConfig || !onSave) return;
		setSaving(true);
		try {
			onSave(draftConfig);
			/** 先本地确认“已提交保存”，让提示即时消失；后续以 props 回写为准 */
			setSavedBaselineConfig(draftConfig);
		} finally {
			setSaving(false);
		}
	}, [draftConfig, onSave]);
	/**
	* 丢弃所有未保存的草稿改动，把 `draftConfig` 重置回上游 `floorMapConfigProp` 的当前值。
	*
	* 用途：编辑态下点击「取消编辑」（不保存退出）时调用，否则 `config` 仍取
	* `draftConfig ?? floorMapConfigProp`，回到阅读态会继续看到编辑后的脏数据。
	*
	* 实现要点：
	* - 与上游 props 同步 effect（line 165）使用相同的规范化路径与 `replaceDraft`，
	*   保证「取消」后状态等价于「上游 props 重新落地一次」；
	* - `replaceDraft` 内部已清空 undo/redo 历史栈，避免取消后又能 redo 出旧 draft；
	* - 同步清空 `savedBaselineConfig`，让 dirty 判定回到「以上游 props 为基线」。
	*/
	const discardDraft = useCallback(() => {
		if (!floorMapConfigProp) {
			replaceDraft(null);
			lastSyncedPropConfigRef.current = null;
			setSavedBaselineConfig(null);
			return;
		}
		const nextConfig = normalizeFloorMapConfigWithStoredGrid(floorMapConfigProp);
		replaceDraft(nextConfig);
		lastSyncedPropConfigRef.current = nextConfig;
		setSavedBaselineConfig(null);
	}, [floorMapConfigProp, replaceDraft]);
	/** 切换当前画布草稿中的 mapLayer.showGrid（随布局保存持久化） */
	const toggleDraftCanvasShowGrid = useCallback(() => {
		commitDraft((prev) => {
			if (!prev || !currentCanvasId) return prev;
			return _objectSpread2(_objectSpread2({}, prev), {}, { canvases: prev.canvases.map((c) => {
				var _c$mapLayer;
				if (c.id !== currentCanvasId) return c;
				const nextShow = !Boolean((_c$mapLayer = c.mapLayer) === null || _c$mapLayer === void 0 ? void 0 : _c$mapLayer.showGrid);
				writeStoredGridVisible(currentCanvasId, nextShow);
				return _objectSpread2(_objectSpread2({}, c), {}, { mapLayer: _objectSpread2(_objectSpread2({}, c.mapLayer), {}, { showGrid: nextShow }) });
			}) });
		});
	}, [currentCanvasId]);
	const handleEditPanelUpdate = useCallback((patch) => {
		if (!selectedId) return;
		const bindingPatch = patch.dataBinding;
		if ((bindingPatch === null || bindingPatch === void 0 ? void 0 : bindingPatch.dataSourceKey) && (bindingPatch === null || bindingPatch === void 0 ? void 0 : bindingPatch.recordId)) {
			var _configRef$current;
			const cand = bindingPatch;
			if (duplicateBinding((_configRef$current = configRef.current) === null || _configRef$current === void 0 ? void 0 : _configRef$current.sceneElements, cand, selectedId)) {
				message.warning("该数据记录已被其他图元绑定");
				return;
			}
		}
		updateDraftSceneElement(selectedId, patch);
	}, [selectedId, updateDraftSceneElement]);
	const handleSaveRecord = useCallback((dataSourceKey, id, newData) => {
		onDataSourceRecordSave === null || onDataSourceRecordSave === void 0 || onDataSourceRecordSave(dataSourceKey, id, newData);
		setSavingRecord(true);
		setTimeout(() => setSavingRecord(false), 500);
	}, [onDataSourceRecordSave]);
	const handleEditPanelDelete = useCallback(() => {
		if (!selectedId) return;
		commitDraft((prev) => {
			var _prev$sceneElements8, _prev$edges;
			if (!prev) return prev;
			return _objectSpread2(_objectSpread2({}, prev), {}, {
				sceneElements: ((_prev$sceneElements8 = prev.sceneElements) !== null && _prev$sceneElements8 !== void 0 ? _prev$sceneElements8 : []).filter((e) => e.instanceId !== selectedId),
				edges: ((_prev$edges = prev.edges) !== null && _prev$edges !== void 0 ? _prev$edges : []).filter((edge) => edge.sourceId !== selectedId && edge.targetId !== selectedId)
			});
		});
		setSelectedId(null);
	}, [selectedId, commitDraft]);
	/** 与编辑侧栏锁定按钮同源：切换 `layoutTransformLocked` */
	const toggleSelectedLayoutLock = useCallback(() => {
		var _configRef$current2;
		if (!selectedId) return;
		const el = (_configRef$current2 = configRef.current) === null || _configRef$current2 === void 0 || (_configRef$current2 = _configRef$current2.sceneElements) === null || _configRef$current2 === void 0 ? void 0 : _configRef$current2.find((e) => e.instanceId === selectedId);
		updateDraftSceneElement(selectedId, { layoutTransformLocked: !(el === null || el === void 0 ? void 0 : el.layoutTransformLocked) });
	}, [selectedId, updateDraftSceneElement]);
	/**
	* 复制选中图元：新实例 + 位移，**清除 dataBinding**（避免与侧栏一致的「同一记录不可重复绑定」冲突）。
	*/
	const duplicateSelectedElement = useCallback(() => {
		var _prev$sceneElements9, _el$x, _el$y, _prev$sceneElements10;
		if (!selectedId) return;
		const prev = configRef.current;
		if (!prev) return;
		const el = ((_prev$sceneElements9 = prev.sceneElements) !== null && _prev$sceneElements9 !== void 0 ? _prev$sceneElements9 : []).find((e) => e.instanceId === selectedId);
		if (!el) return;
		const newId = newInstanceId();
		const copy = _objectSpread2(_objectSpread2({}, el), {}, {
			instanceId: newId,
			x: Math.round(((_el$x = el.x) !== null && _el$x !== void 0 ? _el$x : 0) + DUPLICATE_PLACE_OFFSET),
			y: Math.round(((_el$y = el.y) !== null && _el$y !== void 0 ? _el$y : 0) + DUPLICATE_PLACE_OFFSET),
			dataBinding: void 0,
			layoutTransformLocked: false
		});
		commitDraft(_objectSpread2(_objectSpread2({}, prev), {}, { sceneElements: [...(_prev$sceneElements10 = prev.sceneElements) !== null && _prev$sceneElements10 !== void 0 ? _prev$sceneElements10 : [], copy] }));
		setSelectedId(newId);
	}, [selectedId]);
	const selectedItem = selectedId ? mergedItems.find((m) => m.id === selectedId) : null;
	return {
		config,
		currentCanvasId,
		setCurrentCanvasId,
		selectedId,
		setSelectedId,
		placeMode,
		setPlaceMode,
		mergedItems,
		items,
		saving,
		savingRecord,
		handleAddCanvas,
		handleRemoveCanvas,
		handleRenameCanvas,
		handleUpdateItem,
		handleUpdateItemsBatch,
		handleCanvasPlaceClick,
		handleSave,
		discardDraft,
		placeSceneElementFromPaletteDrop,
		bulkPlacePaletteItems,
		editPanelParams: selectedItem && config && selectedId && !((_selectedItem$_sceneE = selectedItem._sceneElement) === null || _selectedItem$_sceneE === void 0 ? void 0 : _selectedItem$_sceneE.layoutTransformLocked) ? {
			itemId: selectedId,
			sceneElement: selectedItem._sceneElement,
			onUpdate: handleEditPanelUpdate,
			onSaveRecord: onDataSourceRecordSave ? handleSaveRecord : void 0,
			onClose: () => setSelectedId(null),
			onDelete: config.canvasUi.canEdit ? handleEditPanelDelete : void 0,
			recordFormColumns: dataSourceFormColumns,
			dataSourceRecordForm,
			dataSourceLabels,
			layoutDraftDirty: isLayoutDirty,
			showDataSourceRecordFormBlock
		} : null,
		placeOverlayClass,
		isLayoutDirty,
		toggleDraftCanvasShowGrid,
		toggleSelectedLayoutLock,
		duplicateSelectedElement,
		deleteSelectedElement: handleEditPanelDelete,
		undoDraft,
		redoDraft,
		canUndo,
		canRedo,
		commitDraft
	};
}
//#endregion
export { useFloorMapEditState };
