import { _objectSpread2 } from "../../_virtual/_@oxc-project_runtime@0.122.0/helpers/objectSpread2.js";
import { _asyncToGenerator } from "../../_virtual/_@oxc-project_runtime@0.122.0/helpers/asyncToGenerator.js";
import useEngineContext from "../../hooks/useEngineContext.js";
import { request } from "../../utils/index.js";
import locales_default from "./locales.js";
import { getTopologyCanvasDevicesName, normalizeTopologyLocaleMaps, resolveTopologyDisplayLocale } from "./topologyLocale.js";
import { TopologyDeviceCard } from "./TopologyDeviceCard.js";
import { TopologyFloorMapEditToggle } from "./components/TopologyFloorMapEditToggle.js";
import { TopologyConnectionLegend } from "./components/TopologyConnectionLegend.js";
import { TOPOLOGY_MAIN_CANVAS_ID } from "./topologyFloorPlanConstants.js";
import { TOPOLOGY_DEVICE_KIND_VALUES, extractRawDeviceConnectionList, extractRawDeviceList, reconcileTopologyView } from "./topologyDeviceListView.js";
import { fetchTopologyDeviceConnectionList, fetchTopologyDeviceList } from "./topologyDeviceApi.js";
import { saveViewConfigToShopFloorPlan, toastFloorPlanSaveError } from "./floorPlan/topologyFloorPlanApi.js";
import { useTopologyFloorPlan } from "./floorPlan/useTopologyFloorPlan.js";
import { resolveAppHelperIsWebPos } from "./floorPlan/resolveAppHelperIsWebPos.js";
import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { locales } from "@pisell/utils";
import { FLOOR_MAP_SELECTION_ZONE_ELEMENT_KIND, FloorMapSelectionZoneElement, PisellFloorMapLayout, initFloorMapLayoutLocales, wrapFloorMapOnSaveWithRemotePersist } from "@pisell/materials";
import classNames from "classnames";
import "./PisellTopology.less";
//#region src/plus/pisellTopology/PisellTopology.tsx
/**
* PisellTopology - 门店设备拓扑图
*
* 当前默认行为：
* - 挂载时调用 `GET /tenant/device` 拉取设备数组（不再读 floor-plan）
* - 按设备 `type_code` 动态生成 elementKinds，并以近似正方形网格自动布局
* - 永远阅读模式，关闭编辑/连线/调色板
*/
const PREFIX = "pisell-topology";
/** Story / 无引擎时占位，避免 request.getRequest 未注入报错 */
const PREVIEW_HTTP_CLIENT = {
	get: function() {
		var _ref = _asyncToGenerator(function* () {
			return { data: [] };
		});
		return function get() {
			return _ref.apply(this, arguments);
		};
	}(),
	post: function() {
		var _ref2 = _asyncToGenerator(function* () {
			return { data: {} };
		});
		return function post() {
			return _ref2.apply(this, arguments);
		};
	}(),
	put: function() {
		var _ref3 = _asyncToGenerator(function* () {
			return { data: {} };
		});
		return function put() {
			return _ref3.apply(this, arguments);
		};
	}(),
	remove: function() {
		var _ref4 = _asyncToGenerator(function* () {
			return { data: {} };
		});
		return function remove() {
			return _ref4.apply(this, arguments);
		};
	}(),
	custom: function() {
		var _ref5 = _asyncToGenerator(function* () {
			return { data: {} };
		});
		return function custom() {
			return _ref5.apply(this, arguments);
		};
	}()
};
/** 给所有 `topologyDevice:*` kind 共用一份 device card 渲染函数（图标内置兜底） */
function buildDeviceRenderItemByKind(kindValues, devicesById) {
	const map = {};
	map[FLOOR_MAP_SELECTION_ZONE_ELEMENT_KIND] = (item) => {
		var _item$name, _item$_sceneElement;
		return /* @__PURE__ */ React.createElement(FloorMapSelectionZoneElement, { name: (_item$name = item.name) !== null && _item$name !== void 0 ? _item$name : (_item$_sceneElement = item._sceneElement) === null || _item$_sceneElement === void 0 ? void 0 : _item$_sceneElement.name });
	};
	for (const kind of kindValues) map[kind] = (item) => {
		var _item$_sceneElement2;
		const binding = (_item$_sceneElement2 = item._sceneElement) === null || _item$_sceneElement2 === void 0 ? void 0 : _item$_sceneElement2.dataBinding;
		const device = (binding === null || binding === void 0 ? void 0 : binding.recordId) != null ? devicesById.get(String(binding.recordId)) : void 0;
		return /* @__PURE__ */ React.createElement(TopologyDeviceCard, {
			item,
			device
		});
	};
	return map;
}
function PisellTopology(props) {
	var _engineCtx$appHelper, _reconciled$dataSourc;
	const { className, style, devices: devicesProp, overviewHud: overviewHudProp, mode: propMode = "read", floorPlanCode: floorPlanCodeProp, floorPlanCanvasWidth, floorPlanCanvasHeight, floorPlanName, floorPlanSort, floorPlanStatus, putFloorPlan, onFloorPlanPersisted } = props;
	const floorPlanCode = (floorPlanCodeProp === null || floorPlanCodeProp === void 0 ? void 0 : floorPlanCodeProp.trim()) || "pisell_topology";
	const overviewHud = overviewHudProp === false ? false : overviewHudProp === true || overviewHudProp === void 0 ? void 0 : overviewHudProp;
	const engineCtx = useEngineContext();
	const displayLocale = resolveTopologyDisplayLocale(engineCtx);
	const { request: engineRequest, isWebPos: isWebPosRaw } = (engineCtx === null || engineCtx === void 0 || (_engineCtx$appHelper = engineCtx.appHelper) === null || _engineCtx$appHelper === void 0 ? void 0 : _engineCtx$appHelper.utils) || {};
	const subscribeFloorPlanPush = resolveAppHelperIsWebPos(isWebPosRaw);
	useEffect(() => {
		locales.init(normalizeTopologyLocaleMaps(locales_default), displayLocale);
		initFloorMapLayoutLocales(displayLocale);
	}, [displayLocale]);
	request.setRequest(engineRequest !== null && engineRequest !== void 0 ? engineRequest : PREVIEW_HTTP_CLIENT);
	useEffect(() => {
		if (engineRequest) request.setRequest(engineRequest);
	}, [engineRequest]);
	/** 接口拉到的原始设备数组；外部传 devices 时直接走 props（兼容低代码 mock） */
	const [rawDevices, setRawDevices] = useState([]);
	/** 设备连接（边）数组，挂载时和 devices 一并请求 */
	const [rawConnections, setRawConnections] = useState([]);
	/**
	* 设备列表请求是否已结束（成功或失败）。
	* 在 floor-plan 与 devices 都就绪前不把「空 reconcile 结果」灌进 FloorMapLayout，
	* 否则编辑态草稿会先固化成空白画布。
	*/
	const [devicesFetchSettled, setDevicesFetchSettled] = useState(() => Boolean(devicesProp && devicesProp.length > 0));
	useEffect(() => {
		if (devicesProp && devicesProp.length > 0) {
			setRawConnections([]);
			setDevicesFetchSettled(true);
			return;
		}
		setDevicesFetchSettled(false);
		let cancelled = false;
		/**
		* 并发拉设备列表 + 连接列表：连接里的 source/target 都是设备 id，
		* 所以只要两个接口都返回，就能把 edges 投到节点上一起渲染。
		*/
		Promise.all([fetchTopologyDeviceList(), fetchTopologyDeviceConnectionList()]).then(([deviceRes, connectionRes]) => {
			if (cancelled) return;
			const devices = extractRawDeviceList(deviceRes.response);
			const conns = extractRawDeviceConnectionList(connectionRes.response);
			setRawDevices(devices);
			setRawConnections(conns);
		}).catch((error) => {
			if (cancelled) return;
			console.error("[PisellTopology] 拉取设备 / 连接失败:", error);
			setRawDevices([]);
			setRawConnections([]);
		}).finally(() => {
			if (!cancelled) setDevicesFetchSettled(true);
		});
		return () => {
			cancelled = true;
		};
	}, [devicesProp]);
	/** 外部 devices（TopologyDeviceRow[]）走兼容路径：把它们当作已经转换好的设备记录 */
	const propRawDevices = useMemo(() => {
		if (!devicesProp || devicesProp.length === 0) return [];
		return devicesProp.map((row, index) => {
			var _row$id, _row$name, _row$sn, _row$status;
			return {
				id: (_row$id = row.id) !== null && _row$id !== void 0 ? _row$id : `prop-${index}`,
				type_code: "unknown",
				name: (_row$name = row.name) !== null && _row$name !== void 0 ? _row$name : null,
				model: row.model ? { name: { original: row.model } } : null,
				number: (_row$sn = row.sn) !== null && _row$sn !== void 0 ? _row$sn : null,
				status: (_row$status = row.status) !== null && _row$status !== void 0 ? _row$status : null
			};
		});
	}, [devicesProp]);
	const effectiveRawDevices = propRawDevices.length > 0 ? propRawDevices : rawDevices;
	/** 内部编辑切换：mode='read' 时受画布编辑按钮控制；mode='edit' 时强制编辑 */
	const [mapEditInternal, setMapEditInternal] = useState(false);
	const effectiveMode = propMode === "edit" || mapEditInternal ? "edit" : "read";
	const forceEditFromProps = propMode === "edit";
	/**
	* Floor-plan：GET + 404 自动创建 + 对账基线（与 isWebPos 无关）。
	* 仅 WebPOS 额外走按 code 的 OS 推送订阅（卸载 unsubscribe）。
	* 外部传 `devices` 时跳过（低代码预览 / mock）。
	*/
	const enableFloorPlanSync = !devicesProp || devicesProp.length === 0;
	const { floorPlanRemoteId, savedSceneElements, savedEdges, isLoaded: floorPlanLoaded, applyLocalSave, setRemoteFloorPlanView } = useTopologyFloorPlan({
		floorPlanCode,
		enabled: enableFloorPlanSync,
		subscribeFloorPlanPush
	});
	/** floor-plan + 设备列表都就绪后再 reconcile，避免先渲染空白画布 */
	const isViewReady = devicesFetchSettled && (!enableFloorPlanSync || floorPlanLoaded);
	/**
	* 三态对账：rawDevices + rawConnections + savedSceneElements/Edges → 最终 config。
	* 不开启 floor-plan 同步时（外部 devices 模式）走 savedSceneElements=undefined 的
	* 全量 auto-layout 分支，行为与之前一致。
	*/
	const reconciled = useMemo(() => {
		if (!isViewReady) return null;
		return reconcileTopologyView({
			rawDevices: effectiveRawDevices,
			rawConnections,
			savedSceneElements: enableFloorPlanSync ? savedSceneElements : void 0,
			savedEdges: enableFloorPlanSync ? savedEdges : void 0,
			displayLocale
		});
	}, [
		isViewReady,
		effectiveRawDevices,
		rawConnections,
		enableFloorPlanSync,
		savedSceneElements,
		savedEdges,
		displayLocale
	]);
	const baseFloorConfig = useMemo(() => {
		const canEditCanvas = effectiveMode === "edit";
		const core = reconciled === null || reconciled === void 0 ? void 0 : reconciled.config;
		if (!core) return {
			canvases: [{
				id: TOPOLOGY_MAIN_CANVAS_ID,
				name: getTopologyCanvasDevicesName(displayLocale),
				order: 0,
				enabled: true,
				mapLayer: {
					showGrid: false,
					backgroundColor: "#ffffff"
				}
			}],
			canvasUi: {
				canCreate: canEditCanvas,
				canEdit: canEditCanvas
			},
			elementKinds: [],
			sceneElements: [],
			edges: []
		};
		return _objectSpread2(_objectSpread2({}, core), {}, { canvasUi: {
			canCreate: canEditCanvas,
			canEdit: canEditCanvas
		} });
	}, [
		reconciled,
		effectiveMode,
		displayLocale
	]);
	const dataSources = (_reconciled$dataSourc = reconciled === null || reconciled === void 0 ? void 0 : reconciled.dataSources) !== null && _reconciled$dataSourc !== void 0 ? _reconciled$dataSourc : { devices: [] };
	/**
	* 自动持久化：新设备首次落到画布上时，把 reconciled config 写回 floor-plan，
	* 让下一轮反查就能命中已保存基线、不再被识别为「新增」。
	*
	* 防抖 / 防循环：
	* - 仅在 floor-plan 已加载、有 remoteId、确实新增了设备 时触发
	* - 用 ref 记下「正在保存的 reconciled.config」与「上次成功保存的 sceneElements 引用」
	*   避免同一个 reconciled 反复触发
	*/
	const inflightSaveRef = useRef(null);
	useEffect(() => {
		var _reconciled$config$sc, _reconciled$config$sc2, _reconciled$config$sc3, _reconciled$config$ed;
		if (!enableFloorPlanSync) return;
		if (!isViewReady || !reconciled) return;
		if (!floorPlanLoaded) return;
		if (floorPlanRemoteId == null) return;
		if (effectiveRawDevices.length === 0) return;
		if (((_reconciled$config$sc = (_reconciled$config$sc2 = reconciled.config.sceneElements) === null || _reconciled$config$sc2 === void 0 ? void 0 : _reconciled$config$sc2.length) !== null && _reconciled$config$sc !== void 0 ? _reconciled$config$sc : 0) === 0) return;
		if (reconciled.addedDeviceIds.length === 0) return;
		/** 用户正在编辑：不抢用户的未提交变更 */
		if (effectiveMode === "edit") return;
		const token = Symbol("topology-autosave");
		inflightSaveRef.current = token;
		const sceneSnapshot = (_reconciled$config$sc3 = reconciled.config.sceneElements) !== null && _reconciled$config$sc3 !== void 0 ? _reconciled$config$sc3 : [];
		const edgeSnapshot = (_reconciled$config$ed = reconciled.config.edges) !== null && _reconciled$config$ed !== void 0 ? _reconciled$config$ed : [];
		saveViewConfigToShopFloorPlan(floorPlanRemoteId, reconciled.config, {
			canvasWidth: floorPlanCanvasWidth,
			canvasHeight: floorPlanCanvasHeight,
			code: floorPlanCode,
			name: floorPlanName,
			sort: floorPlanSort,
			status: floorPlanStatus,
			putFloorPlan: putFloorPlan != null ? (planId, body) => putFloorPlan(planId, body) : void 0
		}).then(() => {
			if (inflightSaveRef.current !== token) return;
			applyLocalSave({
				sceneElements: sceneSnapshot,
				edges: edgeSnapshot
			});
			onFloorPlanPersisted === null || onFloorPlanPersisted === void 0 || onFloorPlanPersisted();
		}).catch((err) => {
			console.warn("[PisellTopology] auto-save floor-plan failed:", err);
		});
	}, [
		enableFloorPlanSync,
		isViewReady,
		floorPlanLoaded,
		floorPlanRemoteId,
		reconciled,
		effectiveRawDevices.length,
		effectiveMode,
		floorPlanCode,
		floorPlanCanvasWidth,
		floorPlanCanvasHeight,
		floorPlanName,
		floorPlanSort,
		floorPlanStatus,
		putFloorPlan,
		onFloorPlanPersisted,
		applyLocalSave
	]);
	const devicesById = useMemo(() => {
		var _ref6;
		const list = (_ref6 = dataSources["devices"]) !== null && _ref6 !== void 0 ? _ref6 : [];
		return new Map(list.map((d) => [d.id, d]));
	}, [dataSources]);
	const renderItemByKind = useMemo(() => buildDeviceRenderItemByKind(TOPOLOGY_DEVICE_KIND_VALUES, devicesById), [devicesById]);
	const floorMapLayoutRef = useRef(null);
	/**
	* 用户点编辑模式 toolbar 的「保存」时：把当前视图持久化到 floor-plan，
	* 并把保存视图作为新基线写回（保证下一轮 reconcile 不再当作「新增」）。
	*/
	const onSaveWithApi = useMemo(() => {
		const persistTargetId = floorPlanRemoteId;
		return wrapFloorMapOnSaveWithRemotePersist({
			canPersist: enableFloorPlanSync && persistTargetId != null && Number.isFinite(persistTargetId) && persistTargetId > 0,
			persistTargetId: persistTargetId !== null && persistTargetId !== void 0 ? persistTargetId : 0,
			persist: (id, config) => saveViewConfigToShopFloorPlan(id, config, {
				canvasWidth: floorPlanCanvasWidth,
				canvasHeight: floorPlanCanvasHeight,
				code: floorPlanCode,
				name: floorPlanName,
				sort: floorPlanSort,
				status: floorPlanStatus,
				putFloorPlan: putFloorPlan != null ? (planId, body) => putFloorPlan(planId, body) : void 0
			}),
			setRemoteFloorPlanView,
			onFloorPlanPersisted,
			prevOnSave: props.onSave,
			onPersistError: toastFloorPlanSaveError
		});
	}, [
		enableFloorPlanSync,
		floorPlanRemoteId,
		floorPlanCanvasWidth,
		floorPlanCanvasHeight,
		floorPlanCode,
		floorPlanName,
		floorPlanSort,
		floorPlanStatus,
		putFloorPlan,
		setRemoteFloorPlanView,
		onFloorPlanPersisted,
		props.onSave
	]);
	/**
	* 编辑态「自动排列」：
	* 用 connection-aware 全量布局（忽略已保存位置）重排所有真实设备，把新版 config
	* 灌入画布草稿（进 undo 栈，可撤销）。**不**直接持久化到 floor-plan，
	* 等用户手动点「完成编辑」时再走原有 `onSaveWithApi` 流程保存。
	*/
	const handleAutoArrange = useCallback(() => {
		var _floorMapLayoutRef$cu, _floorMapLayoutRef$cu2;
		if (effectiveRawDevices.length === 0) return;
		const { config } = reconcileTopologyView({
			rawDevices: effectiveRawDevices,
			rawConnections,
			displayLocale
		});
		/** 保留当前画布的 canvasUi（编辑权限）等运行时配置，仅替换 sceneElements / edges 等几何信息 */
		const next = _objectSpread2(_objectSpread2(_objectSpread2({}, baseFloorConfig), config), {}, { canvasUi: baseFloorConfig.canvasUi });
		(_floorMapLayoutRef$cu = floorMapLayoutRef.current) === null || _floorMapLayoutRef$cu === void 0 || (_floorMapLayoutRef$cu2 = _floorMapLayoutRef$cu.applySceneSnapshot) === null || _floorMapLayoutRef$cu2 === void 0 || _floorMapLayoutRef$cu2.call(_floorMapLayoutRef$cu, next);
	}, [
		effectiveRawDevices,
		rawConnections,
		baseFloorConfig,
		displayLocale
	]);
	return /* @__PURE__ */ React.createElement("div", { className: `${PREFIX}-root` }, /* @__PURE__ */ React.createElement("div", { className: `${PREFIX}-legend-anchor` }, /* @__PURE__ */ React.createElement(TopologyConnectionLegend, null)), /* @__PURE__ */ React.createElement(PisellFloorMapLayout, {
		ref: floorMapLayoutRef,
		className: classNames(PREFIX, className),
		style: _objectSpread2({
			width: "100%",
			height: "100%",
			minHeight: 0,
			flex: 1
		}, style),
		items: [],
		renderItem: () => null,
		floorMapConfig: baseFloorConfig,
		dataSources,
		loading: !isViewReady,
		mode: effectiveMode,
		itemUnit: "pixel",
		enableEdges: true,
		enableHover: false,
		toolbarShowSave: effectiveMode === "edit",
		onSave: onSaveWithApi,
		renderItemByKind,
		floorMapViewportOverlay: overviewHud,
		viewportOverlayPlacement: "top-start",
		controls: {
			showZoom: true,
			showFitBounds: true,
			showReset: true,
			showGridToggle: false,
			horizontalAlign: "end"
		},
		onExitEdit: forceEditFromProps ? void 0 : () => setMapEditInternal(false),
		viewportDockTrailing: /* @__PURE__ */ React.createElement(TopologyFloorMapEditToggle, {
			mapEditInternal,
			setMapEditInternal,
			floorMapLayoutRef,
			forceEditMode: forceEditFromProps,
			onAutoArrange: enableFloorPlanSync ? handleAutoArrange : void 0
		}),
		pan: { enabled: true },
		zoom: {
			autoFitOnMount: true,
			min: .25,
			max: 2
		}
	}));
}
const PisellTopologyCore = PisellTopology;
//#endregion
export { PisellTopology, PisellTopologyCore };
