const require_runtime = require("../../../_virtual/_rolldown/runtime.js");
let dayjs = require("dayjs");
dayjs = require_runtime.__toESM(dayjs);
//#region src/components/pisellTimeNavigator/utils/index.ts
/** 将 HH:mm 或 HH:mm:ss 解析为当天该时间的 dayjs（基于 reference 的日期） */
function parseTimeOfDay(timeStr, reference) {
	const [h, m, s] = timeStr.trim().split(":").map(Number);
	return reference.hour(isNaN(h) ? 0 : h).minute(isNaN(m) ? 0 : m).second(isNaN(s) ? 0 : s).millisecond(0);
}
/** 匹配 HH:mm 或 HH:mm:ss 这类纯时间字符串 */
const TIME_ONLY_REGEX = /^\s*\d{1,2}:\d{2}(:\d{2})?\s*$/;
/**
* 解析 value 为 Dayjs，无效则返回 null。
* 支持 dayjs 可解析的日期时间；纯时间字符串（如 '12:00'、'14:30'）会基于 reference 的日期解析。
*/
function parseValue(value, reference) {
	if (value == null) return null;
	const ref = reference !== null && reference !== void 0 ? reference : (0, dayjs.default)();
	if (typeof value === "string" && TIME_ONLY_REGEX.test(value)) return parseTimeOfDay(value, ref);
	const d = (0, dayjs.default)(value);
	return d.isValid() ? d : null;
}
/** 日历时间轴默认步长（分钟），与历史「整点列」一致 */
const DEFAULT_CALENDAR_SLOT_STEP_MINUTES = 60;
/**
* 按营业日 {@link TimeRangeOption} 在锚定自然日零点展开，生成日视图时间轴「列左边界」：
* 值为相对 `reference.startOf('day')` 的**浮点小时**（可 ≥24，表示次日时刻），步长为 `slotStepMinutes`。
*/
function buildCalendarTimelineSlotStartsFromRange(range, anchorDate, slotStepMinutes = 60) {
	const step = Math.max(1, Math.min(1440, Math.round(Number(slotStepMinutes)) || 60));
	const ref = (0, dayjs.default)(anchorDate).startOf("day");
	const [rs, re] = getRangeBounds(range, ref);
	const out = [];
	let cur = rs;
	while (cur.isBefore(re)) {
		out.push(cur.diff(ref, "minute", true) / 60);
		cur = cur.add(step, "minute");
	}
	if (out.length === 0) for (let m = 480; m < 1200; m += step) out.push(m / 60);
	return out;
}
/**
* @deprecated 语义上等价于 {@link buildCalendarTimelineSlotStartsFromRange}(..., 60)；保留兼容导出。
*/
function buildCalendarHourSlotsFromRange(range, anchorDate) {
	return buildCalendarTimelineSlotStartsFromRange(range, anchorDate, 60);
}
/**
* 将 range 转为 [startDayjs, endDayjs]（reference 为营业锚定日的 0 点）
* - 同日内：如 08:00–22:00
* - 跨日营业：结束钟面早于或等于开始（如 02:01–次日 02:00）时，end 解析为 reference 的次日同一时刻
*/
function getRangeBounds(range, reference) {
	if (!range) return [reference.startOf("day"), reference.endOf("day")];
	const start = parseTimeOfDay(range.start, reference);
	let end = parseTimeOfDay(range.end, reference);
	if (end.isBefore(start) || end.isSame(start)) end = parseTimeOfDay(range.end, reference.add(1, "day"));
	return [start, end];
}
/**
* 自动挑选「能让 `now` 落入 [refDay + range.start, refDay + range.end] 营业窗口」的锚定日。
*
* 调用方未显式传入 `referenceDate` 时使用：保证当前时刻始终在轴可视范围内，
* 横向「现在」橙色高亮 / 竖向当前时间横线永远展示。
*
* 解析顺序：
* 1. 先尝试 `now.startOf('day')`（典型场景：跨日营业 18:00–04:00 时，下午 20:00 直接命中今天的窗口）；
* 2. 命中失败时尝试前一日（如营业 16:00–次日 16:00，now = 中午 12:00 时窗口需锚到昨天）；
* 3. 再尝试次日（如营业 16:00–次日 16:00，now 处于「即将进入下一个营业日」的边界附近）；
* 4. 三个候选都不包含 now（营业时段较短且 now 落在闭店时段，如 09:00–22:00 + now=23:00）时，
*    回退到 `now.startOf('day')`，保持原有行为，不强行扭曲日期。
*/
function pickReferenceDateContainingNow(range, now) {
	const today = now.startOf("day");
	const candidates = [
		today,
		today.subtract(1, "day"),
		today.add(1, "day")
	];
	for (const cand of candidates) {
		const [s, e] = getRangeBounds(range, cand);
		if (!now.isBefore(s) && !now.isAfter(e)) return cand;
	}
	return today;
}
/**
* 将时间吸附到 step 分钟刻度（0、15、30、45 等）：四舍五入到最近刻度。
*/
function snapToStepMinutes(t, stepMinutes) {
	const m = t.minute();
	const snapped = Math.round(m / stepMinutes) * stepMinutes;
	if (snapped >= 60) return t.add(1, "hour").minute(0).second(0).millisecond(0);
	return t.minute(snapped).second(0).millisecond(0);
}
/**
* 判断时间是否落在任一禁用区间内（按绝对时间与区间 [start,end) 比较）
* 注：与主时间轴同 reference 的跨日 range 一致时需传入同一营业锚定日解析出的区间。
*/
function isInDisabledRanges(t, disabledRanges) {
	if (!(disabledRanges === null || disabledRanges === void 0 ? void 0 : disabledRanges.length)) return false;
	const tMin = t.startOf("minute");
	for (const r of disabledRanges) {
		const [s, e] = getRangeBounds(r, t.startOf("day"));
		if ((tMin.isAfter(s) || tMin.isSame(s)) && tMin.isBefore(e)) return true;
	}
	return false;
}
/**
* 把 [`rangeStart`, `rangeEnd`] 切成「对齐到 step 网格」的格子序列：
*
* 1. 找到第一个 ≥ `rangeStart` 且分钟为 `stepMinutes` 整数倍的对齐点；
* 2. 若对齐点严格大于 `rangeStart`，先放一格 head（`rangeStart` → 对齐点），跨度 < step；
* 3. 中间按 step 步进，每格起点都落在网格上；
* 4. 若 `rangeEnd` 落在两个对齐点之间，最后多出一格 tail（最后对齐点 → `rangeEnd`），跨度 < step。
*
* `rangeStart` / `rangeEnd` 都恰落在网格上时退化为完全等步格。
*/
function getAxisCells(rangeStart, rangeEnd, stepMinutes) {
	const out = [];
	if (!rangeEnd.isAfter(rangeStart) || stepMinutes <= 0) return out;
	const startMin = rangeStart.minute();
	const remainder = (startMin % stepMinutes + stepMinutes) % stepMinutes;
	const firstAligned = remainder === 0 && rangeStart.second() === 0 && rangeStart.millisecond() === 0 ? rangeStart : rangeStart.startOf("hour").minute(startMin - remainder + stepMinutes).second(0).millisecond(0);
	if (firstAligned.isAfter(rangeStart)) {
		const headMin = firstAligned.diff(rangeStart, "minute", true);
		if (headMin > 0) out.push({
			start: rangeStart,
			widthMinutes: headMin
		});
	}
	let cur = firstAligned;
	/**
	* 防御：超大 / 非法 range 时（如 1 年）直接挂死。这里限制最多 2 天 + 1 步的格数，
	* 与 useTimeAxis 的 `totalLengthPx` 上限呼应。
	*/
	const safetyLimit = 2880 / stepMinutes + 4;
	while (cur.isBefore(rangeEnd) && out.length < safetyLimit) {
		const next = cur.add(stepMinutes, "minute");
		if (next.isAfter(rangeEnd)) {
			const tailMin = rangeEnd.diff(cur, "minute", true);
			if (tailMin > 0) out.push({
				start: cur,
				widthMinutes: tailMin
			});
			break;
		}
		out.push({
			start: cur,
			widthMinutes: stepMinutes
		});
		cur = next;
	}
	return out;
}
/**
* 横向时间轴「格」数量，与 useTimeAxis 中 cells 算法一致。
* 用于 cursor-moves 模式下计算压缩后的每格像素宽度。
*/
function getHorizontalAxisSlotCount(range, referenceDate, stepMinutes) {
	const [rangeStart, rangeEnd] = getRangeBounds(range, referenceDate);
	return getAxisCells(rangeStart, rangeEnd, stepMinutes).length;
}
//#endregion
exports.DEFAULT_CALENDAR_SLOT_STEP_MINUTES = DEFAULT_CALENDAR_SLOT_STEP_MINUTES;
exports.buildCalendarHourSlotsFromRange = buildCalendarHourSlotsFromRange;
exports.buildCalendarTimelineSlotStartsFromRange = buildCalendarTimelineSlotStartsFromRange;
exports.getAxisCells = getAxisCells;
exports.getHorizontalAxisSlotCount = getHorizontalAxisSlotCount;
exports.getRangeBounds = getRangeBounds;
exports.isInDisabledRanges = isInDisabledRanges;
exports.parseValue = parseValue;
exports.pickReferenceDateContainingNow = pickReferenceDateContainingNow;
exports.snapToStepMinutes = snapToStepMinutes;
