"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);

// src/services/delegates/gsuite/findOptimalMeetingTime.ts
var findOptimalMeetingTime_exports = {};
__export(findOptimalMeetingTime_exports, {
  findOptimalMeetingTime: () => findOptimalMeetingTime
});
module.exports = __toCommonJS(findOptimalMeetingTime_exports);
var LOG_PREFIX = "GSUITE - findOptimalMeetingTime - ";
function getTimeZoneOffset(timeZone, date, fallbackOffset) {
  try {
    const dtf = new Intl.DateTimeFormat("en-US", {
      timeZone,
      year: "numeric",
      month: "2-digit",
      day: "2-digit",
      hour: "2-digit",
      minute: "2-digit",
      second: "2-digit",
      hour12: false
    });
    const parts = dtf.formatToParts(date);
    const year = parts.find((p) => p.type === "year")?.value;
    const month = parts.find((p) => p.type === "month")?.value;
    const day = parts.find((p) => p.type === "day")?.value;
    const hour = parts.find((p) => p.type === "hour")?.value;
    const minute = parts.find((p) => p.type === "minute")?.value;
    const second = parts.find((p) => p.type === "second")?.value;
    if (!year || !month || !day || !hour || !minute || !second) {
      throw new Error("Incomplete parts");
    }
    const localString = `${year}-${month}-${day}T${hour}:${minute}:${second}`;
    const asLocal = new Date(localString);
    return Math.round((asLocal.getTime() - date.getTime()) / (60 * 1e3));
  } catch (e) {
    return fallbackOffset || 0;
  }
}
function calculateMeetingTime(timeframeContext, workingHours = { start_hour: 8, end_hour: 17 }, now = /* @__PURE__ */ new Date(), timezone = "America/Los_Angeles", fallbackOffset, skipFridayMeetings = false, meetingDuration = 30) {
  const candidateDate = new Date(timeframeContext);
  if (!isNaN(candidateDate.getTime())) {
    const startTime2 = new Date(candidateDate);
    const endTime2 = new Date(startTime2.getTime() + meetingDuration * 60 * 1e3);
    return { startTime: startTime2, endTime: endTime2 };
  }
  const nowLocalString = new Date(now).toLocaleString("en-US", { timeZone: timezone });
  const localNow = new Date(nowLocalString);
  const startTime = new Date(localNow);
  const endTime = new Date(localNow);
  let daysOffset = 0;
  if (localNow.getHours() > workingHours.end_hour - 1) {
    localNow.setDate(localNow.getDate() + 1);
  }
  const currentDay = localNow.getDay();
  switch (currentDay) {
    case 5:
      if (skipFridayMeetings) {
        daysOffset = 3;
      }
      break;
    case 6:
      daysOffset = 2;
      break;
    case 0:
      daysOffset = 1;
      break;
    default:
  }
  if (timeframeContext === "next week") {
    const currentDay2 = localNow.getDay();
    let daysToNextMonday = (1 - currentDay2 + 7) % 7;
    if (daysToNextMonday === 0) {
      daysToNextMonday = 7;
    }
    daysOffset = daysToNextMonday;
  }
  startTime.setDate(localNow.getDate() + daysOffset);
  endTime.setDate(localNow.getDate() + daysOffset);
  startTime.setHours(workingHours.start_hour, 0, 0, 0);
  endTime.setHours(workingHours.end_hour, 0, 0, 0);
  const offsetMinutes = getTimeZoneOffset(timezone, startTime, fallbackOffset);
  const adjustedStartTime = new Date(startTime.getTime() - offsetMinutes * 60 * 1e3);
  const adjustedEndTime = new Date(endTime.getTime() - offsetMinutes * 60 * 1e3);
  return { startTime: adjustedStartTime, endTime: adjustedEndTime };
}
function calculateTimeSlotScore(slot, allBusyTimes) {
  let score = 100;
  const hoursFromNow = (new Date(slot.start).getTime() - (/* @__PURE__ */ new Date()).getTime()) / (1e3 * 60 * 60);
  score -= Math.min(hoursFromNow * 0.5, 20);
  for (const busy of allBusyTimes) {
    const busyStart = new Date(busy.start);
    const busyEnd = new Date(busy.end);
    const slotStart = new Date(slot.start);
    const slotEnd = new Date(slot.end);
    const bufferBefore = (slotStart.getTime() - busyEnd.getTime()) / (1e3 * 60);
    const bufferAfter = (busyStart.getTime() - slotEnd.getTime()) / (1e3 * 60);
    if (bufferBefore > 0 && bufferBefore < 30) score -= 10;
    if (bufferAfter > 0 && bufferAfter < 30) score -= 10;
  }
  return Math.max(0, score);
}
function toISOStringWithTimezone(date, timezone, fallbackOffset) {
  const currentOffset = getTimeZoneOffset(timezone, date);
  if (currentOffset <= 0) {
    return date.toISOString();
  }
  const offsetMinutes = getTimeZoneOffset(timezone, date, fallbackOffset);
  const sign = offsetMinutes >= 0 ? "+" : "-";
  const absOffset = Math.abs(offsetMinutes);
  const pad = (n) => `${n}`.padStart(2, "0");
  const adjustedDate = new Date(date.getTime() + offsetMinutes * 60 * 1e3);
  return adjustedDate.getFullYear() + "-" + pad(adjustedDate.getMonth() + 1) + "-" + pad(adjustedDate.getDate()) + "T" + pad(adjustedDate.getHours()) + ":" + pad(adjustedDate.getMinutes()) + ":" + pad(adjustedDate.getSeconds()) + sign + pad(Math.floor(absOffset / 60)) + ":" + pad(absOffset % 60);
}
function slotsOverlap(slot1, slot2) {
  return new Date(slot1.start) < new Date(slot2.end) && new Date(slot2.start) < new Date(slot1.end);
}
function calculateEventDuration(start, end) {
  const startDate = new Date(start);
  const endDate = new Date(end);
  const durationMs = endDate.getTime() - startDate.getTime();
  return durationMs / (1e3 * 60);
}
function roundUpToNextSlot(date, slotMinutes = 30) {
  const rounded = new Date(date.getTime());
  const minutes = rounded.getMinutes();
  const remainder = minutes % slotMinutes;
  if (remainder !== 0) {
    rounded.setMinutes(minutes - remainder + slotMinutes);
  }
  rounded.setSeconds(0, 0);
  return rounded;
}
async function findOptimalMeetingTime(calendar, context) {
  console.log(`${LOG_PREFIX} Finding optimal meeting time for:
  participants: ${JSON.stringify(context.participants, null, 2)}
  timeframe: ${context.timeframe_context}`);
  try {
    const duration = context.duration_minutes || 30;
    const workingHours = context.working_hours || { start_hour: 8, end_hour: 17 };
    const { startTime, endTime } = calculateMeetingTime(context.timeframe_context, workingHours, /* @__PURE__ */ new Date(), context.timezone);
    const eventTimeSlots = [];
    const participants = [...context.participants];
    for (const participant of participants) {
      const params = {
        calendarId: participant,
        timeMin: toISOStringWithTimezone(startTime, context.timezone, -420),
        timeMax: toISOStringWithTimezone(endTime, context.timezone, -420),
        singleEvents: true
        // Expands recurring events if needed.
      };
      const eventsResponse = await calendar.events.list(params);
      const events = eventsResponse.data.items || [];
      events.forEach((event) => {
        if (event.status !== "cancelled" && event.start && event.end && event.start.dateTime && event.end.dateTime) {
          const duration2 = calculateEventDuration(event.start.dateTime, event.end.dateTime);
          if (duration2 > 0) {
            eventTimeSlots.push({
              attendees: JSON.stringify(event.attendees),
              id: event.iCalUID,
              start: event.start.dateTime,
              end: event.end.dateTime,
              startLocalDate: new Date(event.start.dateTime).toString(),
              endLocalDate: new Date(event.end.dateTime).toString(),
              duration: duration2
            });
          }
        }
      });
    }
    const freeBusyRequest = {
      timeMin: toISOStringWithTimezone(startTime, context.timezone, -420),
      timeMax: toISOStringWithTimezone(endTime, context.timezone, -420),
      items: participants.map((email) => ({ id: email })),
      timeZone: "UTC"
    };
    const freeBusyResponse = await calendar.freebusy.query({
      requestBody: freeBusyRequest
    });
    const busySlots = [];
    const calendars = freeBusyResponse.data.calendars || {};
    const seen = /* @__PURE__ */ new Set();
    for (const email in calendars) {
      const busyPeriods = calendars[email].busy || [];
      busyPeriods.forEach((period) => {
        if (period.start && period.end) {
          busySlots.push({
            start: period.start,
            end: period.end
          });
        }
      });
    }
    const filteredFreeBusySlots = busySlots.filter((fbSlot) => {
      return !eventTimeSlots.some(
        (eventSlot) => slotsOverlap({ start: eventSlot.start, end: eventSlot.end }, { start: fbSlot.start, end: fbSlot.end })
      );
    });
    const allBusyTimes = [
      ...eventTimeSlots,
      // Busy times from events.list
      ...filteredFreeBusySlots
      // Freebusy slots that didn't overlap with events
    ].filter((slot) => {
      if (seen.has(`${new Date(slot.start).toString()}-${new Date(slot.end).toString()}`)) {
        return false;
      }
      seen.add(`${new Date(slot.start).toString()}-${new Date(slot.end).toString()}`);
      return true;
    });
    allBusyTimes.sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime());
    const availableSlots = [];
    let currentTime = roundUpToNextSlot(/* @__PURE__ */ new Date());
    if (currentTime < startTime) {
      currentTime = new Date(startTime);
    }
    while (currentTime < endTime) {
      const userOffset = getTimeZoneOffset(context.timezone, currentTime, -420);
      const localCurrentTime = new Date(currentTime.getTime() + userOffset * 60 * 1e3);
      const localHour = localCurrentTime.getHours();
      if (localHour >= workingHours.start_hour && localHour < workingHours.end_hour) {
        const slotEnd = new Date(currentTime.getTime() + duration * 6e4);
        let isAvailable = true;
        for (const busy of allBusyTimes) {
          const busyStart = new Date(busy.start);
          const busyEnd = new Date(busy.end);
          if (currentTime < busyEnd && slotEnd > busyStart) {
            isAvailable = false;
            currentTime = new Date(busyEnd.getTime());
            break;
          }
        }
        if (isAvailable) {
          console.log(
            `${LOG_PREFIX} isAvailable: ${isAvailable} currentTime: ${currentTime.toString()} toISOStringWithTimezoneL ${toISOStringWithTimezone(currentTime, context.timezone, -420)}`
          );
          const slot = {
            start: toISOStringWithTimezone(currentTime, context.timezone, -420),
            end: toISOStringWithTimezone(slotEnd, context.timezone, -420)
          };
          slot.score = calculateTimeSlotScore(slot, allBusyTimes);
          availableSlots.push(slot);
          currentTime = new Date(currentTime.getTime() + 30 * 6e4);
        }
      } else {
        const localNext = new Date(localCurrentTime);
        localNext.setDate(localNext.getDate() + 1);
        localNext.setHours(workingHours.start_hour, 0, 0, 0);
        const newOffset = getTimeZoneOffset(context.timezone, localNext, -420);
        currentTime = new Date(localNext.getTime() - newOffset * 60 * 1e3);
      }
    }
    const suggestedTimes = availableSlots.sort((a, b) => (b.score || 0) - (a.score || 0)).slice(0, 5).map((slot) => ({
      ...slot,
      score: slot.score || 0
    }));
    const message = suggestedTimes.length > 0 ? `Found ${suggestedTimes.length} optimal time slots.` : "No available time slots found";
    console.log(`${LOG_PREFIX} Found times for:
  participants: ${JSON.stringify(context.participants, null, 2)}
  timeframe: ${JSON.stringify({
      suggested_times: suggestedTimes,
      message
    }, null, 2)}`);
    return {
      suggested_times: suggestedTimes,
      message
    };
  } catch (error) {
    console.error(`${LOG_PREFIX} Finding optimal meeting time failed:
  message: ${error instanceof Error ? error.message : error}
  stack: ${error instanceof Error ? error.stack : ""}`, {
      response: error.response?.data
    });
    throw error;
  }
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  findOptimalMeetingTime
});
