import "./index-rbwdGzy0.js";
import dayjs from "dayjs";
function getAdjustedTimezone(date, timeZone, travelSchedules) {
  let adjustedTimezone = timeZone;
  for (const travelSchedule of travelSchedules) {
    if (!date.isBefore(travelSchedule.startDate) && (!travelSchedule.endDate || !date.isAfter(travelSchedule.endDate))) {
      adjustedTimezone = travelSchedule.timeZone;
      break;
    }
  }
  return adjustedTimezone;
}
function processWorkingHours(results, {
  item,
  timeZone,
  dateFrom,
  dateTo,
  travelSchedules
}) {
  const utcDateTo = dateTo.utc();
  let endTimeToKeyMap;
  for (let date = dateFrom.startOf("day"); utcDateTo.isAfter(date); date = date.add(1, "day")) {
    const fromOffset = dateFrom.startOf("day").utcOffset();
    const adjustedTimezone = getAdjustedTimezone(date, timeZone, travelSchedules);
    const offset = date.tz(adjustedTimezone).utcOffset();
    const dateInTz = date.add(fromOffset - offset, "minutes").tz(adjustedTimezone);
    if (!item.days.includes(dateInTz.day())) {
      continue;
    }
    let start = dateInTz.add(item.startTime.getUTCHours(), "hours").add(item.startTime.getUTCMinutes(), "minutes");
    let end = dateInTz.add(item.endTime.getUTCHours(), "hours").add(item.endTime.getUTCMinutes(), "minutes");
    const offsetBeginningOfDay = dayjs(start.format("YYYY-MM-DD hh:mm")).tz(adjustedTimezone).utcOffset();
    const offsetDiff = start.utcOffset() - offsetBeginningOfDay;
    start = start.add(offsetDiff, "minute");
    end = end.add(offsetDiff, "minute");
    const startResult = dayjs.max(start, dateFrom);
    let endResult = dayjs.min(end, dateTo.tz(adjustedTimezone));
    if (endResult.hour() === 23 && endResult.minute() === 59) {
      endResult = endResult.add(1, "minute");
    }
    if (endResult.isBefore(startResult)) {
      continue;
    }
    const endTimeKey = endResult.valueOf();
    if (!endTimeToKeyMap) {
      endTimeToKeyMap = /* @__PURE__ */ new Map();
      for (const [key, range] of Object.entries(results)) {
        const endTime = range.end.valueOf();
        if (!endTimeToKeyMap.has(endTime)) {
          endTimeToKeyMap.set(endTime, []);
        }
        endTimeToKeyMap.get(endTime).push(Number(key));
      }
    }
    const keysWithSameEndTime = endTimeToKeyMap.get(endTimeKey) || [];
    let foundOverlapping = false;
    for (const key of keysWithSameEndTime) {
      const existingRange = results[key];
      if (startResult.valueOf() <= existingRange.end.valueOf() && endResult.valueOf() >= existingRange.start.valueOf()) {
        results[key] = {
          start: dayjs.min(existingRange.start, startResult),
          end: endResult
        };
        foundOverlapping = true;
        break;
      }
    }
    if (foundOverlapping) {
      continue;
    }
    if (results[startResult.valueOf()]) {
      const oldKey = startResult.valueOf();
      const newKey2 = endResult.valueOf();
      results[newKey2] = {
        start: results[oldKey].start,
        end: dayjs.max(results[oldKey].end, endResult)
      };
      if (endTimeToKeyMap) {
        const oldEndTime = results[oldKey].end.valueOf();
        const oldKeys = endTimeToKeyMap.get(oldEndTime) || [];
        const filteredKeys = oldKeys.filter((k) => k !== oldKey);
        if (filteredKeys.length === 0) {
          endTimeToKeyMap.delete(oldEndTime);
        } else {
          endTimeToKeyMap.set(oldEndTime, filteredKeys);
        }
        if (!endTimeToKeyMap.has(endTimeKey)) {
          endTimeToKeyMap.set(endTimeKey, []);
        }
        endTimeToKeyMap.get(endTimeKey).push(newKey2);
      }
      delete results[oldKey];
      continue;
    }
    const newKey = endResult.valueOf();
    results[newKey] = {
      start: startResult,
      end: endResult
    };
    if (endTimeToKeyMap) {
      if (!endTimeToKeyMap.has(endTimeKey)) {
        endTimeToKeyMap.set(endTimeKey, []);
      }
      endTimeToKeyMap.get(endTimeKey).push(newKey);
    }
  }
  return results;
}
function processDateOverride({
  item,
  itemDateAsUtc,
  timeZone,
  travelSchedules
}) {
  const overrideDate = dayjs(item.date);
  const adjustedTimezone = getAdjustedTimezone(overrideDate, timeZone, travelSchedules);
  const itemDateStartOfDay = itemDateAsUtc.startOf("day");
  const startDate = itemDateStartOfDay.add(item.startTime.getUTCHours(), "hours").add(item.startTime.getUTCMinutes(), "minutes").second(0).tz(adjustedTimezone, true);
  let endDate = itemDateStartOfDay;
  const endTimeHours = item.endTime.getUTCHours();
  const endTimeMinutes = item.endTime.getUTCMinutes();
  if (endTimeHours === 23 && endTimeMinutes === 59) {
    endDate = endDate.add(1, "day").tz(timeZone, true);
  } else {
    endDate = itemDateStartOfDay.add(endTimeHours, "hours").add(endTimeMinutes, "minutes").second(0).tz(adjustedTimezone, true);
  }
  return {
    start: startDate,
    end: endDate
  };
}
function processOOO(outOfOffice, timeZone) {
  const OOOdate = outOfOffice.tz(timeZone, true);
  return {
    start: OOOdate,
    end: OOOdate
  };
}
function buildDateRanges({
  availability,
  timeZone,
  dateFrom,
  dateTo,
  travelSchedules,
  outOfOffice
}) {
  const dateFromOrganizerTZ = dateFrom.tz(timeZone);
  const groupedWorkingHours = groupByDate(
    Object.values(
      availability.reduce((processed, item) => {
        if (!("days" in item)) {
          return processed;
        }
        processed = processWorkingHours(processed, {
          item,
          timeZone,
          dateFrom: dateFromOrganizerTZ,
          dateTo,
          travelSchedules
        });
        return processed;
      }, {})
    )
  );
  const groupedOOO = groupByDate(
    outOfOffice ? Object.keys(outOfOffice).map((outOfOffice2) => processOOO(dayjs.utc(outOfOffice2), timeZone)) : []
  );
  const groupedDateOverrides = groupByDate(
    Object.values(
      availability.reduce((processed, item) => {
        if (!("date" in item && !!item.date)) {
          return processed;
        }
        const itemDateAsUtc = dayjs.utc(item.date);
        if (itemDateAsUtc.isBetween(
          dateFrom.subtract(1, "day").startOf("day"),
          dateTo.add(1, "day").endOf("day"),
          null,
          "[]"
        )) {
          const newProcessedDateOverride = processDateOverride({
            item,
            itemDateAsUtc,
            timeZone,
            travelSchedules
          });
          if (processed[newProcessedDateOverride.start.valueOf()]) {
            processed[newProcessedDateOverride.start.valueOf()].end = dayjs.max(
              processed[newProcessedDateOverride.start.valueOf()].end,
              newProcessedDateOverride.end
            );
            return processed;
          }
          processed[newProcessedDateOverride.end.valueOf()] = newProcessedDateOverride;
        }
        return processed;
      }, {})
    )
  );
  const dateRanges = Object.values({
    ...groupedWorkingHours,
    ...groupedDateOverrides
  }).map(
    // remove 0-length overrides that were kept to cancel out working dates until now.
    (ranges) => ranges.filter((range) => range.start.valueOf() !== range.end.valueOf())
  );
  const oooExcludedDateRanges = Object.values({
    ...groupedWorkingHours,
    ...groupedDateOverrides,
    ...groupedOOO
  }).map(
    // remove 0-length overrides && OOO dates that were kept to cancel out working dates until now.
    (ranges) => ranges.filter((range) => range.start.valueOf() !== range.end.valueOf())
  );
  return { dateRanges: dateRanges.flat(), oooExcludedDateRanges: oooExcludedDateRanges.flat() };
}
function groupByDate(ranges) {
  const results = ranges.reduce(
    (previousValue, currentValue) => {
      const dateString = dayjs(currentValue.start).format("YYYY-MM-DD");
      previousValue[dateString] = typeof previousValue[dateString] === "undefined" ? [currentValue] : [...previousValue[dateString], currentValue];
      return previousValue;
    },
    {}
  );
  return results;
}
function intersect(ranges) {
  if (!ranges.length) {
    return [];
  }
  const sortedRanges = ranges.map(
    (userRanges) => userRanges.map((r) => ({
      ...r,
      startValue: r.start.valueOf(),
      endValue: r.end.valueOf()
    })).sort((a, b) => a.startValue - b.startValue)
  );
  let commonAvailability = sortedRanges[0];
  for (let i = 1; i < sortedRanges.length; i++) {
    if (commonAvailability.length === 0) {
      return [];
    }
    const userRanges = sortedRanges[i];
    const intersectedRanges = [];
    let commonIndex = 0;
    let userIndex = 0;
    while (commonIndex < commonAvailability.length && userIndex < userRanges.length) {
      const commonRange = commonAvailability[commonIndex];
      const userRange = userRanges[userIndex];
      const intersectStartValue = Math.max(commonRange.startValue, userRange.startValue);
      const intersectEndValue = Math.min(commonRange.endValue, userRange.endValue);
      if (intersectStartValue < intersectEndValue) {
        const intersectStart = commonRange.startValue > userRange.startValue ? commonRange.start : userRange.start;
        const intersectEnd = commonRange.endValue < userRange.endValue ? commonRange.end : userRange.end;
        intersectedRanges.push({
          start: intersectStart,
          end: intersectEnd,
          startValue: intersectStartValue,
          endValue: intersectEndValue
        });
      }
      if (commonRange.endValue <= userRange.endValue) {
        commonIndex++;
      } else {
        userIndex++;
      }
    }
    commonAvailability = intersectedRanges;
  }
  return commonAvailability.map(({ start, end }) => ({ start, end }));
}
function subtract(sourceRanges, excludedRanges) {
  const result = [];
  const sortedExcludedRanges = [...excludedRanges].sort((a, b) => a.start.valueOf() - b.start.valueOf());
  for (const { start: sourceStart, end: sourceEnd, ...passThrough } of sourceRanges) {
    let currentStart = sourceStart;
    for (const excludedRange of sortedExcludedRanges) {
      if (excludedRange.start.valueOf() >= sourceEnd.valueOf())
        break;
      if (excludedRange.end.valueOf() <= currentStart.valueOf())
        continue;
      if (excludedRange.start.valueOf() > currentStart.valueOf()) {
        result.push({ start: currentStart, end: excludedRange.start, ...passThrough });
      }
      if (excludedRange.end.valueOf() > currentStart.valueOf()) {
        currentStart = excludedRange.end;
      }
    }
    if (sourceEnd.valueOf() > currentStart.valueOf()) {
      result.push({ start: currentStart, end: sourceEnd, ...passThrough });
    }
  }
  return result;
}
function mergeOverlappingRanges(ranges) {
  if (ranges.length === 0)
    return [];
  const sortedRanges = ranges.sort((a, b) => a.start.valueOf() - b.start.valueOf());
  const mergedRanges = [sortedRanges[0]];
  for (let i = 1; i < sortedRanges.length; i++) {
    const lastMergedRange = mergedRanges[mergedRanges.length - 1];
    const currentRange = sortedRanges[i];
    if (currentRange.start.getTime() <= lastMergedRange.end.getTime()) {
      lastMergedRange.end = new Date(Math.max(lastMergedRange.end.getTime(), currentRange.end.getTime()));
    } else {
      mergedRanges.push(currentRange);
    }
  }
  return mergedRanges;
}
export {
  buildDateRanges as b,
  intersect as i,
  mergeOverlappingRanges as m,
  subtract as s
};
