import dayjs from 'dayjs';

"use strict";
const parseTimeToMinutes = (value) => {
  const [hours = "0", minutes = "0"] = value.split(":");
  return Number(hours) * 60 + Number(minutes);
};
const toDateValue = (value) => {
  if (!value) {
    return void 0;
  }
  return value instanceof Date ? value : new Date(value);
};
const toInternalDateValue = (value) => {
  if (!value) {
    return void 0;
  }
  return value instanceof Date ? value : new Date(value);
};
const normalizeDateLikeValue = (value) => {
  if (value instanceof Date) {
    return value.toISOString();
  }
  if (typeof value === "string") {
    const date = new Date(value);
    return Number.isNaN(date.getTime()) ? value : date.toISOString();
  }
  return value;
};
const toCalendarEvent = (event) => ({
  ...event,
  start: toInternalDateValue(event.start) || event.start,
  end: toInternalDateValue(event.end) || event.end
});
const toCalendarEvents = (events) => events.map(toCalendarEvent);
const normalizeEventValue = (value) => {
  const normalizedDateLikeValue = normalizeDateLikeValue(value);
  if (normalizedDateLikeValue !== value) {
    return normalizedDateLikeValue;
  }
  if (Array.isArray(value)) {
    return value.map(normalizeEventValue);
  }
  if (value && typeof value === "object") {
    return Object.keys(value).sort().reduce((result, key) => {
      result[key] = normalizeEventValue(
        value[key]
      );
      return result;
    }, {});
  }
  return value;
};
const getEventsSignature = (events) => JSON.stringify(events.map(normalizeEventValue));
const isSameEvents = (left, right) => getEventsSignature(left) === getEventsSignature(right);
const isEventOverlappingRange = (event, rangeStart, rangeEnd) => {
  const start = toDateValue(event.start);
  const end = toDateValue(event.end) || start;
  if (!start || Number.isNaN(start.getTime()) || !end || Number.isNaN(end.getTime())) {
    return false;
  }
  const normalizedRangeStart = dayjs(rangeStart).startOf("day").toDate();
  const normalizedRangeEnd = dayjs(rangeEnd).endOf("day").toDate();
  const effectiveEnd = new Date(Math.max(end.getTime() - 1, start.getTime()));
  return start.getTime() <= normalizedRangeEnd.getTime() && effectiveEnd.getTime() >= normalizedRangeStart.getTime();
};
const isCrossDayEvent = (event) => {
  const start = toDateValue(event.start);
  const end = toDateValue(event.end);
  if (event.allDay) {
    return true;
  }
  if (!start || Number.isNaN(start.getTime()) || !end || Number.isNaN(end.getTime())) {
    return false;
  }
  const crossesDayBoundary = dayjs(end).startOf("day").isAfter(dayjs(start).startOf("day"));
  if (!crossesDayBoundary) {
    return false;
  }
  const dayMs = 24 * 60 * 60 * 1e3;
  const durationMs = end.getTime() - start.getTime();
  return durationMs >= dayMs;
};
const getCrossDaySortMetrics = (event) => {
  const start = toDateValue(event.start);
  const end = toDateValue(event.end) || start;
  if (!start || Number.isNaN(start.getTime()) || !end || Number.isNaN(end.getTime())) {
    return {
      spanDays: 0,
      durationMinutes: 0,
      startAt: Number.MAX_SAFE_INTEGER
    };
  }
  const effectiveEnd = new Date(Math.max(end.getTime() - 1, start.getTime()));
  const spanDays = Math.max(
    dayjs(effectiveEnd).startOf("day").diff(dayjs(start).startOf("day"), "day") + 1,
    1
  );
  const durationMinutes = Math.max(
    Math.round((end.getTime() - start.getTime()) / 6e4),
    0
  );
  return {
    spanDays,
    durationMinutes,
    startAt: start.getTime()
  };
};
const formatEventDateTime = (date) => {
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const day = String(date.getDate()).padStart(2, "0");
  const hours = String(date.getHours()).padStart(2, "0");
  const minutes = String(date.getMinutes()).padStart(2, "0");
  return `${month}-${day} ${hours}:${minutes}`;
};
const formatEventTime = (date) => {
  const hours = String(date.getHours()).padStart(2, "0");
  const minutes = String(date.getMinutes()).padStart(2, "0");
  return `${hours}:${minutes}`;
};
const getEventStartTimestamp = (event) => {
  const start = toDateValue(event.start);
  return start && !Number.isNaN(start.getTime()) ? start.getTime() : 0;
};
const getEventDurationMinutes = (event) => {
  const start = toDateValue(event.start);
  const end = toDateValue(event.end);
  if (!start || Number.isNaN(start.getTime()) || !end || Number.isNaN(end.getTime())) {
    return 0;
  }
  return Math.max(Math.round((end.getTime() - start.getTime()) / 6e4), 0);
};
const getEventSpanDays = (event) => {
  const start = toDateValue(event.start);
  const end = toDateValue(event.end);
  if (!start || Number.isNaN(start.getTime()) || !end || Number.isNaN(end.getTime())) {
    return 1;
  }
  const startDay = dayjs(start).startOf("day");
  const endDay = dayjs(end).startOf("day");
  const diffDays = endDay.diff(startDay, "day");
  return Math.max(diffDays + 1, 1);
};
const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
const toEditDate = (value) => {
  if (!value) {
    return void 0;
  }
  if (dayjs.isDayjs(value)) {
    return value.toDate();
  }
  const date = toDateValue(value);
  if (!date || Number.isNaN(date.getTime())) {
    return void 0;
  }
  return date;
};
const createDraftClassToken = () => `ll-calendar__event-draft-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
const mergeClassName = (currentClass, nextToken) => {
  const tokens = String(currentClass || "").split(/\s+/).filter(Boolean);
  if (!tokens.includes(nextToken)) {
    tokens.push(nextToken);
  }
  return tokens.join(" ");
};
const stripClassName = (currentClass, removeToken) => String(currentClass || "").split(/\s+/).filter((token) => token && token !== removeToken).join(" ");
const hasClassToken = (currentClass, token) => String(currentClass || "").split(/\s+/).filter(Boolean).includes(token);
const isToday = (date) => {
  const today = /* @__PURE__ */ new Date();
  return date.getFullYear() === today.getFullYear() && date.getMonth() === today.getMonth() && date.getDate() === today.getDate();
};
const hasActiveTextSelection = () => {
  if (typeof window === "undefined" || !window.getSelection) {
    return false;
  }
  const selection = window.getSelection();
  if (!selection) {
    return false;
  }
  return !selection.isCollapsed && Boolean(selection.toString().trim());
};
const LUNAR_DAY_NAMES = [
  "\u521D\u4E00",
  "\u521D\u4E8C",
  "\u521D\u4E09",
  "\u521D\u56DB",
  "\u521D\u4E94",
  "\u521D\u516D",
  "\u521D\u4E03",
  "\u521D\u516B",
  "\u521D\u4E5D",
  "\u521D\u5341",
  "\u5341\u4E00",
  "\u5341\u4E8C",
  "\u5341\u4E09",
  "\u5341\u56DB",
  "\u5341\u4E94",
  "\u5341\u516D",
  "\u5341\u4E03",
  "\u5341\u516B",
  "\u5341\u4E5D",
  "\u4E8C\u5341",
  "\u5EFF\u4E00",
  "\u5EFF\u4E8C",
  "\u5EFF\u4E09",
  "\u5EFF\u56DB",
  "\u5EFF\u4E94",
  "\u5EFF\u516D",
  "\u5EFF\u4E03",
  "\u5EFF\u516B",
  "\u5EFF\u4E5D",
  "\u4E09\u5341"
];
let _lunarFormatter;
const getLunarFormatter = () => {
  if (_lunarFormatter !== void 0) {
    return _lunarFormatter;
  }
  try {
    _lunarFormatter = new Intl.DateTimeFormat("zh-CN-u-ca-chinese", {
      month: "long",
      day: "numeric"
    });
  } catch {
    _lunarFormatter = null;
  }
  return _lunarFormatter;
};
const getLunarDate = (date) => {
  const d = date instanceof Date ? date : new Date(date);
  if (!d || Number.isNaN(d.getTime())) return "";
  const formatter = getLunarFormatter();
  if (!formatter) return "";
  try {
    const parts = formatter.formatToParts(d);
    const monthPart = parts.find((p) => p.type === "month")?.value ?? "";
    const dayPart = parts.find((p) => p.type === "day")?.value ?? "";
    const dayNum = parseInt(dayPart, 10);
    if (dayNum === 1) {
      return monthPart;
    }
    return !Number.isNaN(dayNum) && dayNum >= 1 && dayNum <= 30 ? LUNAR_DAY_NAMES[dayNum - 1] : dayPart;
  } catch {
    return "";
  }
};
const toDateLike = (value) => {
  if (value == null || value === "") {
    return null;
  }
  if (value instanceof Date) {
    return Number.isNaN(value.getTime()) ? null : value;
  }
  if (dayjs.isDayjs(value)) {
    const date = value.toDate();
    return Number.isNaN(date.getTime()) ? null : date;
  }
  if (typeof value === "number") {
    const ms = value < 1e12 ? value * 1e3 : value;
    const date = new Date(ms);
    return Number.isNaN(date.getTime()) ? null : date;
  }
  if (typeof value === "string") {
    const numeric = Number(value);
    if (!Number.isNaN(numeric) && /^\d+$/.test(value.trim())) {
      return toDateLike(numeric);
    }
    const date = new Date(value);
    return Number.isNaN(date.getTime()) ? null : date;
  }
  return null;
};
const formatDateGroupDateTime = (date) => {
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, "0");
  const day = String(date.getDate()).padStart(2, "0");
  const hours = String(date.getHours()).padStart(2, "0");
  const minutes = String(date.getMinutes()).padStart(2, "0");
  const seconds = String(date.getSeconds()).padStart(2, "0");
  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
};
const applyDateGroupTitleFormat = (record, format) => {
  if (!format) {
    return record.title != null ? String(record.title) : "";
  }
  return format.replace(/\{(\w+)\}/g, (_, key) => {
    const value = record[key];
    return value == null ? "" : String(value);
  });
};
const getEnabledDateGroups = (groups) => (groups ?? []).filter(
  (group) => group && group.enabled !== false && Boolean(group.id) && Boolean(group.startField)
);
const COMPOSITE_ID_SEPARATOR = "_";
const buildCompositeEventId = (rawRecordId, groupId) => `${rawRecordId}${COMPOSITE_ID_SEPARATOR}${groupId}`;
const buildDateGroupEvents = (records, groups, format) => {
  const result = [];
  records.forEach((record, index) => {
    if (!record || typeof record !== "object") {
      return;
    }
    const rawRecordId = String(record.id ?? index);
    let produced = false;
    groups.forEach((group) => {
      const startDate = toDateLike(record[group.startField]);
      if (!startDate) {
        return;
      }
      produced = true;
      const endVal = group.endField ? record[group.endField] : null;
      const endDate = endVal ? toDateLike(endVal) || startDate : startDate;
      const event = {
        ...record,
        id: buildCompositeEventId(rawRecordId, group.id),
        title: applyDateGroupTitleFormat(record, format),
        start: formatDateGroupDateTime(startDate),
        end: formatDateGroupDateTime(endDate),
        rawRecordId,
        _dateGroupId: group.id
      };
      const resolvedEventColor = group.eventColor ?? group.labelColor;
      if (resolvedEventColor != null) {
        event.eventColor = resolvedEventColor;
      }
      if (group.backgroundColor != null) {
        event.backgroundColor = group.backgroundColor;
      }
      if (group.borderColor != null) {
        event.borderColor = group.borderColor;
      }
      if (group.color != null) {
        event.color = group.color;
      }
      const resolvedTag = group.tag ?? group.tagText ?? group.labelText;
      if (resolvedTag != null) {
        event.tag = resolvedTag;
      }
      if (group.tagColor != null) {
        event.tagColor = group.tagColor;
      }
      result.push(event);
    });
    if (!produced && toDateLike(record.start)) {
      result.push(record);
    }
  });
  return result;
};
const parseCompositeEventId = (id, groups) => {
  const raw = String(id ?? "");
  for (const group of groups) {
    const suffix = `${COMPOSITE_ID_SEPARATOR}${group.id}`;
    if (raw.endsWith(suffix) && raw.length > suffix.length) {
      return { rawRecordId: raw.slice(0, -suffix.length), groupId: group.id };
    }
  }
  return { rawRecordId: raw, groupId: null };
};
const foldDateGroupEvents = (events, baseRecords, groups) => {
  const INTERNAL_KEYS = /* @__PURE__ */ new Set([
    "id",
    "start",
    "end",
    "title",
    "rawRecordId",
    "_dateGroupId",
    "_eid",
    "class",
    "eventColor",
    "backgroundColor",
    "borderColor",
    "color",
    "tag",
    "tagColor"
  ]);
  const recordMap = /* @__PURE__ */ new Map();
  const order = [];
  baseRecords.forEach((record, index) => {
    if (!record || typeof record !== "object") {
      return;
    }
    const key = String(record.id ?? index);
    recordMap.set(key, { ...record });
    order.push(key);
  });
  const aliveRawIds = /* @__PURE__ */ new Set();
  const aliveGroupKeys = /* @__PURE__ */ new Set();
  const passthrough = [];
  events.forEach((event) => {
    if (!event || typeof event !== "object") {
      return;
    }
    const { rawRecordId, groupId } = parseCompositeEventId(event.id, groups);
    const group = groupId ? groups.find((g) => g.id === groupId) : void 0;
    if (!group) {
      passthrough.push(event);
      return;
    }
    aliveRawIds.add(rawRecordId);
    aliveGroupKeys.add(`${rawRecordId}::${group.id}`);
    const target = recordMap.get(rawRecordId) ?? { id: rawRecordId };
    if (!recordMap.has(rawRecordId)) {
      recordMap.set(rawRecordId, target);
      order.push(rawRecordId);
    }
    const startDate = toDateLike(event.start);
    if (startDate) {
      target[group.startField] = formatDateGroupDateTime(startDate);
    }
    if (group.endField) {
      const endDate = toDateLike(event.end);
      if (endDate) {
        target[group.endField] = formatDateGroupDateTime(endDate);
      }
    }
    Object.keys(event).forEach((key) => {
      if (!INTERNAL_KEYS.has(key)) {
        target[key] = event[key];
      }
    });
  });
  const result = [];
  order.forEach((key) => {
    const record = recordMap.get(key);
    if (!record) {
      return;
    }
    const producedDerived = groups.some((group) => toDateLike(record[group.startField]));
    if (producedDerived && !aliveRawIds.has(key)) {
      return;
    }
    if (aliveRawIds.has(key)) {
      groups.forEach((group) => {
        if (!aliveGroupKeys.has(`${key}::${group.id}`)) {
          delete record[group.startField];
          if (group.endField) {
            delete record[group.endField];
          }
        }
      });
    }
    result.push(record);
  });
  return [...result, ...passthrough];
};

export { applyDateGroupTitleFormat, buildCompositeEventId, buildDateGroupEvents, clamp, createDraftClassToken, foldDateGroupEvents, formatDateGroupDateTime, formatEventDateTime, formatEventTime, getCrossDaySortMetrics, getEnabledDateGroups, getEventDurationMinutes, getEventSpanDays, getEventStartTimestamp, getEventsSignature, getLunarDate, hasActiveTextSelection, hasClassToken, isCrossDayEvent, isEventOverlappingRange, isSameEvents, isToday, mergeClassName, normalizeDateLikeValue, normalizeEventValue, parseCompositeEventId, parseTimeToMinutes, stripClassName, toCalendarEvent, toCalendarEvents, toDateLike, toDateValue, toEditDate, toInternalDateValue };
//# sourceMappingURL=calendar-utils.mjs.map
