"use strict";
const uuid = require("uuid");
require("./index-Br7AzmuA.cjs");
const CalEventParser = require("./CalEventParser-RR7nWuhu.cjs");
const features_repository = require("./features.repository-AjiJiuzA.cjs");
const index = require("./email-manager-NqFEofh9.cjs");
const index$1 = require("./i18n-HHdcQbuJ.cjs");
const dayjs = require("dayjs");
const dubPackage = require("dub-package");
const z = require("zod");
const customer = require("./customer-4xVYl5D0.cjs");
const Stripe = require("stripe");
const whatsappEventCancelledTemplate = (isEditingMode, locale, action, timeFormat, startTime, eventName, timeZone, attendee, name) => {
  const currentTimeFormat = timeFormat || index.TimeFormat.TWELVE_HOUR;
  const dateTimeFormat = `ddd, MMM D, YYYY ${currentTimeFormat}`;
  let eventDate;
  if (isEditingMode) {
    eventName = "{EVENT_NAME}";
    timeZone = "{TIMEZONE}";
    startTime = `{START_TIME_${currentTimeFormat}}`;
    eventDate = `{EVENT_DATE_${dateTimeFormat}}`;
    attendee = action === index$1.WorkflowActions.WHATSAPP_ATTENDEE ? "{ORGANIZER}" : "{ATTENDEE}";
    name = action === index$1.WorkflowActions.WHATSAPP_ATTENDEE ? "{ATTENDEE}" : "{ORGANIZER}";
  } else {
    eventDate = dayjs(startTime).tz(timeZone).locale(locale).format("YYYY MMM D");
    startTime = dayjs(startTime).tz(timeZone).locale(locale).format(currentTimeFormat);
  }
  const templateOne = `Hi${name ? ` ${name}` : ``}, your meeting (*${eventName}*) with ${attendee} on ${eventDate} at ${startTime} ${timeZone} has been canceled.`;
  if (templateOne.length <= 1024)
    return templateOne;
  return null;
};
const whatsappEventCompletedTemplate = (isEditingMode, locale, action, timeFormat, startTime, eventName, timeZone, attendee, name) => {
  const currentTimeFormat = timeFormat || index.TimeFormat.TWELVE_HOUR;
  const dateTimeFormat = `ddd, MMM D, YYYY ${currentTimeFormat}`;
  let eventDate;
  if (isEditingMode) {
    eventName = "{EVENT_NAME}";
    timeZone = "{TIMEZONE}";
    startTime = `{START_TIME_${currentTimeFormat}}`;
    eventDate = `{EVENT_DATE_${dateTimeFormat}}`;
    action === index$1.WorkflowActions.WHATSAPP_ATTENDEE ? "{ORGANIZER}" : "{ATTENDEE}";
    name = action === index$1.WorkflowActions.WHATSAPP_ATTENDEE ? "{ATTENDEE}" : "{ORGANIZER}";
  } else {
    eventDate = dayjs(startTime).tz(timeZone).locale(locale).format("YYYY MMM D");
    startTime = dayjs(startTime).tz(timeZone).locale(locale).format(currentTimeFormat);
  }
  const templateOne = `Hi${name ? ` ${name}` : ``}, thank you for attending the event (*${eventName}*) on ${eventDate} at ${startTime} ${timeZone}.`;
  if (templateOne.length <= 1024)
    return templateOne;
  return null;
};
const whatsappReminderTemplate = (isEditingMode, locale, action, timeFormat, startTime, eventName, timeZone, attendee, name) => {
  const currentTimeFormat = timeFormat || index.TimeFormat.TWELVE_HOUR;
  const dateTimeFormat = `ddd, MMM D, YYYY ${currentTimeFormat}`;
  let eventDate;
  if (isEditingMode) {
    eventName = "{EVENT_NAME}";
    timeZone = "{TIMEZONE}";
    startTime = `{START_TIME_${currentTimeFormat}}`;
    eventDate = `{EVENT_DATE_${dateTimeFormat}}`;
    attendee = action === index$1.WorkflowActions.WHATSAPP_ATTENDEE ? "{ORGANIZER}" : "{ATTENDEE}";
    name = action === index$1.WorkflowActions.WHATSAPP_ATTENDEE ? "{ATTENDEE}" : "{ORGANIZER}";
  } else {
    eventDate = dayjs(startTime).tz(timeZone).locale(locale).format("YYYY MMM D");
    startTime = dayjs(startTime).tz(timeZone).locale(locale).format(currentTimeFormat);
  }
  const templateOne = `Hi${name ? ` ${name}` : ``}, this is a reminder that your meeting (*${eventName}*) with ${attendee} is on ${eventDate} at ${startTime} ${timeZone}.`;
  if (templateOne.length <= 1024)
    return templateOne;
  return null;
};
const whatsappEventRescheduledTemplate = (isEditingMode, locale, action, timeFormat, startTime, eventName, timeZone, attendee, name) => {
  const currentTimeFormat = timeFormat || index.TimeFormat.TWELVE_HOUR;
  const dateTimeFormat = `ddd, MMM D, YYYY ${currentTimeFormat}`;
  let eventDate;
  if (isEditingMode) {
    eventName = "{EVENT_NAME}";
    timeZone = "{TIMEZONE}";
    startTime = `{START_TIME_${currentTimeFormat}}`;
    eventDate = `{EVENT_DATE_${dateTimeFormat}}`;
    attendee = action === index$1.WorkflowActions.WHATSAPP_ATTENDEE ? "{ORGANIZER}" : "{ATTENDEE}";
    name = action === index$1.WorkflowActions.WHATSAPP_ATTENDEE ? "{ATTENDEE}" : "{ORGANIZER}";
  } else {
    eventDate = dayjs(startTime).tz(timeZone).locale(locale).format("YYYY MMM D");
    startTime = dayjs(startTime).tz(timeZone).locale(locale).format(currentTimeFormat);
  }
  const templateOne = `Hi${name ? ` ${name}` : ``}, your meeting (*${eventName}*) with ${attendee} on ${eventDate} at ${startTime} ${timeZone} has been rescheduled.`;
  if (templateOne.length <= 1024)
    return templateOne;
  return null;
};
const emailRatingTemplate = ({
  isEditingMode,
  locale,
  action,
  t,
  timeFormat,
  startTime,
  endTime,
  eventName,
  timeZone,
  organizer,
  name,
  isBrandingDisabled,
  ratingUrl,
  noShowUrl
}) => {
  const currentTimeFormat = timeFormat || index.TimeFormat.TWELVE_HOUR;
  const dateTimeFormat = `ddd, MMM D, YYYY ${currentTimeFormat}`;
  let eventDate = "";
  if (isEditingMode) {
    endTime = "{EVENT_END_TIME}";
    eventName = "{EVENT_NAME}";
    timeZone = "{TIMEZONE}";
    organizer = "{ORGANIZER}";
    name = action === index$1.WorkflowActions.EMAIL_ATTENDEE ? "{ATTENDEE}" : "{ORGANIZER}";
    eventDate = `{EVENT_DATE_${dateTimeFormat}}`;
    ratingUrl = "{RATING_URL}";
    noShowUrl = "{NO_SHOW_URL}";
  } else {
    eventDate = dayjs(startTime).tz(timeZone).locale(locale).format(dateTimeFormat);
    endTime = dayjs(endTime).tz(timeZone).locale(locale).format(currentTimeFormat);
  }
  const emailSubject = `${t("experience_review_prompt")} ${eventName}`;
  const introHtml = `<p>${t("hi")}${name ? ` ${name}` : ""},<br><br>${t(
    "improve_customer_experience_message"
  )} ${t("meeting_satisfaction_question")}<br></p>`;
  const ratingHtml = `<h6><a href="${ratingUrl}=1">😠 </a> <a href="${ratingUrl}=2">🙁 </a> <a href="${ratingUrl}=3">😐 </a> <a href="${ratingUrl}=4">😄 </a> <a href="${ratingUrl}=5">😍</a></h6>`;
  const noShowHtml = `${organizer} ${t("meeting_not_joined_question")}<a href="${noShowUrl}"> ${t(
    "reschedule_cta_short"
  )}</a><br><br>`;
  const eventHtml = `<strong>${t("event_upper_case")}: </strong>${eventName}<br><br>`;
  const dateTimeHtml = `<strong>${t(
    "date_and_time"
  )}: </strong>${eventDate} - ${endTime} (${timeZone})<br><br>`;
  const attendeeHtml = `<strong>${t("attendees")}: </strong>${t("you_and_conjunction")} ${organizer}<br><br>`;
  const branding = !isBrandingDisabled && !isEditingMode ? `<div>_<br><br>${t("scheduling_by")} ${index$1.APP_NAME}</div>` : "";
  const endingHtml = `${t("email_survey_triggered_by_workflow")}${branding}`;
  const emailBody = `<body>${introHtml}${ratingHtml}<p>${noShowHtml}${eventHtml}${dateTimeHtml}${attendeeHtml}${endingHtml}</p></body>`;
  return { emailSubject, emailBody };
};
const emailReminderTemplate = ({
  isEditingMode,
  locale,
  t,
  action,
  timeFormat,
  startTime,
  endTime,
  eventName,
  timeZone,
  location,
  meetingUrl,
  otherPerson,
  name,
  isBrandingDisabled
}) => {
  const currentTimeFormat = timeFormat || index.TimeFormat.TWELVE_HOUR;
  const dateTimeFormat = `ddd, MMM D, YYYY ${currentTimeFormat}`;
  let eventDate = "";
  let locationString = `${CalEventParser.guessEventLocationType(location)?.label || location} ${meetingUrl}`;
  if (isEditingMode) {
    endTime = "{EVENT_END_TIME}";
    eventName = "{EVENT_NAME}";
    timeZone = "{TIMEZONE}";
    locationString = "{LOCATION} {MEETING_URL}";
    otherPerson = action === index$1.WorkflowActions.EMAIL_ATTENDEE ? "{ORGANIZER}" : "{ATTENDEE}";
    name = action === index$1.WorkflowActions.EMAIL_ATTENDEE ? "{ATTENDEE}" : "{ORGANIZER}";
    eventDate = `{EVENT_DATE_${dateTimeFormat}}`;
  } else {
    eventDate = dayjs(startTime).tz(timeZone).locale(locale).format(dateTimeFormat);
    endTime = dayjs(endTime).tz(timeZone).locale(locale).format(currentTimeFormat);
  }
  const emailSubject = `${t("reminder")}: ${eventName} - ${eventDate}`;
  const introHtml = `<body>${t("hi")}${name ? ` ${name}` : ""},<br><br>${t?.(
    "email_reminder_upcoming_event_notice"
  )}<br><br>`;
  const eventHtml = `<div><strong class="editor-text-bold">${t(
    "event_upper_case"
  )}: </strong></div>${eventName}<br><br>`;
  const dateTimeHtml = `<div><strong class="editor-text-bold">${t(
    "date_and_time"
  )}: </strong></div>${eventDate} - ${endTime} (${timeZone})<br><br>`;
  const attendeeHtml = `<div><strong class="editor-text-bold">${t("attendees")}: </strong></div>${t(
    "you_and_conjunction"
  )} ${otherPerson}<br><br>`;
  const locationHtml = `<div><strong class="editor-text-bold">${t(
    "location"
  )}: </strong></div>${locationString}<br><br>`;
  const branding = !isBrandingDisabled && !isEditingMode ? `<br><br>_<br><br>${t("scheduling_by")} ${index$1.APP_NAME}` : "";
  const endingHtml = `${t("email_reminder_triggered_by_workflow")}${branding}</body>`;
  const emailBody = introHtml + eventHtml + dateTimeHtml + attendeeHtml + locationHtml + endingHtml;
  return { emailSubject, emailBody };
};
const smsReminderTemplate = (isEditingMode, locale, action, timeFormat, startTime, eventName, timeZone, attendee, name) => {
  const currentTimeFormat = timeFormat || index.TimeFormat.TWELVE_HOUR;
  let eventDate;
  if (isEditingMode) {
    eventName = "{EVENT_NAME}";
    timeZone = "{TIMEZONE}";
    startTime = `{EVENT_TIME_${currentTimeFormat}}`;
    eventDate = "{EVENT_DATE_YYYY MMM D}";
    attendee = action === index$1.WorkflowActions.SMS_ATTENDEE ? "{ORGANIZER}" : "{ATTENDEE}";
    name = action === index$1.WorkflowActions.SMS_ATTENDEE ? "{ATTENDEE}" : "{ORGANIZER}";
  } else {
    eventDate = dayjs(startTime).tz(timeZone).locale(locale).format("YYYY MMM D");
    startTime = dayjs(startTime).tz(timeZone).locale(locale).format(currentTimeFormat);
  }
  const templateOne = `Hi${name ? ` ${name}` : ``}, this is a reminder that your meeting (${eventName}) with ${attendee} is on ${eventDate} at ${startTime} ${timeZone}.`;
  if (templateOne.length <= 320)
    return templateOne;
  const templateTwo = `Hi, this is a reminder that your meeting with ${attendee} is on ${eventDate} at ${startTime} ${timeZone}`;
  if (templateTwo.length <= 1600)
    return templateTwo;
  return null;
};
function isSMSAction(action) {
  return action === index$1.WorkflowActions.SMS_ATTENDEE || action === index$1.WorkflowActions.SMS_NUMBER;
}
function isWhatsappAction(action) {
  return action === index$1.WorkflowActions.WHATSAPP_NUMBER || action === index$1.WorkflowActions.WHATSAPP_ATTENDEE;
}
function isSMSOrWhatsappAction(action) {
  return isSMSAction(action) || isWhatsappAction(action);
}
function isCalAIAction(action) {
  return action === index$1.WorkflowActions.CAL_AI_PHONE_CALL;
}
function isEmailAction(action) {
  return action === index$1.WorkflowActions.EMAIL_ADDRESS || action === index$1.WorkflowActions.EMAIL_ATTENDEE || action === index$1.WorkflowActions.EMAIL_HOST;
}
function isAttendeeAction(action) {
  return action === index$1.WorkflowActions.SMS_ATTENDEE || action === index$1.WorkflowActions.EMAIL_ATTENDEE || action === index$1.WorkflowActions.WHATSAPP_ATTENDEE;
}
function extractPhoneNumber(responses) {
  if (!responses)
    return void 0;
  const aiAgentPhoneResponse = responses[CalEventParser.CAL_AI_AGENT_PHONE_NUMBER_FIELD];
  if (aiAgentPhoneResponse && typeof aiAgentPhoneResponse === "object" && "value" in aiAgentPhoneResponse) {
    return aiAgentPhoneResponse.value;
  }
  const attendeePhoneResponse = responses.attendeePhoneNumber;
  if (attendeePhoneResponse && typeof attendeePhoneResponse === "object" && "value" in attendeePhoneResponse) {
    return attendeePhoneResponse.value;
  }
  return void 0;
}
const createWorkflowReminderAndExtractPhone = async (args) => {
  const { evt, workflowStepId, scheduledDate, seatReferenceUid } = args;
  let attendeePhoneNumber = extractPhoneNumber(evt.responses);
  if (!attendeePhoneNumber) {
    const attendeePhone = evt.attendees?.[0]?.phoneNumber;
    if (attendeePhone) {
      attendeePhoneNumber = attendeePhone;
    } else {
      throw new Error(`No attendee phone number found for workflow step ${workflowStepId}`);
    }
  }
  const workflowReminder = await index$1.prisma.workflowReminder.create({
    data: {
      bookingUid: evt.uid,
      workflowStepId,
      method: index$1.WorkflowMethods.AI_PHONE_CALL,
      scheduledDate: scheduledDate.toDate(),
      scheduled: true,
      seatReferenceId: seatReferenceUid
    }
  });
  return { workflowReminder, attendeePhoneNumber };
};
const scheduleAIPhoneCall = async (args) => {
  const { evt, triggerEvent, timeSpan, workflowStepId, userId, teamId, seatReferenceUid, verifiedAt } = args;
  if (!verifiedAt || !workflowStepId) {
    index$1.logger.warn(`Workflow step ${workflowStepId} not yet verified or not found`);
    return;
  }
  const workflowStep = await index$1.prisma.workflowStep.findUnique({
    where: { id: workflowStepId },
    select: {
      agent: {
        select: {
          id: true,
          providerAgentId: true,
          outboundPhoneNumbers: {
            select: {
              phoneNumber: true,
              subscriptionStatus: true
            }
          }
        }
      }
    }
  });
  const activePhoneNumbers = workflowStep?.agent?.outboundPhoneNumbers?.filter(
    (phoneNumber) => phoneNumber.subscriptionStatus === index$1.PhoneNumberSubscriptionStatus.ACTIVE || phoneNumber.subscriptionStatus === null || phoneNumber.subscriptionStatus === void 0
  );
  if (!workflowStep?.agent) {
    index$1.logger.warn(`No agent configured for workflow step ${workflowStepId}`);
    return;
  }
  if (!workflowStep.agent.outboundPhoneNumbers?.length || !activePhoneNumbers?.length) {
    index$1.logger.warn(`No active outbound phone number configured for agent ${workflowStep.agent.id}`);
    return;
  }
  const featuresRepository = new features_repository.FeaturesRepository(index$1.prisma);
  const calAIVoiceAgents = await featuresRepository.checkIfFeatureIsEnabledGlobally("cal-ai-voice-agents");
  if (!calAIVoiceAgents) {
    index$1.logger.warn("Cal AI voice agents are disabled - skipping AI phone call scheduling");
    return;
  }
  const { startTime, endTime } = evt;
  const uid = evt.uid;
  const currentDate = dayjs();
  const timeUnit = timeSpan.timeUnit?.toLocaleLowerCase();
  let scheduledDate = null;
  if (triggerEvent === index$1.WorkflowTriggerEvents.BEFORE_EVENT) {
    scheduledDate = timeSpan.time && timeUnit ? dayjs(startTime).subtract(timeSpan.time, timeUnit) : null;
  } else if (triggerEvent === index$1.WorkflowTriggerEvents.AFTER_EVENT) {
    scheduledDate = timeSpan.time && timeUnit ? dayjs(endTime).add(timeSpan.time, timeUnit) : null;
  }
  if (!scheduledDate) {
    scheduledDate = currentDate;
  }
  const shouldExecuteImmediately = (
    // Immediate triggers (NEW_EVENT, EVENT_CANCELLED, etc.)
    !timeSpan.time || !timeSpan.timeUnit || // Or if the scheduled time has already passed
    scheduledDate && currentDate.isAfter(scheduledDate)
  );
  if (!shouldExecuteImmediately) {
    try {
      const { workflowReminder, attendeePhoneNumber } = await createWorkflowReminderAndExtractPhone({
        evt,
        workflowStepId,
        scheduledDate,
        seatReferenceUid
      });
      await scheduleAIPhoneCallTask({
        workflowReminderId: workflowReminder.id,
        scheduledDate: scheduledDate.toDate(),
        agentId: workflowStep.agent.id,
        phoneNumber: activePhoneNumbers[0].phoneNumber,
        attendeePhoneNumber,
        bookingUid: uid,
        userId,
        teamId,
        providerAgentId: workflowStep.agent.providerAgentId,
        referenceUid: workflowReminder.uuid || uuid.v4()
      });
      index$1.logger.info(`AI phone call scheduled for workflow step ${workflowStepId} at ${scheduledDate}`);
    } catch (error) {
      index$1.logger.error(`Error scheduling AI phone call with error ${error}`);
    }
  } else {
    try {
      const { workflowReminder, attendeePhoneNumber } = await createWorkflowReminderAndExtractPhone({
        evt,
        workflowStepId,
        scheduledDate: currentDate,
        seatReferenceUid
      });
      await scheduleAIPhoneCallTask({
        workflowReminderId: workflowReminder.id,
        scheduledDate: currentDate.toDate(),
        agentId: workflowStep.agent.id,
        phoneNumber: activePhoneNumbers[0].phoneNumber,
        attendeePhoneNumber,
        bookingUid: uid,
        userId,
        teamId,
        providerAgentId: workflowStep.agent.providerAgentId,
        referenceUid: workflowReminder.uuid || uuid.v4()
      });
      index$1.logger.info(`AI phone call scheduled for immediate execution for workflow step ${workflowStepId}`);
    } catch (error) {
      index$1.logger.error(`Error scheduling immediate AI phone call with error ${error}`);
    }
  }
};
const scheduleAIPhoneCallTask = async (args) => {
  const {
    workflowReminderId,
    scheduledDate,
    agentId,
    phoneNumber,
    attendeePhoneNumber,
    bookingUid,
    userId,
    teamId,
    providerAgentId,
    referenceUid
  } = args;
  const featuresRepository = new features_repository.FeaturesRepository(index$1.prisma);
  const calAIVoiceAgents = await featuresRepository.checkIfFeatureIsEnabledGlobally("cal-ai-voice-agents");
  if (!calAIVoiceAgents) {
    index$1.logger.warn("Cal AI voice agents are disabled - skipping AI phone call");
    return;
  }
  if (userId) {
    await index.checkRateLimitAndThrowError({
      rateLimitingType: "core",
      identifier: `ai-phone-call:${userId}`
    });
  }
  try {
    await index.tasker.create(
      "executeAIPhoneCall",
      {
        workflowReminderId,
        agentId,
        fromNumber: phoneNumber,
        toNumber: attendeePhoneNumber,
        bookingUid,
        userId,
        teamId,
        providerAgentId
      },
      {
        scheduledAt: scheduledDate,
        maxAttempts: 1,
        referenceUid
      }
    );
  } catch (error) {
    console.error("Error creating AI phone call task:", error);
    throw error;
  }
};
const deleteScheduledAIPhoneCall = async (reminderId, referenceId) => {
  const workflowReminder = await index$1.prisma.workflowReminder.findUnique({
    where: {
      id: reminderId
    }
  });
  if (!workflowReminder) {
    index$1.logger.error("AI Phone Call workflow reminder not found");
    return;
  }
  const { uuid: uuid2 } = workflowReminder;
  const taskReferenceId = referenceId || uuid2;
  if (taskReferenceId) {
    try {
      const taskId = await index.tasker.cancelWithReference(taskReferenceId, "executeAIPhoneCall");
      if (taskId) {
        await index$1.prisma.workflowReminder.delete({
          where: {
            id: reminderId
          }
        });
        index$1.logger.info(`AI phone call reminder ${reminderId} cancelled and deleted`);
        return;
      }
    } catch (error) {
      index$1.logger.error(`Error canceling/deleting AI phone call reminder with tasker. Error: ${error}`);
    }
  }
  try {
    await index$1.prisma.workflowReminder.delete({
      where: {
        id: reminderId
      }
    });
    index$1.logger.info(`AI phone call reminder ${reminderId} deleted (no task to cancel)`);
  } catch (error) {
    index$1.logger.error(`Error deleting AI phone call reminder ${reminderId}: ${error}`);
  }
};
function getWorkflowRecipientEmail({
  action,
  organizerEmail,
  attendeeEmail,
  sendToEmail
}) {
  switch (action) {
    case "EMAIL_ADDRESS":
      return sendToEmail || null;
    case "EMAIL_HOST":
      return organizerEmail || null;
    case "EMAIL_ATTENDEE":
      return attendeeEmail || null;
    case "SMS_ATTENDEE":
      return attendeeEmail || null;
    case "WHATSAPP_ATTENDEE":
      return attendeeEmail || null;
    case "SMS_NUMBER":
    case "WHATSAPP_NUMBER":
      return null;
    default:
      return null;
  }
}
function replaceVariablePlaceholders(text) {
  return text.replace(/\{([A-Z0-9_]+)_VARIABLE}/g, (_, base) => `{${base}}`);
}
const customTemplate = (text, variables, locale, timeFormat, isBrandingDisabled) => {
  const translatedDate = new Intl.DateTimeFormat(locale, {
    weekday: "long",
    month: "long",
    day: "numeric",
    year: "numeric"
  }).format(variables.eventDate?.add(dayjs().tz(variables.timeZone).utcOffset(), "minute").toDate());
  let locationString = variables.location || "";
  text = replaceVariablePlaceholders(text);
  if (text.includes("{LOCATION}")) {
    locationString = CalEventParser.guessEventLocationType(locationString)?.label || locationString;
  }
  const cancelLink = variables.cancelLink ?? "";
  const rescheduleLink = variables.rescheduleLink ?? "";
  const currentTimeFormat = timeFormat || index.TimeFormat.TWELVE_HOUR;
  const attendeeNameWords = variables.attendeeName?.trim().split(" ");
  const attendeeNameWordCount = attendeeNameWords?.length ?? 0;
  const attendeeFirstName = variables.attendeeFirstName ? variables.attendeeFirstName : attendeeNameWords?.[0] ?? "";
  const attendeeLastName = variables.attendeeLastName ? variables.attendeeLastName : attendeeNameWordCount > 1 ? attendeeNameWords[attendeeNameWordCount - 1] : "";
  let dynamicText = text.replaceAll("{EVENT_NAME}", variables.eventName || "").replaceAll("{ORGANIZER}", variables.organizerName || "").replaceAll("{ATTENDEE}", variables.attendeeName || "").replaceAll("{ORGANIZER_NAME}", variables.organizerName || "").replaceAll("{ATTENDEE_NAME}", variables.attendeeName || "").replaceAll("{ATTENDEE_FIRST_NAME}", attendeeFirstName).replaceAll("{ATTENDEE_LAST_NAME}", attendeeLastName).replaceAll("{EVENT_DATE}", translatedDate).replaceAll("{EVENT_TIME}", variables.eventDate?.format(currentTimeFormat) || "").replaceAll("{START_TIME}", variables.eventDate?.format(currentTimeFormat) || "").replaceAll("{EVENT_END_TIME}", variables.eventEndTime?.format(currentTimeFormat) || "").replaceAll("{LOCATION}", locationString).replaceAll("{ADDITIONAL_NOTES}", variables.additionalNotes || "").replaceAll("{ATTENDEE_EMAIL}", variables.attendeeEmail || "").replaceAll("{TIMEZONE}", variables.timeZone || "").replaceAll("{CANCEL_URL}", cancelLink).replaceAll("{CANCELLATION_REASON}", variables.cancelReason || "").replaceAll("{RESCHEDULE_URL}", rescheduleLink).replaceAll("{RESCHEDULE_REASON}", variables.rescheduleReason || "").replaceAll("{MEETING_URL}", variables.meetingUrl || "").replaceAll("{RATING_URL}", variables.ratingUrl || "").replaceAll("{NO_SHOW_URL}", variables.noShowUrl || "").replaceAll("{ATTENDEE_TIMEZONE}", variables.attendeeTimezone || "").replaceAll(
    "{EVENT_START_TIME_IN_ATTENDEE_TIMEZONE}",
    variables.eventTimeInAttendeeTimezone?.format(currentTimeFormat) || ""
  ).replaceAll(
    "{EVENT_END_TIME_IN_ATTENDEE_TIMEZONE}",
    variables.eventEndTimeInAttendeeTimezone?.format(currentTimeFormat) || ""
  );
  const customInputvariables = dynamicText.match(/\{(.+?)}/g)?.map((variable) => {
    return variable.replace("{", "").replace("}", "");
  });
  customInputvariables?.forEach((variable) => {
    if (variable.startsWith("EVENT_DATE_") || variable.startsWith("EVENT_TIME_") || variable.startsWith("START_TIME_")) {
      const dateFormat = variable.substring(11, text.length);
      const formattedDate = variables.eventDate?.locale(locale).format(dateFormat);
      dynamicText = dynamicText.replace(`{${variable}}`, formattedDate || "");
      return;
    }
    if (variable.startsWith("EVENT_END_TIME_")) {
      const dateFormat = variable.substring(15, text.length);
      const formattedDate = variables.eventEndTime?.locale(locale).format(dateFormat);
      dynamicText = dynamicText.replace(`{${variable}}`, formattedDate || "");
      return;
    }
    if (variables.responses) {
      Object.keys(variables.responses).forEach((customInput) => {
        const formatedToVariable = customInput.replace(/[^a-zA-Z0-9 ]/g, "").trim().replaceAll(" ", "_").toUpperCase();
        if (variable === formatedToVariable && variables.responses && variables.responses[customInput].value) {
          dynamicText = dynamicText.replace(
            `{${variable}}`,
            variables.responses[customInput].value.toString()
          );
        }
      });
    }
  });
  const branding = !isBrandingDisabled ? `<br><br>_<br><br>Scheduling by ${index$1.APP_NAME}` : "";
  const textHtml = `<body style="white-space: pre-wrap;">${dynamicText}${branding}</body>`;
  return { text: dynamicText, html: textHtml };
};
const log$4 = index$1.logger.getSubLogger({ prefix: ["[emailReminderManager]"] });
const scheduleEmailReminder = async (args) => {
  const {
    evt,
    triggerEvent,
    timeSpan,
    template,
    sender,
    workflowStepId,
    seatReferenceUid,
    sendTo,
    emailSubject = "",
    emailBody = "",
    hideBranding,
    includeCalendarEvent,
    action,
    verifiedAt
  } = args;
  if (!verifiedAt) {
    log$4.warn(`Workflow step ${workflowStepId} not yet verified`);
    return;
  }
  const { startTime, endTime } = evt;
  const uid = evt.uid;
  const timeUnit = timeSpan.timeUnit?.toLocaleLowerCase();
  let scheduledDate = null;
  if (triggerEvent === index$1.WorkflowTriggerEvents.BEFORE_EVENT) {
    scheduledDate = timeSpan.time && timeUnit ? dayjs(startTime).subtract(timeSpan.time, timeUnit) : null;
  } else if (triggerEvent === index$1.WorkflowTriggerEvents.AFTER_EVENT) {
    scheduledDate = timeSpan.time && timeUnit ? dayjs(endTime).add(timeSpan.time, timeUnit) : null;
  }
  let attendeeToBeUsedInMail = null;
  let name = "";
  let attendeeName = "";
  let timeZone = "";
  switch (action) {
    case index$1.WorkflowActions.EMAIL_ADDRESS:
      name = "";
      attendeeToBeUsedInMail = evt.attendees[0];
      attendeeName = evt.attendees[0].name;
      timeZone = evt.organizer.timeZone;
      break;
    case index$1.WorkflowActions.EMAIL_HOST:
      attendeeToBeUsedInMail = evt.attendees[0];
      name = evt.organizer.name;
      attendeeName = attendeeToBeUsedInMail.name;
      timeZone = evt.organizer.timeZone;
      break;
    case index$1.WorkflowActions.EMAIL_ATTENDEE:
      const attendeeEmailToBeUsedInMailFromEvt = evt.attendees.find(
        (attendee) => attendee.email === sendTo[0]
      );
      attendeeToBeUsedInMail = attendeeEmailToBeUsedInMailFromEvt ? attendeeEmailToBeUsedInMailFromEvt : evt.attendees[0];
      name = attendeeToBeUsedInMail.name;
      attendeeName = evt.organizer.name;
      timeZone = attendeeToBeUsedInMail.timeZone;
      break;
  }
  let emailContent = {
    emailSubject,
    emailBody: `<body style="white-space: pre-wrap;">${emailBody}</body>`
  };
  const bookerUrl = evt.bookerUrl ?? index$1.WEBSITE_URL;
  if (emailBody) {
    const isEmailAttendeeAction = action === index$1.WorkflowActions.EMAIL_ATTENDEE;
    const recipientEmail = getWorkflowRecipientEmail({
      action,
      attendeeEmail: attendeeToBeUsedInMail.email,
      organizerEmail: evt.organizer.email,
      sendToEmail: sendTo[0]
    });
    const variables = {
      eventName: evt.title || "",
      organizerName: evt.organizer.name,
      attendeeName: attendeeToBeUsedInMail.name,
      attendeeFirstName: attendeeToBeUsedInMail.firstName,
      attendeeLastName: attendeeToBeUsedInMail.lastName,
      attendeeEmail: attendeeToBeUsedInMail.email,
      eventDate: dayjs(startTime).tz(timeZone),
      eventEndTime: dayjs(endTime).tz(timeZone),
      timeZone,
      location: evt.location,
      additionalNotes: evt.additionalNotes,
      responses: evt.responses,
      meetingUrl: index$1.bookingMetadataSchema.parse(evt.metadata || {})?.videoCallUrl,
      cancelLink: `${bookerUrl}/booking/${evt.uid}?cancel=true${recipientEmail ? `&cancelledBy=${encodeURIComponent(recipientEmail)}` : ""}${isEmailAttendeeAction && seatReferenceUid ? `&seatReferenceUid=${seatReferenceUid}` : ""}`,
      cancelReason: evt.cancellationReason,
      rescheduleLink: `${bookerUrl}/reschedule/${evt.uid}${recipientEmail ? `?rescheduledBy=${encodeURIComponent(recipientEmail)}${isEmailAttendeeAction && seatReferenceUid ? `&seatReferenceUid=${encodeURIComponent(seatReferenceUid)}` : ""}` : isEmailAttendeeAction && seatReferenceUid ? `?seatReferenceUid=${encodeURIComponent(seatReferenceUid)}` : ""}`,
      rescheduleReason: evt.rescheduleReason,
      ratingUrl: `${bookerUrl}/booking/${evt.uid}?rating`,
      noShowUrl: `${bookerUrl}/booking/${evt.uid}?noShow=true`,
      attendeeTimezone: evt.attendees[0].timeZone,
      eventTimeInAttendeeTimezone: dayjs(startTime).tz(evt.attendees[0].timeZone),
      eventEndTimeInAttendeeTimezone: dayjs(endTime).tz(evt.attendees[0].timeZone)
    };
    const locale = isEmailAttendeeAction ? attendeeToBeUsedInMail.language?.locale : evt.organizer.language.locale;
    const emailSubjectTemplate = customTemplate(emailSubject, variables, locale, evt.organizer.timeFormat);
    emailContent.emailSubject = emailSubjectTemplate.text;
    emailContent.emailBody = customTemplate(
      emailBody,
      variables,
      locale,
      evt.organizer.timeFormat,
      hideBranding
    ).html;
  } else if (template === index$1.WorkflowTemplates.REMINDER) {
    emailContent = emailReminderTemplate({
      isEditingMode: false,
      locale: evt.organizer.language.locale,
      t: await index$1.getTranslation(evt.organizer.language.locale || "en", "common"),
      action,
      timeFormat: evt.organizer.timeFormat,
      startTime,
      endTime,
      eventName: evt.title,
      timeZone,
      location: evt.location || "",
      meetingUrl: index$1.bookingMetadataSchema.parse(evt.metadata || {})?.videoCallUrl || "",
      otherPerson: attendeeName,
      name
    });
  } else if (template === index$1.WorkflowTemplates.RATING) {
    emailContent = emailRatingTemplate({
      isEditingMode: true,
      locale: evt.organizer.language.locale,
      action,
      t: await index$1.getTranslation(evt.organizer.language.locale || "en", "common"),
      timeFormat: evt.organizer.timeFormat,
      startTime,
      endTime,
      eventName: evt.title,
      timeZone,
      organizer: evt.organizer.name,
      name,
      ratingUrl: `${bookerUrl}/booking/${evt.uid}?rating`,
      noShowUrl: `${bookerUrl}/booking/${evt.uid}?noShow=true`
    });
  }
  log$4.debug(`Sending Email for trigger ${triggerEvent}`, JSON.stringify(emailContent));
  async function prepareEmailData() {
    const status = triggerEvent === index$1.WorkflowTriggerEvents.EVENT_CANCELLED ? "CANCELLED" : "CONFIRMED";
    const organizerT = await index$1.getTranslation(evt.organizer.language.locale || "en", "common");
    const attendeeT = await index$1.getTranslation(evt.attendees[0].language.locale || "en", "common");
    const attendee = {
      ...evt.attendees[0],
      name: index$1.preprocessNameFieldDataWithVariant("fullName", evt.attendees[0].name),
      language: { ...evt.attendees[0].language, translate: attendeeT }
    };
    const emailEvent = {
      ...evt,
      type: evt.eventType?.slug || "",
      organizer: { ...evt.organizer, language: { ...evt.organizer.language, translate: organizerT } },
      attendees: [attendee],
      location: index$1.bookingMetadataSchema.parse(evt.metadata || {})?.videoCallUrl || evt.location
    };
    const attachments = includeCalendarEvent ? [
      {
        content: index.generateIcsString({
          event: emailEvent,
          status
        }) || "",
        filename: "event.ics",
        type: "text/calendar; method=REQUEST",
        disposition: "attachment",
        contentId: uuid.v4()
      }
    ] : void 0;
    return {
      subject: emailContent.emailSubject,
      html: emailContent.emailBody,
      ...!evt.hideOrganizerEmail && { replyTo: evt?.eventType?.customReplyToEmail || evt.organizer.email },
      attachments,
      sender
    };
  }
  const mailData = await prepareEmailData();
  let reminderUid = void 0;
  if (scheduledDate) {
    const reminder = await index$1.prisma.workflowReminder.create({
      data: {
        bookingUid: uid,
        workflowStepId,
        method: index$1.WorkflowMethods.EMAIL,
        scheduledDate: scheduledDate.toDate(),
        scheduled: true
      }
    });
    reminderUid = reminder.uuid;
  }
  await index.sendOrScheduleWorkflowEmails({
    ...mailData,
    to: sendTo,
    sendAt: scheduledDate?.toDate(),
    referenceUid: reminderUid ?? void 0
  });
};
const deleteScheduledEmailReminder = async (reminderId) => {
  const workflowReminder = await index$1.prisma.workflowReminder.findUnique({
    where: {
      id: reminderId
    }
  });
  if (!workflowReminder) {
    console.error("Workflow reminder not found");
    return;
  }
  const { uuid: uuid2, referenceId } = workflowReminder;
  if (uuid2) {
    try {
      const taskId = await index.tasker.cancelWithReference(uuid2, "sendWorkflowEmails");
      if (taskId) {
        await index$1.prisma.workflowReminder.delete({
          where: {
            id: reminderId
          }
        });
        return;
      }
    } catch (error) {
      log$4.error(`Error canceling/deleting reminder with tasker. Error: ${error}`);
    }
  }
  try {
    if (!referenceId) {
      await index$1.prisma.workflowReminder.delete({
        where: {
          id: reminderId
        }
      });
      return;
    }
    await index$1.prisma.workflowReminder.update({
      where: {
        id: reminderId
      },
      data: {
        cancelled: true
      }
    });
  } catch (error) {
    log$4.error(`Error canceling reminder with error ${error}`);
  }
};
const dub = new dubPackage.Dub({
  token: process.env.DUB_API_KEY
});
[
  index$1.WorkflowTriggerEvents.BEFORE_EVENT,
  index$1.WorkflowTriggerEvents.EVENT_CANCELLED,
  index$1.WorkflowTriggerEvents.NEW_EVENT,
  index$1.WorkflowTriggerEvents.AFTER_EVENT,
  index$1.WorkflowTriggerEvents.RESCHEDULE_EVENT,
  index$1.WorkflowTriggerEvents.AFTER_HOSTS_CAL_VIDEO_NO_SHOW,
  index$1.WorkflowTriggerEvents.AFTER_GUESTS_CAL_VIDEO_NO_SHOW,
  index$1.WorkflowTriggerEvents.BOOKING_REJECTED,
  index$1.WorkflowTriggerEvents.BOOKING_REQUESTED,
  index$1.WorkflowTriggerEvents.BOOKING_PAYMENT_INITIATED,
  index$1.WorkflowTriggerEvents.BOOKING_PAID,
  index$1.WorkflowTriggerEvents.BOOKING_NO_SHOW_UPDATED
];
[
  index$1.WorkflowActions.EMAIL_HOST,
  index$1.WorkflowActions.EMAIL_ATTENDEE,
  index$1.WorkflowActions.EMAIL_ADDRESS,
  index$1.WorkflowActions.SMS_ATTENDEE,
  index$1.WorkflowActions.SMS_NUMBER,
  index$1.WorkflowActions.WHATSAPP_ATTENDEE,
  index$1.WorkflowActions.WHATSAPP_NUMBER,
  index$1.WorkflowActions.CAL_AI_PHONE_CALL
];
[index$1.TimeUnit.DAY, index$1.TimeUnit.HOUR, index$1.TimeUnit.MINUTE];
[
  index$1.WorkflowTemplates.CUSTOM,
  index$1.WorkflowTemplates.REMINDER,
  index$1.WorkflowTemplates.RATING,
  index$1.WorkflowTemplates.CANCELLED,
  index$1.WorkflowTemplates.COMPLETED,
  index$1.WorkflowTemplates.RESCHEDULED
];
[index$1.WorkflowTemplates.CUSTOM, index$1.WorkflowTemplates.REMINDER];
[
  index$1.WorkflowTemplates.CUSTOM,
  index$1.WorkflowTemplates.REMINDER,
  index$1.WorkflowTemplates.RATING
];
[
  index$1.WorkflowTemplates.REMINDER,
  index$1.WorkflowTemplates.COMPLETED,
  index$1.WorkflowTemplates.CANCELLED,
  index$1.WorkflowTemplates.RESCHEDULED
];
const IMMEDIATE_WORKFLOW_TRIGGER_EVENTS = [
  index$1.WorkflowTriggerEvents.NEW_EVENT,
  index$1.WorkflowTriggerEvents.EVENT_CANCELLED,
  index$1.WorkflowTriggerEvents.RESCHEDULE_EVENT,
  index$1.WorkflowTriggerEvents.BOOKING_NO_SHOW_UPDATED,
  index$1.WorkflowTriggerEvents.BOOKING_PAID,
  index$1.WorkflowTriggerEvents.BOOKING_PAYMENT_INITIATED,
  index$1.WorkflowTriggerEvents.BOOKING_REJECTED,
  index$1.WorkflowTriggerEvents.BOOKING_REQUESTED
];
const bulkShortenLinks = async (links) => {
  if (!process.env.DUB_API_KEY) {
    return links.map((link) => ({ shortLink: link }));
  }
  const linksToShorten = links.filter((link) => link);
  const results = await dub.links.createMany(
    linksToShorten.map((link) => ({
      domain: "sms.cal.com",
      url: link,
      folderId: "fold_wx3NZDKQYbLDbncSubeMu0ss"
    }))
  );
  return links.map((link) => {
    const createdLink = results.find(
      (result) => !("error" in result) && result.url === link
    );
    if (createdLink) {
      return { shortLink: createdLink.shortLink };
    } else {
      return { shortLink: link };
    }
  });
};
const getSMSMessageWithVariables = async (smsMessage, evt, attendeeToBeUsedInSMS, action) => {
  const recipientEmail = getWorkflowRecipientEmail({
    action,
    attendeeEmail: attendeeToBeUsedInSMS.email
  });
  const urls = {
    meetingUrl: index$1.bookingMetadataSchema.parse(evt.metadata || {})?.videoCallUrl || "",
    cancelLink: `${evt.bookerUrl ?? index$1.WEBSITE_URL}/booking/${evt.uid}?cancel=true${recipientEmail ? `&cancelledBy=${recipientEmail}` : ""}`,
    rescheduleLink: `${evt.bookerUrl ?? index$1.WEBSITE_URL}/reschedule/${evt.uid}${recipientEmail ? `?rescheduledBy=${recipientEmail}` : ""}`
  };
  const [{ shortLink: meetingUrl }, { shortLink: cancelLink }, { shortLink: rescheduleLink }] = await bulkShortenLinks([urls.meetingUrl, urls.cancelLink, urls.rescheduleLink]);
  const timeZone = action === index$1.WorkflowActions.SMS_ATTENDEE ? attendeeToBeUsedInSMS.timeZone : evt.organizer.timeZone;
  const variables = {
    eventName: evt.title,
    organizerName: evt.organizer.name,
    attendeeName: attendeeToBeUsedInSMS.name,
    attendeeFirstName: attendeeToBeUsedInSMS.firstName,
    attendeeLastName: attendeeToBeUsedInSMS.lastName,
    attendeeEmail: attendeeToBeUsedInSMS.email,
    eventDate: dayjs(evt.startTime).tz(timeZone),
    eventEndTime: dayjs(evt.endTime).tz(timeZone),
    timeZone,
    location: evt.location,
    additionalNotes: evt.additionalNotes,
    responses: evt.responses,
    meetingUrl,
    cancelLink,
    rescheduleLink,
    cancelReason: evt.cancellationReason,
    rescheduleReason: evt.rescheduleReason,
    attendeeTimezone: evt.attendees[0].timeZone,
    eventTimeInAttendeeTimezone: dayjs(evt.startTime).tz(evt.attendees[0].timeZone),
    eventEndTimeInAttendeeTimezone: dayjs(evt.endTime).tz(evt.attendees[0].timeZone)
  };
  const locale = action === index$1.WorkflowActions.SMS_ATTENDEE ? attendeeToBeUsedInSMS.language?.locale : evt.organizer.language.locale;
  const customMessage = customTemplate(smsMessage, variables, locale, evt.organizer.timeFormat);
  smsMessage = customMessage.text;
  return smsMessage;
};
const getAttendeeToBeUsedInSMS = (action, evt, reminderPhone) => {
  let attendeeToBeUsedInSMS = null;
  if (action === index$1.WorkflowActions.SMS_ATTENDEE) {
    const attendeeWithReminderPhoneAsSMSReminderNumber = reminderPhone && evt.attendees.find((attendee) => attendee.email === evt.responses?.email?.value);
    attendeeToBeUsedInSMS = attendeeWithReminderPhoneAsSMSReminderNumber ? attendeeWithReminderPhoneAsSMSReminderNumber : evt.attendees[0];
  } else {
    attendeeToBeUsedInSMS = evt.attendees[0];
  }
  return attendeeToBeUsedInSMS;
};
const shouldUseTwilio = (trigger, scheduledDate) => {
  if (IMMEDIATE_WORKFLOW_TRIGGER_EVENTS.includes(trigger)) {
    return true;
  }
  if (trigger === index$1.WorkflowTriggerEvents.BEFORE_EVENT || trigger === index$1.WorkflowTriggerEvents.AFTER_EVENT) {
    const currentDate = dayjs();
    if (scheduledDate && currentDate.isBefore(scheduledDate.subtract(15, "minute")) && !scheduledDate.isAfter(currentDate.add(2, "hour"))) {
      return true;
    }
  }
  return false;
};
class WorkflowOptOutContactRepository {
  static async addPhoneNumber(phoneNumber) {
    await index$1.prisma.workflowOptOutContact.upsert({
      where: {
        type_value: {
          type: index$1.WorkflowContactType.PHONE,
          value: phoneNumber
        }
      },
      update: {
        optedOut: true
      },
      create: {
        type: index$1.WorkflowContactType.PHONE,
        optedOut: true,
        value: phoneNumber
      }
    });
  }
  static async removePhoneNumber(phoneNumber) {
    await index$1.prisma.workflowOptOutContact.update({
      where: {
        type_value: {
          type: index$1.WorkflowContactType.PHONE,
          value: phoneNumber
        }
      },
      data: {
        optedOut: false
      }
    });
  }
  static async isOptedOut(phoneNumber) {
    const optOutContact = await index$1.prisma.workflowOptOutContact.findFirst({
      where: {
        type: index$1.WorkflowContactType.PHONE,
        value: phoneNumber,
        optedOut: true
      },
      select: {
        optedOut: true
      }
    });
    return !!optOutContact?.optedOut;
  }
}
class WorkflowReminderRepository {
  static getFutureScheduledAttendeeSMSReminders(phoneNumber) {
    return index$1.prisma.workflowReminder.findMany({
      where: {
        method: index$1.WorkflowMethods.SMS,
        cancelled: null,
        scheduledDate: { gte: /* @__PURE__ */ new Date() },
        booking: {
          smsReminderNumber: phoneNumber
        },
        workflowStep: {
          action: index$1.WorkflowActions.SMS_ATTENDEE
        }
      }
    });
  }
  static deleteWorkflowReminders(ids) {
    if (!ids.length) {
      return [];
    }
    return index$1.prisma.workflowReminder.deleteMany({
      where: {
        id: {
          in: ids
        }
      }
    });
  }
}
class WorkflowOptOutService {
  static async optOutPhoneNumber(phoneNumber) {
    await WorkflowOptOutContactRepository.addPhoneNumber(phoneNumber);
    const scheduledReminders = await WorkflowReminderRepository.getFutureScheduledAttendeeSMSReminders(
      phoneNumber
    );
    await index.deleteMultipleScheduledSMS(
      scheduledReminders.filter((reminder) => !!reminder.referenceId).map((reminder) => reminder.referenceId)
    );
    await WorkflowReminderRepository.deleteWorkflowReminders(
      scheduledReminders.filter((reminder) => !reminder.referenceId).map((reminder) => reminder.id)
    );
  }
  static async addOptOutMessage(message, locale) {
    const t = await index$1.getTranslation(locale, "common");
    return `${message}

${t("sms_opt_out_message")}`;
  }
}
const log$3 = index$1.logger.getSubLogger({ prefix: ["[smsReminderManager]"] });
const scheduleSMSReminder = async (args) => {
  const {
    evt,
    reminderPhone,
    triggerEvent,
    action,
    timeSpan,
    message = "",
    workflowStepId,
    template,
    sender,
    userId,
    teamId,
    isVerificationPending = false,
    seatReferenceUid,
    verifiedAt
  } = args;
  if (!verifiedAt) {
    log$3.warn(`Workflow step ${workflowStepId} not yet verified`);
    return;
  }
  if (reminderPhone && await WorkflowOptOutContactRepository.isOptedOut(reminderPhone)) {
    log$3.warn(
      `Phone number opted out of SMS workflows`,
      index$1.safeStringify({ workflowStep: workflowStepId, eventUid: evt.uid })
    );
    return;
  }
  const { startTime, endTime } = evt;
  const uid = evt.uid;
  const timeUnit = timeSpan.timeUnit?.toLocaleLowerCase();
  let scheduledDate = null;
  const senderID = index.getSenderId(reminderPhone, sender || index$1.SENDER_ID);
  async function getIsNumberVerified() {
    if (action === index$1.WorkflowActions.SMS_ATTENDEE)
      return true;
    const verifiedNumber = await index$1.prisma.verifiedNumber.findFirst({
      where: {
        OR: [{ userId }, { teamId }],
        phoneNumber: reminderPhone || ""
      }
    });
    if (!!verifiedNumber)
      return true;
    return isVerificationPending;
  }
  const isNumberVerified = await getIsNumberVerified();
  if (triggerEvent === index$1.WorkflowTriggerEvents.BEFORE_EVENT) {
    scheduledDate = timeSpan.time && timeUnit ? dayjs(startTime).subtract(timeSpan.time, timeUnit) : null;
  } else if (triggerEvent === index$1.WorkflowTriggerEvents.AFTER_EVENT) {
    scheduledDate = timeSpan.time && timeUnit ? dayjs(endTime).add(timeSpan.time, timeUnit) : null;
  }
  if (reminderPhone && isNumberVerified) {
    const useTwilio = shouldUseTwilio(triggerEvent, scheduledDate);
    if (useTwilio) {
      const attendeeToBeUsedInSMS = getAttendeeToBeUsedInSMS(action, evt, reminderPhone);
      const name = action === index$1.WorkflowActions.SMS_ATTENDEE ? attendeeToBeUsedInSMS.name : "";
      const attendeeName = action === index$1.WorkflowActions.SMS_ATTENDEE ? evt.organizer.name : attendeeToBeUsedInSMS.name;
      const timeZone = action === index$1.WorkflowActions.SMS_ATTENDEE ? attendeeToBeUsedInSMS.timeZone : evt.organizer.timeZone;
      let smsMessage = message;
      if (smsMessage) {
        smsMessage = await getSMSMessageWithVariables(smsMessage, evt, attendeeToBeUsedInSMS, action);
      } else if (template === index$1.WorkflowTemplates.REMINDER) {
        smsMessage = smsReminderTemplate(
          false,
          evt.organizer.language.locale,
          action,
          evt.organizer.timeFormat,
          evt.startTime,
          evt.title,
          timeZone,
          attendeeName,
          name
        ) || message;
      }
      if (smsMessage.length > 0) {
        const smsMessageWithoutOptOut = smsMessage;
        if (process.env.TWILIO_OPT_OUT_ENABLED === "true") {
          smsMessage = await WorkflowOptOutService.addOptOutMessage(
            smsMessage,
            evt.organizer.language.locale
          );
        }
        log$3.debug(`Sending sms for trigger ${triggerEvent}`, smsMessage);
        if (IMMEDIATE_WORKFLOW_TRIGGER_EVENTS.includes(triggerEvent)) {
          try {
            await index.sendSmsOrFallbackEmail({
              twilioData: {
                phoneNumber: reminderPhone,
                body: smsMessage,
                sender: senderID,
                bodyWithoutOptOut: smsMessageWithoutOptOut,
                bookingUid: evt.uid,
                userId,
                teamId
              },
              fallbackData: isAttendeeAction(action) ? {
                email: evt.attendees[0].email,
                t: await index$1.getTranslation(evt.attendees[0].language.locale, "common"),
                replyTo: evt.organizer.email
              } : void 0
            });
          } catch (error) {
            log$3.error(`Error sending SMS with error ${error}`);
          }
        }
        if ((triggerEvent === index$1.WorkflowTriggerEvents.BEFORE_EVENT || triggerEvent === index$1.WorkflowTriggerEvents.AFTER_EVENT) && scheduledDate) {
          try {
            const scheduledNotification = await index.scheduleSmsOrFallbackEmail({
              twilioData: {
                phoneNumber: reminderPhone,
                body: smsMessage,
                scheduledDate: scheduledDate.toDate(),
                sender: senderID,
                bookingUid: evt.uid,
                userId,
                teamId
              },
              fallbackData: isAttendeeAction(action) ? {
                email: evt.attendees[0].email,
                t: await index$1.getTranslation(evt.attendees[0].language.locale, "common"),
                replyTo: evt.organizer.email,
                workflowStepId
              } : void 0
            });
            if (scheduledNotification?.sid) {
              await index$1.prisma.workflowReminder.create({
                data: {
                  bookingUid: uid,
                  workflowStepId,
                  method: index$1.WorkflowMethods.SMS,
                  scheduledDate: scheduledDate.toDate(),
                  scheduled: true,
                  referenceId: scheduledNotification.sid,
                  seatReferenceId: seatReferenceUid
                }
              });
            }
          } catch (error) {
            log$3.error(`Error scheduling SMS with error ${error}`);
          }
        }
      }
      return;
    }
    if (!useTwilio && scheduledDate) {
      await index$1.prisma.workflowReminder.create({
        data: {
          bookingUid: uid,
          workflowStepId,
          method: index$1.WorkflowMethods.SMS,
          scheduledDate: scheduledDate.toDate(),
          scheduled: false,
          seatReferenceId: seatReferenceUid
        }
      });
    }
  }
};
const deleteScheduledSMSReminder = async (reminderId, referenceId) => {
  try {
    if (referenceId) {
      await index.cancelSMS(referenceId);
    }
    await index$1.prisma.workflowReminder.delete({
      where: {
        id: reminderId
      }
    });
  } catch (error) {
    log$3.error(`Error canceling reminder with error ${error}`);
  }
};
const getContentSidForTemplate = (template) => {
  if (!template) {
    return process.env.TWILIO_WHATSAPP_REMINDER_CONTENT_SID;
  }
  switch (template) {
    case index$1.WorkflowTemplates.REMINDER:
      return process.env.TWILIO_WHATSAPP_REMINDER_CONTENT_SID;
    case index$1.WorkflowTemplates.CANCELLED:
      return process.env.TWILIO_WHATSAPP_CANCELLED_CONTENT_SID;
    case index$1.WorkflowTemplates.RESCHEDULED:
      return process.env.TWILIO_WHATSAPP_RESCHEDULED_CONTENT_SID;
    case index$1.WorkflowTemplates.COMPLETED:
      return process.env.TWILIO_WHATSAPP_COMPLETED_CONTENT_SID;
    default:
      return process.env.TWILIO_WHATSAPP_REMINDER_CONTENT_SID;
  }
};
const getContentVariablesForTemplate = (variables) => {
  const { name, attendeeName, eventName, eventDate, startTime, timeZone } = variables;
  const commonVariables = {
    "1": name || "",
    "2": attendeeName || "",
    "3": eventName || "",
    "4": eventDate || "",
    "5": `${startTime ?? ""} ${timeZone ?? ""}`
  };
  return commonVariables;
};
const log$2 = index$1.logger.getSubLogger({ prefix: ["[whatsappReminderManager]"] });
const scheduleWhatsappReminder = async (args) => {
  const {
    evt,
    reminderPhone,
    triggerEvent,
    action,
    timeSpan,
    message = "",
    workflowStepId,
    template,
    userId,
    teamId,
    isVerificationPending = false,
    seatReferenceUid,
    verifiedAt
  } = args;
  if (!verifiedAt) {
    log$2.warn(`Workflow step ${workflowStepId} not verified`);
    return;
  }
  const { startTime, endTime } = evt;
  const uid = evt.uid;
  const currentDate = dayjs();
  const timeUnit = timeSpan.timeUnit?.toLocaleLowerCase();
  let scheduledDate = null;
  async function getIsNumberVerified() {
    if (action === index$1.WorkflowActions.WHATSAPP_ATTENDEE)
      return true;
    const verifiedNumber = await index$1.prisma.verifiedNumber.findFirst({
      where: {
        OR: [{ userId }, { teamId }],
        phoneNumber: reminderPhone || ""
      }
    });
    if (!!verifiedNumber)
      return true;
    return isVerificationPending;
  }
  const isNumberVerified = await getIsNumberVerified();
  if (triggerEvent === index$1.WorkflowTriggerEvents.BEFORE_EVENT) {
    scheduledDate = timeSpan.time && timeUnit ? dayjs(startTime).subtract(timeSpan.time, timeUnit) : null;
  } else if (triggerEvent === index$1.WorkflowTriggerEvents.AFTER_EVENT) {
    scheduledDate = timeSpan.time && timeUnit ? dayjs(endTime).add(timeSpan.time, timeUnit) : null;
  }
  const name = action === index$1.WorkflowActions.WHATSAPP_ATTENDEE ? evt.attendees[0].name : evt.organizer.name;
  const attendeeName = action === index$1.WorkflowActions.WHATSAPP_ATTENDEE ? evt.organizer.name : evt.attendees[0].name;
  const timeZone = action === index$1.WorkflowActions.WHATSAPP_ATTENDEE ? evt.attendees[0].timeZone : evt.organizer.timeZone;
  const locale = evt.organizer.language.locale;
  const timeFormat = evt.organizer.timeFormat;
  const contentSid = getContentSidForTemplate(template);
  const contentVariables = getContentVariablesForTemplate({
    name,
    attendeeName,
    eventName: evt.title,
    eventDate: dayjs(startTime).tz(timeZone).locale(locale).format("YYYY MMM D"),
    startTime: dayjs(startTime).tz(timeZone).locale(locale).format(timeFormat || "h:mma"),
    timeZone
  });
  let textMessage = message;
  switch (template) {
    case index$1.WorkflowTemplates.REMINDER:
      textMessage = whatsappReminderTemplate(
        false,
        locale,
        action,
        timeFormat,
        evt.startTime,
        evt.title,
        timeZone,
        attendeeName,
        name
      ) || message;
      break;
    case index$1.WorkflowTemplates.CANCELLED:
      textMessage = whatsappEventCancelledTemplate(
        false,
        locale,
        action,
        timeFormat,
        evt.startTime,
        evt.title,
        timeZone,
        attendeeName,
        name
      ) || message;
      break;
    case index$1.WorkflowTemplates.RESCHEDULED:
      textMessage = whatsappEventRescheduledTemplate(
        false,
        locale,
        action,
        timeFormat,
        evt.startTime,
        evt.title,
        timeZone,
        attendeeName,
        name
      ) || message;
      break;
    case index$1.WorkflowTemplates.COMPLETED:
      textMessage = whatsappEventCompletedTemplate(
        false,
        locale,
        action,
        timeFormat,
        evt.startTime,
        evt.title,
        timeZone,
        attendeeName,
        name
      ) || message;
      break;
    default:
      textMessage = whatsappReminderTemplate(
        false,
        locale,
        action,
        timeFormat,
        evt.startTime,
        evt.title,
        timeZone,
        attendeeName,
        name
      ) || message;
  }
  log$2.debug(`Sending Whatsapp for trigger ${triggerEvent}`, textMessage);
  if (textMessage.length > 0 && reminderPhone && isNumberVerified) {
    if (IMMEDIATE_WORKFLOW_TRIGGER_EVENTS.includes(triggerEvent)) {
      try {
        await index.sendSmsOrFallbackEmail({
          twilioData: {
            phoneNumber: reminderPhone,
            body: textMessage,
            sender: "",
            bookingUid: evt.uid,
            userId,
            teamId,
            isWhatsapp: true,
            contentSid,
            contentVariables
          },
          fallbackData: isAttendeeAction(action) ? {
            email: evt.attendees[0].email,
            t: await index$1.getTranslation(evt.attendees[0].language.locale ?? "en", "common"),
            replyTo: evt.organizer.email
          } : void 0
        });
      } catch (error) {
        console.log(`Error sending WHATSAPP with error ${error}`);
      }
    } else if ((triggerEvent === index$1.WorkflowTriggerEvents.BEFORE_EVENT || triggerEvent === index$1.WorkflowTriggerEvents.AFTER_EVENT) && scheduledDate) {
      if (currentDate.isBefore(scheduledDate.subtract(15, "minute")) && !scheduledDate.isAfter(currentDate.add(2, "hour"))) {
        try {
          const scheduledNotification = await index.scheduleSmsOrFallbackEmail({
            twilioData: {
              phoneNumber: reminderPhone,
              body: textMessage,
              scheduledDate: scheduledDate.toDate(),
              sender: "",
              bookingUid: evt.uid ?? "",
              userId,
              teamId,
              isWhatsapp: true,
              contentSid,
              contentVariables
            },
            fallbackData: isAttendeeAction(action) ? {
              email: evt.attendees[0].email,
              t: await index$1.getTranslation(evt.attendees[0].language.locale ?? "en", "common"),
              replyTo: evt.organizer.email,
              workflowStepId
            } : void 0
          });
          if (scheduledNotification?.sid) {
            await index$1.prisma.workflowReminder.create({
              data: {
                bookingUid: uid,
                workflowStepId,
                method: index$1.WorkflowMethods.WHATSAPP,
                scheduledDate: scheduledDate.toDate(),
                scheduled: true,
                referenceId: scheduledNotification.sid,
                seatReferenceId: seatReferenceUid
              }
            });
          }
        } catch (error) {
          console.log(`Error scheduling WHATSAPP with error ${error}`);
        }
      } else if (scheduledDate.isAfter(currentDate.add(2, "hour"))) {
        await index$1.prisma.workflowReminder.create({
          data: {
            bookingUid: uid,
            workflowStepId,
            method: index$1.WorkflowMethods.WHATSAPP,
            scheduledDate: scheduledDate.toDate(),
            scheduled: false,
            seatReferenceId: seatReferenceUid
          }
        });
      }
    }
  }
};
const stripe = globalThis.stripe || new Stripe(process.env.STRIPE_PRIVATE_KEY, {
  apiVersion: "2020-08-27"
});
if (process.env.NODE_ENV !== "production") {
  globalThis.stripe = stripe;
}
const stripe$1 = stripe;
function getRequestedSlugError(error, requestedSlug) {
  let message = `Unknown error`;
  let statusCode = 500;
  if (error instanceof index$1.client.Prisma.PrismaClientKnownRequestError && error.code === "P2002") {
    statusCode = 400;
    message = `It seems like the requestedSlug: '${requestedSlug}' is already taken. Please contact support at help@cal.com so we can resolve this issue.`;
  } else if (error instanceof Error) {
    message = error.message;
  }
  return { message, statusCode };
}
const log$1 = index$1.logger.getSubLogger({ prefix: ["teams/lib/payments"] });
z.z.object({
  // Redefine paymentId, subscriptionId and subscriptionItemId to ensure that they are present and nonNullable
  paymentId: z.z.string(),
  subscriptionId: z.z.string(),
  subscriptionItemId: z.z.string(),
  orgSeats: index$1.teamMetadataSchema.unwrap().shape.orgSeats
});
const checkIfTeamPaymentRequired = async ({ teamId = -1 }) => {
  const team = await index$1.prisma.team.findUniqueOrThrow({
    where: { id: teamId },
    select: { metadata: true }
  });
  const metadata = index$1.teamMetadataSchema.parse(team.metadata);
  if (!metadata?.paymentId)
    return { url: null };
  const checkoutSession = await stripe$1.checkout.sessions.retrieve(metadata.paymentId);
  if (checkoutSession.payment_status !== "paid")
    return { url: null };
  return { url: `${index$1.WEBAPP_URL}/api/teams/${teamId}/upgrade?session_id=${metadata.paymentId}` };
};
const purchaseTeamOrOrgSubscription = async (input) => {
  const {
    teamId,
    seatsToChargeFor,
    seatsUsed,
    userId,
    isOrg,
    pricePerSeat,
    billingPeriod = index$1.BillingPeriod.MONTHLY
  } = input;
  const { url } = await checkIfTeamPaymentRequired({ teamId });
  if (url)
    return { url };
  const seats = isOrg ? Math.max(seatsUsed, index$1.MINIMUM_NUMBER_OF_ORG_SEATS) : seatsUsed;
  const quantity = seatsToChargeFor ? seatsToChargeFor : seats;
  const customer$1 = await customer.getStripeCustomerIdFromUserId(userId);
  const fixedPrice = await getFixedPrice();
  let priceId;
  if (pricePerSeat) {
    if (isOrg && pricePerSeat === index$1.ORGANIZATION_SELF_SERVE_PRICE && seats === index$1.ORGANIZATION_SELF_SERVE_MIN_SEATS) {
      priceId = fixedPrice;
    } else {
      const customPriceObj = await getPriceObject(fixedPrice);
      priceId = await createPrice({
        isOrg: !!isOrg,
        teamId,
        pricePerSeat,
        billingPeriod,
        product: customPriceObj.product,
        // We don't expand the object from stripe so just use the product as ID
        currency: customPriceObj.currency
      });
    }
  } else {
    priceId = fixedPrice;
  }
  const session = await stripe$1.checkout.sessions.create({
    customer: customer$1,
    mode: "subscription",
    allow_promotion_codes: true,
    success_url: `${index$1.WEBAPP_URL}/api/teams/${teamId}/upgrade?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${index$1.WEBAPP_URL}/settings/my-account/profile`,
    line_items: [
      {
        price: priceId,
        quantity
      }
    ],
    customer_update: {
      address: "auto"
    },
    // Disabled when testing locally as usually developer doesn't setup Tax in Stripe Test mode
    automatic_tax: {
      enabled: index$1.IS_PRODUCTION
    },
    metadata: {
      teamId
    },
    subscription_data: {
      metadata: {
        teamId,
        dubCustomerId: userId
      }
    }
  });
  return { url: session.url };
  async function createPrice({
    isOrg: isOrg2,
    teamId: teamId2,
    pricePerSeat: pricePerSeat2,
    billingPeriod: billingPeriod2,
    product,
    currency
  }) {
    try {
      const pricePerSeatInCents = pricePerSeat2 * 100;
      const occurrence = billingPeriod2 === "MONTHLY" ? 1 : 12;
      const yearlyPrice = pricePerSeatInCents * occurrence;
      const customPriceObj = await stripe$1.prices.create({
        nickname: `Custom price for ${isOrg2 ? "Organization" : "Team"} ID: ${teamId2}`,
        unit_amount: yearlyPrice,
        // Stripe expects the amount in cents
        // Use the same currency as in the fixed price to avoid hardcoding it.
        currency,
        recurring: { interval: billingPeriod2 === "MONTHLY" ? "month" : "year" },
        // Define your subscription interval
        product: typeof product === "string" ? product : product.id,
        tax_behavior: "exclusive"
      });
      return customPriceObj.id;
    } catch (e) {
      log$1.error(
        `Error creating custom price for ${isOrg2 ? "Organization" : "Team"} ID: ${teamId2}`,
        index$1.safeStringify(e)
      );
      throw new Error("Error in creation of custom price");
    }
  }
  async function getFixedPrice() {
    const fixedPriceId = isOrg ? process.env.STRIPE_ORG_MONTHLY_PRICE_ID : process.env.STRIPE_TEAM_MONTHLY_PRICE_ID;
    if (!fixedPriceId) {
      throw new Error(
        "You need to have STRIPE_ORG_MONTHLY_PRICE_ID and STRIPE_TEAM_MONTHLY_PRICE_ID env variables set"
      );
    }
    log$1.debug("Getting price ID", index$1.safeStringify({ fixedPriceId, isOrg, teamId, pricePerSeat }));
    return fixedPriceId;
  }
};
async function getPriceObject(priceId) {
  const priceObj = await stripe$1.prices.retrieve(priceId);
  if (!priceObj)
    throw new Error(`No price found for ID ${priceId}`);
  return priceObj;
}
const objectKeys = (obj) => Object.keys(obj);
const objectKeys$1 = objectKeys;
function getMetadataHelpers(schema, rawMetadata) {
  const metadata = schema.parse(rawMetadata);
  return {
    metadata,
    get: (key) => metadata[key],
    /** This method prevents overwriting the metadata fields that you don't want to change. */
    mergeMetadata: (newMetadata) => {
      const newMetadataToReturn = { ...metadata, ...newMetadata };
      objectKeys$1(newMetadata).forEach((key) => {
        if (newMetadata[key] === void 0) {
          delete newMetadataToReturn[key];
        }
      });
      return newMetadataToReturn;
    }
  };
}
class Redirect {
  constructor(code, url) {
    this.code = code;
    this.url = url;
  }
}
class OrganizationOnboardingRepository {
  static async create(data) {
    index$1.logger.debug("Creating organization onboarding", index$1.safeStringify(data));
    return await index$1.prisma.organizationOnboarding.create({
      // HEKOP
      data: {
        billingPeriod: data.billingPeriod,
        pricePerSeat: data.pricePerSeat,
        seats: data.seats,
        orgOwnerEmail: data.orgOwnerEmail,
        name: data.name,
        slug: data.slug,
        logo: data.logo,
        bio: data.bio,
        stripeCustomerId: data.stripeCustomerId,
        stripeSubscriptionId: data.stripeSubscriptionId,
        invitedMembers: data.invitedMembers || [],
        teams: data.teams || [],
        createdById: data.createdById
      }
    });
  }
  static async findByStripeCustomerId(stripeCustomerId) {
    index$1.logger.debug(
      "Finding organization onboarding by stripe customer id",
      index$1.safeStringify({ stripeCustomerId })
    );
    return await index$1.prisma.organizationOnboarding.findUnique({
      where: {
        stripeCustomerId
      }
    });
  }
  static async findById(id) {
    index$1.logger.debug("Finding organization onboarding by id", index$1.safeStringify({ id }));
    return await index$1.prisma.organizationOnboarding.findUnique({
      where: {
        id
      }
    });
  }
  static async findByOrgOwnerEmail(email) {
    index$1.logger.debug("Finding organization onboarding by org owner email", index$1.safeStringify({ email }));
    return await index$1.prisma.organizationOnboarding.findUnique({
      where: {
        orgOwnerEmail: email
      }
    });
  }
  // TODO: This method should be moved to OrganizationOnboardingService
  static async markAsComplete(id) {
    index$1.logger.debug("Marking organization onboarding as complete", { id });
    return await index$1.prisma.organizationOnboarding.update({
      where: {
        id
      },
      data: {
        error: null,
        isComplete: true
      }
    });
  }
  static async update(id, data) {
    index$1.logger.debug("Updating organization onboarding", index$1.safeStringify({ id, data }));
    const { organizationId, createdById: _, ...rest } = data;
    return await index$1.prisma.organizationOnboarding.update({
      where: {
        id
      },
      data: {
        ...rest,
        ...organizationId ? { organization: { connect: { id: organizationId } } } : {},
        updatedAt: /* @__PURE__ */ new Date()
      }
    });
  }
  static async findByOrganizationId(organizationId) {
    index$1.logger.debug("Finding organization onboarding by organization id", index$1.safeStringify({ organizationId }));
    return await index$1.prisma.organizationOnboarding.findUnique({
      where: {
        organizationId
      }
    });
  }
  static async findAllBySlug(slug) {
    index$1.logger.debug("Finding all organization onboardings by slug", index$1.safeStringify({ slug }));
    return await index$1.prisma.organizationOnboarding.findMany({
      where: {
        slug
      }
    });
  }
  static async delete(id) {
    index$1.logger.debug("Deleting organization onboarding", { id });
    return await index$1.prisma.organizationOnboarding.delete({
      where: {
        id
      }
    });
  }
}
class StripeBillingService {
  constructor() {
    this.stripe = new Stripe(process.env.STRIPE_PRIVATE_KEY, {
      apiVersion: "2020-08-27"
    });
  }
  async createCustomer(args) {
    const { email, metadata } = args;
    const customer2 = await this.stripe.customers.create({
      email,
      metadata: {
        email,
        ...metadata
      }
    });
    return { stripeCustomerId: customer2.id };
  }
  async createPaymentIntent(args) {
    const { customerId, amount, metadata } = args;
    const paymentIntent = await this.stripe.paymentIntents.create({
      customer: customerId,
      amount,
      currency: "usd",
      automatic_payment_methods: {
        enabled: true
      },
      metadata
    });
    return {
      id: paymentIntent.id,
      client_secret: paymentIntent.client_secret
    };
  }
  async createOneTimeCheckout(args) {
    const { priceId, quantity, successUrl, cancelUrl, metadata } = args;
    const session = await this.stripe.checkout.sessions.create({
      line_items: [{ price: priceId, quantity }],
      mode: "payment",
      success_url: successUrl,
      cancel_url: cancelUrl,
      metadata,
      invoice_creation: {
        enabled: true
      }
    });
    return {
      checkoutUrl: session.url,
      sessionId: session.id
    };
  }
  async createSubscriptionCheckout(args) {
    const {
      customerId,
      successUrl,
      cancelUrl,
      priceId,
      quantity,
      metadata,
      mode = "subscription",
      allowPromotionCodes = true,
      customerUpdate,
      automaticTax,
      discounts,
      subscriptionData
    } = args;
    const session = await this.stripe.checkout.sessions.create({
      customer: customerId,
      success_url: successUrl,
      cancel_url: cancelUrl,
      mode,
      metadata,
      line_items: [
        {
          price: priceId,
          quantity
        }
      ],
      allow_promotion_codes: allowPromotionCodes,
      customer_update: customerUpdate,
      automatic_tax: automaticTax,
      discounts,
      subscription_data: subscriptionData
    });
    return {
      checkoutUrl: session.url,
      sessionId: session.id
    };
  }
  async createPrice(args) {
    const { amount, currency, interval, productId, nickname, metadata } = args;
    const price = await this.stripe.prices.create({
      nickname,
      unit_amount: amount,
      currency,
      recurring: {
        interval
      },
      product: productId,
      metadata
    });
    return {
      priceId: price.id
    };
  }
  async handleSubscriptionCreation(subscriptionId) {
    throw new Error("Method not implemented.");
  }
  async handleSubscriptionCancel(subscriptionId) {
    await this.stripe.subscriptions.cancel(subscriptionId);
  }
  async handleSubscriptionUpdate(args) {
    const { subscriptionId, subscriptionItemId, membershipCount } = args;
    const subscription = await this.stripe.subscriptions.retrieve(subscriptionId);
    const subscriptionQuantity = subscription.items.data.find(
      (sub) => sub.id === subscriptionItemId
    )?.quantity;
    if (!subscriptionQuantity)
      throw new Error("Subscription not found");
    await this.stripe.subscriptions.update(subscriptionId, {
      items: [{ quantity: membershipCount, id: subscriptionItemId }]
    });
  }
  async handleEndTrial(subscriptionId) {
    const subscription = await this.stripe.subscriptions.retrieve(subscriptionId);
    if (subscription.status !== "trialing") {
      return;
    }
    await this.stripe.subscriptions.update(subscriptionId, {
      trial_end: "now"
    });
  }
  async checkoutSessionIsPaid(paymentId) {
    const checkoutSession = await this.stripe.checkout.sessions.retrieve(paymentId);
    return checkoutSession.payment_status === "paid";
  }
  async getSubscriptionStatus(subscriptionId) {
    const subscription = await this.stripe.subscriptions.retrieve(subscriptionId);
    if (!subscription || !subscription.status)
      return null;
    return subscription.status;
  }
  async getCheckoutSession(checkoutSessionId) {
    const checkoutSession = await this.stripe.checkout.sessions.retrieve(checkoutSessionId);
    return checkoutSession;
  }
  async getCustomer(customerId) {
    const customer2 = await this.stripe.customers.retrieve(customerId);
    return customer2;
  }
  async getSubscriptions(customerId) {
    const subscriptions = await this.stripe.subscriptions.list({ customer: customerId });
    return subscriptions.data;
  }
  async updateCustomer(args) {
    const { customerId, email, userId } = args;
    const metadata = {};
    if (email)
      metadata.email = email;
    if (userId)
      metadata.userId = userId;
    await this.stripe.customers.update(customerId, { metadata });
  }
  async getPrice(priceId) {
    const price = await this.stripe.prices.retrieve(priceId);
    return price;
  }
}
class BillingFactory {
  constructor() {
    return new StripeBillingService();
  }
}
const globalForBilling = global;
const billing = globalForBilling.billingService || new BillingFactory();
if (process.env.NODE_ENV !== "production") {
  globalForBilling.billingService = billing;
}
const TeamBillingPublishResponseStatus = {
  REQUIRES_PAYMENT: "REQUIRES_PAYMENT",
  REQUIRES_UPGRADE: "REQUIRES_UPGRADE",
  SUCCESS: "SUCCESS"
};
const log = index$1.logger.getSubLogger({ prefix: ["TeamBilling"] });
const teamPaymentMetadataSchema = index$1.teamMetadataStrictSchema.unwrap();
class InternalTeamBilling {
  constructor(team) {
    this.team = team;
  }
  set team(team) {
    const metadata = teamPaymentMetadataSchema.parse(team.metadata || {});
    this._team = { ...team, metadata };
  }
  get team() {
    return this._team;
  }
  async getOrgIfNeeded() {
    if (!this.team.parentId)
      return;
    const parentTeam = await index$1.prisma.team.findUniqueOrThrow({
      where: { id: this.team.parentId },
      select: { metadata: true, id: true, parentId: true, isOrganization: true }
    });
    this.team = parentTeam;
  }
  logErrorFromUnknown(error) {
    let message = "Unknown error on InternalTeamBilling.";
    if (error instanceof Error)
      message = error.message;
    log.error(message);
  }
  async cancel() {
    try {
      const { subscriptionId } = this.team.metadata;
      log.info(`Cancelling subscription ${subscriptionId} for team ${this.team.id}`);
      if (!subscriptionId)
        throw Error("missing subscriptionId");
      await billing.handleSubscriptionCancel(subscriptionId);
      await this.downgrade();
      log.info(`Cancelled subscription ${subscriptionId} for team ${this.team.id}`);
    } catch (error) {
      this.logErrorFromUnknown(error);
    }
  }
  // New teams are published on creation, this is for backwards compatibility
  async publish() {
    const { url } = await this.checkIfTeamPaymentRequired();
    const teamId = this.team.id;
    if (url) {
      return { redirectUrl: url, status: TeamBillingPublishResponseStatus.REQUIRES_UPGRADE };
    }
    const requestedSlug = this.team.metadata?.requestedSlug || "";
    const membershipCount = await index$1.prisma.membership.count({ where: { teamId } });
    const owner = await index$1.prisma.membership.findFirstOrThrow({
      where: { teamId, role: "OWNER" },
      select: {
        userId: true
      }
    });
    try {
      const checkoutSession = await purchaseTeamOrOrgSubscription({
        teamId,
        seatsUsed: membershipCount,
        userId: owner.userId,
        pricePerSeat: null
      });
      if (checkoutSession.url) {
        return {
          redirectUrl: checkoutSession.url,
          status: TeamBillingPublishResponseStatus.REQUIRES_PAYMENT
        };
      }
      const { mergeMetadata } = getMetadataHelpers(teamPaymentMetadataSchema, this.team.metadata);
      const data = {
        metadata: mergeMetadata({ requestedSlug: void 0 })
      };
      if (requestedSlug)
        data.slug = requestedSlug;
      await index$1.prisma.team.update({ where: { id: teamId }, data });
      return { status: TeamBillingPublishResponseStatus.SUCCESS, redirectUrl: null };
    } catch (error) {
      if (error instanceof Redirect)
        throw error;
      const { message } = getRequestedSlugError(error, requestedSlug);
      throw Error(message);
    }
  }
  async downgrade() {
    try {
      const { mergeMetadata } = getMetadataHelpers(teamPaymentMetadataSchema, this.team.metadata);
      const metadata = mergeMetadata({
        paymentId: void 0,
        subscriptionId: void 0,
        subscriptionItemId: void 0
      });
      await index$1.prisma.team.update({ where: { id: this.team.id }, data: { metadata } });
      log.info(`Downgraded team ${this.team.id}`);
    } catch (error) {
      this.logErrorFromUnknown(error);
    }
  }
  async updateQuantity() {
    try {
      await this.getOrgIfNeeded();
      const { id: teamId, metadata, isOrganization } = this.team;
      const { url } = await this.checkIfTeamPaymentRequired();
      const organizationOnboarding = await OrganizationOnboardingRepository.findByOrganizationId(
        this.team.id
      );
      log.debug("updateQuantity", index$1.safeStringify({ url, team: this.team }));
      if (!url && !isOrganization)
        return;
      const { subscriptionId, subscriptionItemId, orgSeats } = metadata;
      const orgMinimumSubscriptionQuantity = orgSeats || index$1.MINIMUM_NUMBER_OF_ORG_SEATS;
      const membershipCount = await index$1.prisma.membership.count({ where: { teamId } });
      if (isOrganization && membershipCount < orgMinimumSubscriptionQuantity) {
        log.info(
          `Org ${teamId} has less members than the min required ${orgMinimumSubscriptionQuantity}, skipping updating subscription.`
        );
        return;
      }
      if (!subscriptionId)
        throw Error("missing subscriptionId");
      if (!subscriptionItemId)
        throw Error("missing subscriptionItemId");
      await billing.handleSubscriptionUpdate({ subscriptionId, subscriptionItemId, membershipCount });
      log.info(`Updated subscription ${subscriptionId} for team ${teamId} to ${membershipCount} seats.`);
    } catch (error) {
      this.logErrorFromUnknown(error);
    }
  }
  /** Used to prevent double charges for the same team */
  async checkIfTeamPaymentRequired() {
    const { paymentId } = this.team.metadata || {};
    if (!paymentId)
      return { url: null, paymentId: null, paymentRequired: true };
    const checkoutSessionIsPaid = await billing.checkoutSessionIsPaid(paymentId);
    if (!checkoutSessionIsPaid)
      return { url: null, paymentId, paymentRequired: true };
    return {
      url: `${index$1.WEBAPP_URL}/api/teams/${this.team.id}/upgrade?session_id=${paymentId}`,
      paymentId,
      paymentRequired: false
    };
  }
  /** Returns the subscription status (active, past_due, trialing, ...) */
  async getSubscriptionStatus() {
    const { subscriptionId } = this.team.metadata;
    if (!subscriptionId)
      return null;
    return await billing.getSubscriptionStatus(subscriptionId);
  }
  /**
   * Ends the trial period for a team subscription by converting it to a regular subscription
   * @returns {Promise<boolean>} True if successful, false otherwise
   */
  async endTrial() {
    try {
      const { subscriptionId } = this.team.metadata;
      log.info(`Ending trial for subscription ${subscriptionId} of team ${this.team.id}`);
      if (!subscriptionId) {
        log.warn(`No subscription ID found for team ${this.team.id}`);
        return false;
      }
      await billing.handleEndTrial(subscriptionId);
      log.info(`Successfully ended trial for team ${this.team.id}`);
      return true;
    } catch (error) {
      this.logErrorFromUnknown(error);
      return false;
    }
  }
}
exports.InternalTeamBilling = InternalTeamBilling;
exports.StripeBillingService = StripeBillingService;
exports.TeamBillingPublishResponseStatus = TeamBillingPublishResponseStatus;
exports.deleteScheduledAIPhoneCall = deleteScheduledAIPhoneCall;
exports.deleteScheduledEmailReminder = deleteScheduledEmailReminder;
exports.deleteScheduledSMSReminder = deleteScheduledSMSReminder;
exports.emailRatingTemplate = emailRatingTemplate;
exports.emailReminderTemplate = emailReminderTemplate;
exports.isAttendeeAction = isAttendeeAction;
exports.isCalAIAction = isCalAIAction;
exports.isEmailAction = isEmailAction;
exports.isSMSAction = isSMSAction;
exports.isSMSOrWhatsappAction = isSMSOrWhatsappAction;
exports.isWhatsappAction = isWhatsappAction;
exports.scheduleAIPhoneCall = scheduleAIPhoneCall;
exports.scheduleEmailReminder = scheduleEmailReminder;
exports.scheduleSMSReminder = scheduleSMSReminder;
exports.scheduleWhatsappReminder = scheduleWhatsappReminder;
exports.stripe = stripe$1;
