import { S as SystemField } from "./CalEventParser-yX6oM9Dn.js";
import { isValidPhoneNumber } from "libphonenumber-js";
import z__default from "zod";
import { a1 as bookingResponses, a2 as dbReadResponseSchema, a3 as fieldTypesSchemaMap, a4 as emailSchemaRefinement, s as slugify, l as logger, a5 as eventTypeBookingFields, a6 as WebhookTriggerEvents, w as withReporting, p as prisma, B as BookingStatus } from "./i18n-VAwx2uzJ.js";
import { H as HttpError } from "./http-error-RJXCPZV2.js";
import "uuid";
import "./index-rbwdGzy0.js";
import { u as tasker } from "./email-manager-em79awUz.js";
const bookingResponsesDbSchema = z__default.record(dbReadResponseSchema);
const ensureValidPhoneNumber = (value) => {
  if (!value)
    return "";
  return value.replace(/^ +/, "+");
};
function getBookingResponsesSchema({ bookingFields, view }) {
  const schema = bookingResponses.and(z__default.record(z__default.any()));
  return preprocess({ schema, bookingFields, isPartialSchema: false, view });
}
function preprocess({
  schema,
  bookingFields,
  isPartialSchema,
  view: currentView,
  checkOptional = false
}) {
  const preprocessed = z__default.preprocess(
    (responses) => {
      const parsedResponses = z__default.record(z__default.any()).nullable().parse(responses) || {};
      const newResponses = {};
      if (!bookingFields)
        return parsedResponses;
      bookingFields.forEach((field) => {
        const value = parsedResponses[field.name];
        if (value === void 0) {
          return;
        }
        const views = field.views;
        const isFieldApplicableToCurrentView = currentView === "ALL_VIEWS" ? true : views ? views.find((view) => view.id === currentView) : true;
        if (!isFieldApplicableToCurrentView) {
          return;
        }
        const fieldTypeSchema = fieldTypesSchemaMap[field.type];
        if (fieldTypeSchema) {
          newResponses[field.name] = fieldTypeSchema.preprocess({
            response: value,
            isPartialSchema,
            field
          });
          return;
        }
        if (field.type === "boolean") {
          newResponses[field.name] = value === "true" || value === true;
        } else if (field.type === "multiemail" || field.type === "checkbox" || field.type === "multiselect") {
          newResponses[field.name] = value instanceof Array ? value : [value];
        } else if (field.type === "radioInput" && typeof value === "string") {
          let parsedValue = {
            optionValue: "",
            value: ""
          };
          try {
            parsedValue = JSON.parse(value);
          } catch (e) {
          }
          const optionsInputs = field.optionsInputs;
          const optionInputField = optionsInputs?.[parsedValue.value];
          if (optionInputField && optionInputField.type === "phone") {
            parsedValue.optionValue = ensureValidPhoneNumber(parsedValue.optionValue);
          }
          newResponses[field.name] = parsedValue;
        } else if (field.type === "phone") {
          newResponses[field.name] = ensureValidPhoneNumber(value);
        } else {
          newResponses[field.name] = value;
        }
      });
      return {
        ...parsedResponses,
        ...newResponses
      };
    },
    schema.superRefine(async (responses, ctx) => {
      if (!bookingFields) {
        return;
      }
      const attendeePhoneNumberField = bookingFields.find((field) => field.name === "attendeePhoneNumber");
      const isAttendeePhoneNumberFieldHidden = attendeePhoneNumberField?.hidden;
      const emailField = bookingFields.find((field) => field.name === "email");
      const isEmailFieldHidden = !!emailField?.hidden;
      if (isEmailFieldHidden && !isAttendeePhoneNumberFieldHidden) {
        responses["email"] = "";
      }
      for (const bookingField of bookingFields) {
        const value = responses[bookingField.name];
        const stringSchema = z__default.string();
        const emailSchema = isPartialSchema ? z__default.string() : z__default.string().refine(emailSchemaRefinement);
        const phoneSchema = isPartialSchema ? z__default.string() : z__default.string().refine(async (val) => {
          return isValidPhoneNumber(val);
        });
        const m = (message) => `{${bookingField.name}}${message}`;
        const views = bookingField.views;
        const isFieldApplicableToCurrentView = currentView === "ALL_VIEWS" ? true : views ? views.find((view) => view.id === currentView) : true;
        let hidden = bookingField.hidden;
        const numOptions = bookingField.options?.length ?? 0;
        if (bookingField.hideWhenJustOneOption) {
          hidden = hidden || numOptions <= 1;
        }
        let isRequired = false;
        if (!hidden && isFieldApplicableToCurrentView) {
          isRequired = checkOptional || !!bookingField.required;
        }
        if ((isPartialSchema || !isRequired) && value === void 0) {
          continue;
        }
        if (isRequired && !isPartialSchema && !value) {
          ctx.addIssue({ code: z__default.ZodIssueCode.custom, message: m(`error_required_field`) });
          return;
        }
        if (bookingField.type === "email") {
          if (!bookingField.hidden && checkOptional ? true : bookingField.required) {
            if (!emailSchema.safeParse(value).success) {
              ctx.addIssue({
                code: z__default.ZodIssueCode.custom,
                message: m("email_validation_error")
              });
            }
            const bookerEmail = value;
            const excludedEmails = bookingField.excludeEmails?.split(",").map((domain) => domain.trim()) || [];
            const match = excludedEmails.find((email) => bookerEmail.includes(email));
            if (match) {
              ctx.addIssue({
                code: z__default.ZodIssueCode.custom,
                message: m("exclude_emails_match_found_error_message")
              });
            }
            const requiredEmails = bookingField.requireEmails?.split(",").map((domain) => domain.trim()).filter(Boolean) || [];
            const requiredEmailsMatch = requiredEmails.find((email) => bookerEmail.includes(email));
            if (requiredEmails.length > 0 && !requiredEmailsMatch) {
              ctx.addIssue({
                code: z__default.ZodIssueCode.custom,
                message: m("require_emails_no_match_found_error_message")
              });
            }
          }
          continue;
        }
        const fieldTypeSchema = fieldTypesSchemaMap[bookingField.type];
        if (fieldTypeSchema) {
          fieldTypeSchema.superRefine({
            response: value,
            ctx,
            m,
            field: bookingField,
            isPartialSchema
          });
          continue;
        }
        if (bookingField.type === "multiemail") {
          const emailsParsed = emailSchema.array().safeParse(value);
          if (isRequired && (!value || value.length === 0)) {
            ctx.addIssue({ code: z__default.ZodIssueCode.custom, message: m(`error_required_field`) });
            continue;
          }
          if (!emailsParsed.success) {
            if (bookingField.name === "guests" && value.every((email) => email === "")) {
              responses[bookingField.name] = [];
              continue;
            }
            ctx.addIssue({
              code: z__default.ZodIssueCode.custom,
              message: m("email_validation_error")
            });
            continue;
          }
          const emails = emailsParsed.data;
          emails.sort().some((item, i) => {
            if (item === emails[i + 1]) {
              ctx.addIssue({ code: z__default.ZodIssueCode.custom, message: m("duplicate_email") });
              return true;
            }
          });
          continue;
        }
        if (bookingField.type === "multiselect") {
          if (isRequired && (!value || value.length === 0)) {
            ctx.addIssue({ code: z__default.ZodIssueCode.custom, message: m(`error_required_field`) });
            continue;
          }
          if (!stringSchema.array().safeParse(value).success) {
            ctx.addIssue({ code: z__default.ZodIssueCode.custom, message: m("Invalid array of strings") });
          }
          continue;
        }
        if (bookingField.type === "checkbox") {
          if (!stringSchema.array().safeParse(value).success) {
            ctx.addIssue({ code: z__default.ZodIssueCode.custom, message: m("Invalid array of strings") });
          }
          continue;
        }
        if (bookingField.type === "phone") {
          const needsValidation = isRequired || value && value.trim() !== "";
          if (!bookingField.hidden && needsValidation) {
            if (!(await phoneSchema.safeParseAsync(value)).success) {
              ctx.addIssue({ code: z__default.ZodIssueCode.custom, message: m("invalid_number") });
            }
          }
          continue;
        }
        if (bookingField.type === "boolean") {
          const schema2 = z__default.boolean();
          if (!schema2.safeParse(value).success) {
            ctx.addIssue({ code: z__default.ZodIssueCode.custom, message: m("Invalid Boolean") });
          }
          continue;
        }
        if (bookingField.type === "radioInput") {
          if (bookingField.optionsInputs) {
            const optionValue = value?.optionValue;
            const optionField = bookingField.optionsInputs[value?.value];
            const typeOfOptionInput = optionField?.type;
            if (
              // Either the field is required or there is a radio selected, we need to check if the optionInput is required or not.
              (isRequired || value?.value) && checkOptional ? true : optionField?.required && !optionValue
            ) {
              ctx.addIssue({ code: z__default.ZodIssueCode.custom, message: m("error_required_field") });
              return;
            }
            if (optionValue) {
              if (typeOfOptionInput === "phone") {
                if (!(await phoneSchema.safeParseAsync(optionValue)).success) {
                  ctx.addIssue({ code: z__default.ZodIssueCode.custom, message: m("invalid_number") });
                }
              }
            }
          }
          continue;
        }
        if (["address", "text", "select", "number", "radio", "textarea"].includes(bookingField.type)) {
          const schema2 = stringSchema;
          if (!schema2.safeParse(value).success) {
            ctx.addIssue({ code: z__default.ZodIssueCode.custom, message: m("Invalid string") });
          }
          continue;
        }
        ctx.addIssue({
          code: z__default.ZodIssueCode.custom,
          message: `Can't parse unknown booking field type: ${bookingField.type}`
        });
      }
    })
  );
  if (isPartialSchema) {
    return preprocessed.catch(function(res) {
      console.error("Failed to preprocess query params, prefilling will be skipped", res?.error);
      return {};
    });
  }
  return preprocessed;
}
const contructEmailFromPhoneNumber = (phoneNumber) => {
  const cleanedPhoneNumber = phoneNumber.replace(/\+/g, "");
  return `${cleanedPhoneNumber}@sms.cal.com`;
};
function getResponsesFromOldBooking(rawBooking) {
  const customInputs = rawBooking.customInputs || {};
  const responses = Object.keys(customInputs).reduce((acc, label) => {
    acc[slugify(label)] = customInputs[label];
    return acc;
  }, {});
  return {
    // It is possible to have no attendees in a booking when the booking is cancelled.
    name: rawBooking.attendees[0]?.name || "Nameless",
    email: rawBooking.attendees[0]?.email || "",
    guests: rawBooking.attendees.slice(1).map((attendee) => {
      return attendee.email;
    }),
    notes: rawBooking.description || "",
    location: {
      value: rawBooking.location || "",
      optionValue: rawBooking.location || ""
    },
    ...responses
  };
}
const getBookingWithResponses = (booking) => {
  return {
    ...booking,
    responses: bookingResponsesDbSchema.parse(booking.responses || getResponsesFromOldBooking(booking))
  };
};
const log = logger.getSubLogger({ prefix: ["[getCalEventResponses]"] });
const getCalEventResponses = ({
  bookingFields,
  booking,
  responses,
  seatsEnabled
}) => {
  const calEventUserFieldsResponses = {};
  const calEventResponses = {};
  const parsedBookingFields = bookingFields ? eventTypeBookingFields.parse(bookingFields) : null;
  const backwardCompatibleResponses = responses ?? (booking ? getBookingWithResponses(booking).responses : null);
  if (!backwardCompatibleResponses)
    throw new Error("Couldn't get responses");
  if (!!!backwardCompatibleResponses.email) {
    if (typeof backwardCompatibleResponses["attendeePhoneNumber"] !== "string") {
      log.error(`backwardCompatibleResponses: ${JSON.stringify(backwardCompatibleResponses)}`, {
        responses,
        bookingResponses: booking?.responses
      });
      throw new HttpError({
        statusCode: 400,
        message: "Both Phone and Email are missing"
      });
    }
    backwardCompatibleResponses.email = contructEmailFromPhoneNumber(
      backwardCompatibleResponses["attendeePhoneNumber"]
    );
  }
  if (parsedBookingFields) {
    parsedBookingFields.forEach((field) => {
      const label = field.label || field.defaultLabel;
      if (!label) {
        throw new Error(`Missing label for booking field "${field.name}"`);
      }
      if (field.name == "guests" && !!seatsEnabled) {
        backwardCompatibleResponses[field.name] = [];
      }
      if (field.editable === "user" || field.editable === "user-readonly") {
        calEventUserFieldsResponses[field.name] = {
          label,
          value: backwardCompatibleResponses[field.name],
          isHidden: !!field.hidden
        };
      }
      calEventResponses[field.name] = {
        label,
        value: backwardCompatibleResponses[field.name],
        isHidden: !!field.hidden
      };
    });
  } else {
    for (const [name, value] of Object.entries(backwardCompatibleResponses)) {
      const isSystemField = SystemField.safeParse(name);
      const label = name;
      if (!isSystemField.success) {
        calEventUserFieldsResponses[name] = {
          label,
          value
        };
      }
      calEventResponses[name] = {
        label,
        value
      };
    }
  }
  return { userFieldsResponses: calEventUserFieldsResponses, responses: calEventResponses };
};
[
  WebhookTriggerEvents.MEETING_ENDED,
  WebhookTriggerEvents.MEETING_STARTED
];
const NO_SHOW_TRIGGERS = [
  WebhookTriggerEvents.AFTER_HOSTS_CAL_VIDEO_NO_SHOW,
  WebhookTriggerEvents.AFTER_GUESTS_CAL_VIDEO_NO_SHOW
];
logger.getSubLogger({ prefix: ["[node-scheduler]"] });
async function scheduleTrigger({
  booking,
  subscriberUrl,
  subscriber,
  triggerEvent,
  isDryRun = false
}) {
  if (isDryRun)
    return;
  try {
    const payload = JSON.stringify({ triggerEvent, ...booking });
    await prisma.webhookScheduledTriggers.create({
      data: {
        payload,
        appId: subscriber.appId,
        startAfter: triggerEvent === WebhookTriggerEvents.MEETING_ENDED ? booking.endTime : booking.startTime,
        subscriberUrl,
        webhook: {
          connect: {
            id: subscriber.id
          }
        },
        booking: {
          connect: {
            id: booking.id
          }
        }
      }
    });
  } catch (error) {
    console.error("Error cancelling scheduled jobs", error);
  }
}
async function _deleteWebhookScheduledTriggers({
  booking,
  appId,
  triggerEvent,
  webhookId,
  userId,
  teamId,
  isDryRun = false
}) {
  if (isDryRun)
    return;
  try {
    if (appId && (userId || teamId)) {
      const where = {};
      if (userId) {
        where.eventType = { userId };
      } else {
        where.eventType = { teamId };
      }
      await prisma.webhookScheduledTriggers.deleteMany({
        where: {
          appId,
          booking: where
        }
      });
    } else {
      if (booking) {
        await prisma.webhookScheduledTriggers.deleteMany({
          where: {
            bookingId: booking.id
          }
        });
      } else if (webhookId) {
        const where = { webhookId };
        if (triggerEvent) {
          const shouldContain = `"triggerEvent":"${triggerEvent}"`;
          where.payload = { contains: shouldContain };
        }
        await prisma.webhookScheduledTriggers.deleteMany({
          where
        });
      }
    }
  } catch (error) {
    console.error("Error deleting webhookScheduledTriggers ", error);
  }
}
const deleteWebhookScheduledTriggers = withReporting(
  _deleteWebhookScheduledTriggers,
  "deleteWebhookScheduledTriggers"
);
async function fetchBookingsFromWebhook(webhook) {
  const currentTime = /* @__PURE__ */ new Date();
  const where = {
    AND: [{ status: BookingStatus.ACCEPTED }],
    OR: [{ startTime: { gt: currentTime }, endTime: { gt: currentTime } }]
  };
  let bookings = [];
  if (Array.isArray(where.AND)) {
    if (webhook.teamId) {
      const org = await prisma.team.findFirst({
        where: {
          id: webhook.teamId,
          isOrganization: true
        },
        select: {
          id: true,
          children: {
            select: {
              id: true
            }
          },
          members: {
            select: {
              userId: true
            }
          }
        }
      });
      if (org) {
        const teamEvents = await prisma.eventType.findMany({
          where: {
            teamId: {
              in: org.children.map((team) => team.id)
            }
          },
          select: {
            bookings: {
              where
            }
          }
        });
        const teamEventBookings = teamEvents.flatMap((event) => event.bookings);
        const teamBookingsId = teamEventBookings.map((booking) => booking.id);
        const orgMemberIds = org.members.map((member) => member.userId);
        where.AND.push({
          userId: {
            in: orgMemberIds
          }
        });
        where.AND.push({
          id: {
            notIn: teamBookingsId
          }
        });
        const userBookings = await prisma.booking.findMany({
          where
        });
        bookings = teamEventBookings.concat(userBookings);
      } else {
        const teamEvents = await prisma.eventType.findMany({
          where: {
            teamId: webhook.teamId
          },
          select: {
            bookings: {
              where
            }
          }
        });
        bookings = teamEvents.flatMap((event) => event.bookings);
      }
    } else {
      if (webhook.eventTypeId) {
        where.AND.push({ eventTypeId: webhook.eventTypeId });
      } else if (webhook.userId) {
        where.AND.push({ userId: webhook.userId });
      }
      bookings = await prisma.booking.findMany({
        where
      });
    }
  }
  return bookings;
}
async function cancelNoShowTasksForBooking({
  bookingUid,
  triggerEvent,
  webhook
}) {
  if (bookingUid) {
    if (triggerEvent && !NO_SHOW_TRIGGERS.includes(triggerEvent))
      return;
    if (triggerEvent === WebhookTriggerEvents.AFTER_HOSTS_CAL_VIDEO_NO_SHOW) {
      await tasker.cancelWithReference(bookingUid, "triggerHostNoShowWebhook");
    } else if (triggerEvent === WebhookTriggerEvents.AFTER_GUESTS_CAL_VIDEO_NO_SHOW) {
      await tasker.cancelWithReference(bookingUid, "triggerGuestNoShowWebhook");
    } else {
      await prisma.task.deleteMany({
        where: {
          referenceUid: bookingUid
        }
      });
    }
  } else if (webhook) {
    const bookings = await fetchBookingsFromWebhook(webhook);
    if (bookings.length === 0)
      return;
    const promises = bookings.map(async (booking) => {
      return await prisma.task.deleteMany({
        where: {
          referenceUid: booking.uid
        }
      });
    });
    await Promise.all(promises);
  }
}
const PaymentServiceMap = {
  alby: import("./PaymentService-1xOpFM3j.js"),
  btcpayserver: import("./PaymentService-AOYUUaz0.js"),
  hitpay: import("./PaymentService-B4_lrgqM.js"),
  "mock-payment-app": import("./PaymentService-HHNjUeaI.js"),
  paypal: import("./PaymentService-drvNkR3i.js"),
  stripepayment: import("./PaymentService-XZARPNZx.js")
};
export {
  PaymentServiceMap as P,
  getBookingResponsesSchema as a,
  bookingResponsesDbSchema as b,
  cancelNoShowTasksForBooking as c,
  deleteWebhookScheduledTriggers as d,
  getCalEventResponses as g,
  scheduleTrigger as s
};
