import { normalizeSalesResourceBookingListResult } from "../data/reservationTablesMerge.js";
import { normalizeSalesTimelineHighlightsRaw, reservationTimelineRangeStrings } from "../data/reservationDensity.js";
import dayjs from "dayjs";
import { useEffect, useMemo, useRef, useState } from "react";
//#region src/plus/pisellReservation/hooks/useReservationSalesHostData.ts
/**
* 内置列表且资源台 + floorDayBooking 均就绪后，向宿主 sales 拉两套数据：
* - `getResourceBookingList`：游标时刻下的预约子集，参与合并进平面图 dataSources；
* - `getTimelineHighlights`：时间轴密度折线（无则退回 PisellReservation 内本地计算）。
*
* **跟随当前**：`followWallClock === true` 时第三个参数 `currentTimeStr` 与 `timeStr` 相同（均来自 `debouncedSchedule.at`），
* 避免与墙钟 `dayjs()` 差一分钟；手动关闭跟随后仍传实时 `dayjs()`，保留「游标 vs 此刻」双通道语义。
*/
function useReservationSalesHostData(options) {
	const { isInternalListData, pisellosRef, hasPisellosGetModule, debouncedSchedule, followWallClock, resourceBoardLoading, floorDayBookingLoading, floorDayBookingData, floorDayBookingDataVersion, scheduleDate, timeNavigatorPropsProp, timelineRange, operatingDayBoundary } = options;
	const [salesResourceBookings, setSalesResourceBookings] = useState(null);
	const salesBookingRequestRef = useRef(0);
	/** 非宿主 / sales 子模块拒绝时避免同一「日程 + 列表快照」下反复打接口与刷 console */
	const resourceListHostSkipRef = useRef(false);
	const resourceListRetryGateRef = useRef("");
	const debouncedAtMs = (debouncedSchedule === null || debouncedSchedule === void 0 ? void 0 : debouncedSchedule.at) != null && dayjs(debouncedSchedule.at).isValid() ? dayjs(debouncedSchedule.at).valueOf() : null;
	/**
	* 勿把 `floorDayBookingData` 数组引用直接放进 effect：useRequest 常给新的 `[]`，会触发死循环。
	*
	* 旧实现只采样头 24 条 id，导致 WS 推送只更新中后段 / 仅改字段不动顺序时，dep key 不变，
	* effect 不重跑 → `salesResourceBookings` 无法刷新 → 画布看上去"推送丢了 / 不更新"。
	* 这里改成对全量遍历一次，把每条的 booking_id + 关键状态字段拼进一个 cheap 字符串 hash，
	* 比对时只是字符串相等，性能仍是 O(N) 一次纯字符串拼接，万级也只在 ms 级。
	*/
	const floorDayBookingDepKey = useMemo(() => {
		const d = floorDayBookingData;
		if (!Array.isArray(d) || d.length === 0) return "__empty__";
		/**
		* 用 djb2 的轻量变种逐字节积分，避免 join 长字符串占内存：
		*   h = ((h << 5) + h) ^ ch
		* 对 booking_id + status + 主要时间 + 支付态做指纹，其余字段视为不影响合并 / 画布。
		*/
		let h = 5381;
		const accChar = (s) => {
			for (let i = 0; i < s.length; i++) h = (h << 5) + h ^ s.charCodeAt(i) | 0;
			h = (h << 5) + h + 31 | 0;
		};
		for (let i = 0; i < d.length; i++) {
			var _ref, _ref2, _row$booking_id, _row$status, _ref3, _row$start_time, _ref4, _row$end_time, _ref5, _order$payment_status;
			const row = d[i];
			if (!row) {
				accChar("null");
				continue;
			}
			accChar(String((_ref = (_ref2 = (_row$booking_id = row.booking_id) !== null && _row$booking_id !== void 0 ? _row$booking_id : row.id) !== null && _ref2 !== void 0 ? _ref2 : row.bookingId) !== null && _ref !== void 0 ? _ref : ""));
			accChar(String((_row$status = row.status) !== null && _row$status !== void 0 ? _row$status : ""));
			accChar(String((_ref3 = (_row$start_time = row.start_time) !== null && _row$start_time !== void 0 ? _row$start_time : row.start_date) !== null && _ref3 !== void 0 ? _ref3 : ""));
			accChar(String((_ref4 = (_row$end_time = row.end_time) !== null && _row$end_time !== void 0 ? _row$end_time : row.end_date) !== null && _ref4 !== void 0 ? _ref4 : ""));
			const order = row.order;
			accChar(String((_ref5 = (_order$payment_status = order === null || order === void 0 ? void 0 : order.payment_status) !== null && _order$payment_status !== void 0 ? _order$payment_status : row.order_payment_status) !== null && _ref5 !== void 0 ? _ref5 : ""));
		}
		return `${d.length}:${h}`;
	}, [floorDayBookingData]);
	const floorDayBookingDataRef = useRef(floorDayBookingData);
	floorDayBookingDataRef.current = floorDayBookingData;
	useEffect(() => {
		const pisellosNow = pisellosRef.current;
		if (!isInternalListData) {
			setSalesResourceBookings((p) => p == null ? p : null);
			return;
		}
		if (!(pisellosNow === null || pisellosNow === void 0 ? void 0 : pisellosNow.getModule)) {
			setSalesResourceBookings((p) => p == null ? p : null);
			return;
		}
		if (!(debouncedSchedule === null || debouncedSchedule === void 0 ? void 0 : debouncedSchedule.at) || !dayjs(debouncedSchedule.at).isValid()) return;
		if (resourceBoardLoading || floorDayBookingLoading) return;
		const list = floorDayBookingDataRef.current;
		if (!Array.isArray(list)) return;
		const retryGateKey = `${debouncedAtMs !== null && debouncedAtMs !== void 0 ? debouncedAtMs : "na"}|${floorDayBookingDepKey}|v${floorDayBookingDataVersion}|${hasPisellosGetModule ? 1 : 0}|fw${followWallClock ? 1 : 0}`;
		if (retryGateKey !== resourceListRetryGateRef.current) {
			resourceListRetryGateRef.current = retryGateKey;
			resourceListHostSkipRef.current = false;
		}
		if (resourceListHostSkipRef.current) return;
		let salesModule;
		try {
			salesModule = pisellosNow.getModule("sales");
		} catch (_unused) {
			setSalesResourceBookings((p) => p == null ? p : null);
			return;
		}
		const fn = salesModule === null || salesModule === void 0 ? void 0 : salesModule.getResourceBookingList;
		if (typeof fn !== "function") {
			setSalesResourceBookings((p) => p == null ? p : null);
			return;
		}
		const reqId = ++salesBookingRequestRef.current;
		/**
		* 不再在请求开始时 `setSalesResourceBookings(null)`，避免「画布闪空 → 等宿主返回 → 再切回」的中间态闪烁。
		* stale-while-revalidate：保留旧值给画布继续渲染，等新值到达后再 swap；
		* 出错时再回退到本地全量过滤（让 buildReservationMergedTableRows 用 floorDayBookingList）。
		*/
		const resourceBookingListTimeFormat = "YYYY-MM-DD HH:mm";
		const timeStr = dayjs(debouncedSchedule.at).format(resourceBookingListTimeFormat);
		const currentTimeStr = followWallClock ? timeStr : dayjs().format(resourceBookingListTimeFormat);
		const applyHostList = (raw) => {
			if (salesBookingRequestRef.current !== reqId) return;
			setSalesResourceBookings(normalizeSalesResourceBookingListResult(raw));
			resourceListHostSkipRef.current = false;
		};
		const revertToLocalFilter = () => {
			if (salesBookingRequestRef.current !== reqId) return;
			setSalesResourceBookings((p) => p == null ? p : null);
		};
		try {
			const result = fn.call(salesModule, timeStr, list, currentTimeStr);
			if (result != null && typeof result.then === "function") result.then(applyHostList).catch((e) => {
				resourceListHostSkipRef.current = true;
				if (process.env.NODE_ENV !== "production") console.warn("[PisellReservation] sales.getResourceBookingList rejected（非宿主或 sales 未就绪时将不再重试，直至日程或列表数据变化）", e);
				revertToLocalFilter();
			});
			else applyHostList(result);
		} catch (e) {
			resourceListHostSkipRef.current = true;
			if (process.env.NODE_ENV !== "production") console.warn("[PisellReservation] sales.getResourceBookingList error", e);
			revertToLocalFilter();
		}
	}, [
		isInternalListData,
		hasPisellosGetModule,
		resourceBoardLoading,
		floorDayBookingLoading,
		floorDayBookingDepKey,
		floorDayBookingDataVersion,
		debouncedAtMs,
		followWallClock,
		pisellosRef
	]);
	const [salesTimelineHighlights, setSalesTimelineHighlights] = useState(null);
	const timelineHighlightsReqRef = useRef(0);
	const timelineHighlightsMissingLoggedRef = useRef(false);
	const scheduleDayMs = dayjs(scheduleDate).startOf("day").valueOf();
	useEffect(() => {
		if (!isInternalListData) setSalesTimelineHighlights(null);
	}, [isInternalListData]);
	useEffect(() => {
		if (!isInternalListData) return;
		const pisellosNow = pisellosRef.current;
		if (!(pisellosNow === null || pisellosNow === void 0 ? void 0 : pisellosNow.getModule)) return;
		if (resourceBoardLoading || floorDayBookingLoading) return;
		const bookingList = floorDayBookingDataRef.current;
		if (!Array.isArray(bookingList)) return;
		let salesModule;
		try {
			salesModule = pisellosNow.getModule("sales");
		} catch (_unused2) {
			return;
		}
		const highlightsFn = salesModule === null || salesModule === void 0 ? void 0 : salesModule.getTimelineHighlights;
		if (typeof highlightsFn !== "function") {
			setSalesTimelineHighlights(null);
			if (!timelineHighlightsMissingLoggedRef.current) {
				timelineHighlightsMissingLoggedRef.current = true;
				console.warn("[PisellReservation] sales.getTimelineHighlights 未注册或非函数；时间轴密度退回本地计算");
			}
			return;
		}
		const reqId = ++timelineHighlightsReqRef.current;
		const { startStr, endStr } = reservationTimelineRangeStrings(scheduleDate, timelineRange, operatingDayBoundary, dayjs());
		const applyHighlights = (raw) => {
			if (timelineHighlightsReqRef.current !== reqId) return;
			setSalesTimelineHighlights(normalizeSalesTimelineHighlightsRaw(raw));
		};
		try {
			const out = highlightsFn.call(salesModule, bookingList, startStr, endStr);
			if (out != null && typeof out.then === "function") out.then(applyHighlights).catch((e) => {
				if (process.env.NODE_ENV !== "production") console.warn("[PisellReservation] sales.getTimelineHighlights Promise 失败", e);
			});
			else applyHighlights(out);
		} catch (e) {
			if (process.env.NODE_ENV !== "production") console.warn("[PisellReservation] sales.getTimelineHighlights 同步调用异常", e);
		}
	}, [
		isInternalListData,
		hasPisellosGetModule,
		resourceBoardLoading,
		floorDayBookingLoading,
		floorDayBookingDepKey,
		scheduleDayMs,
		debouncedAtMs,
		timelineRange.start,
		timelineRange.end,
		pisellosRef,
		operatingDayBoundary
	]);
	return {
		salesResourceBookings,
		salesTimelineHighlights
	};
}
//#endregion
export { useReservationSalesHostData };
