"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const querystring = require("querystring");
const index$2 = require("./user-v6mfWQ2d.cjs");
const defaultHandler = require("./defaultHandler-MSjhitc-.cjs");
const defaultResponder = require("./defaultResponder-QWPOXt61.cjs");
const encodeOAuthState = require("./encodeOAuthState-VBfelnxQ.cjs");
const z = require("zod");
const getAppKeysFromSlug = require("./getAppKeysFromSlug-W8EkKxxV.cjs");
require("./index-Br7AzmuA.cjs");
const piiFreeData = require("./piiFreeData-FAapPrV2.cjs");
const markTokenAsExpired = require("./markTokenAsExpired-yzGdqjc-.cjs");
const CalEventParser = require("./CalEventParser-60bHdlQO.cjs");
const dayjs = require("dayjs");
const getSafeRedirectUrl = require("./getSafeRedirectUrl-ErDpjTNV.cjs");
const getInstalledAppPath = require("./getInstalledAppPath-3c7mqtrk.cjs");
const createOAuthAppCredential = require("./createOAuthAppCredential-ccvXpWYY.cjs");
const decodeOAuthState = require("./decodeOAuthState--7fljpCf.cjs");
const zoomAppKeysSchema = z.z.object({
  client_id: z.z.string(),
  client_secret: z.z.string()
});
const getZoomAppKeys = async () => {
  const appKeys = await getAppKeysFromSlug.getAppKeysFromSlug("zoom");
  return zoomAppKeysSchema.parse(appKeys);
};
const log = index$2.logger.getSubLogger({ prefix: ["app-store/zoomvideo/lib/VideoApiAdapter"] });
const zoomEventResultSchema = z.z.object({
  id: z.z.number(),
  join_url: z.z.string(),
  password: z.z.string().optional().default("")
});
const zoomMeetingsSchema = z.z.object({
  next_page_token: z.z.string(),
  page_count: z.z.number(),
  page_number: z.z.number(),
  page_size: z.z.number(),
  total_records: z.z.number(),
  meetings: z.z.array(
    z.z.object({
      agenda: z.z.string(),
      created_at: z.z.string(),
      duration: z.z.number(),
      host_id: z.z.string(),
      id: z.z.number(),
      join_url: z.z.string(),
      pmi: z.z.string(),
      start_time: z.z.string(),
      timezone: z.z.string(),
      topic: z.z.string(),
      type: z.z.number(),
      uuid: z.z.string()
    })
  )
});
const zoomUserSettingsSchema = z.z.object({
  recording: z.z.object({
    auto_recording: z.z.string().nullish()
  }).nullish(),
  schedule_meeting: z.z.object({
    default_password_for_scheduled_meetings: z.z.string().nullish()
  }).nullish(),
  in_meeting: z.z.object({
    waiting_room: z.z.boolean()
  }).nullish(),
  meeting_security: z.z.object({
    waiting_room_settings: z.z.object({
      participants_to_place_in_waiting_room: z.z.number().optional(),
      users_who_can_admit_participants_from_waiting_room: z.z.number().optional(),
      whitelisted_domains_for_waiting_room: z.z.string().optional()
    }).optional()
  }).nullish()
});
const settingsApiFilterResp = "default_password_for_scheduled_meetings,auto_recording,waiting_room,waiting_room_settings";
const ZoomVideoApiAdapter = (credential) => {
  const tokenResponse = markTokenAsExpired.getTokenObjectFromCredential(credential);
  const getUserSettings = async () => {
    let userSettings;
    try {
      const responseBody = await fetchZoomApi(
        `users/me/settings?custom_query_fields=${settingsApiFilterResp}`
      );
      userSettings = zoomUserSettingsSchema.parse(responseBody);
    } catch (err) {
      log.error("Failed to retrieve zoom user settings", index$2.safeStringify(err));
    }
    return userSettings;
  };
  const translateEvent = async (event) => {
    const getRecurrence = ({
      recurringEvent,
      startTime,
      attendees
    }) => {
      if (!recurringEvent) {
        return;
      }
      let recurrence2;
      switch (recurringEvent.freq) {
        case index$2.Frequency.DAILY:
          recurrence2 = {
            type: 1
          };
          break;
        case index$2.Frequency.WEEKLY:
          recurrence2 = {
            type: 2,
            weekly_days: dayjs(startTime).tz(attendees[0].timeZone).day() + 1
          };
          break;
        case index$2.Frequency.MONTHLY:
          recurrence2 = {
            type: 3,
            monthly_day: dayjs(startTime).tz(attendees[0].timeZone).date()
          };
          break;
        default:
          return;
      }
      recurrence2.repeat_interval = recurringEvent.interval;
      if (recurringEvent.until) {
        recurrence2.end_date_time = recurringEvent.until.toISOString();
      } else {
        recurrence2.end_times = recurringEvent.count;
      }
      return {
        recurrence: {
          ...recurrence2
        }
      };
    };
    const truncateAgenda = (description) => {
      if (!description)
        return description;
      const maxLength = 1900;
      const trimmed = description.trimEnd();
      if (trimmed.length > maxLength) {
        return `${trimmed.substring(0, maxLength).trimEnd()}...`;
      }
      return trimmed;
    };
    const userSettings = await getUserSettings();
    const recurrence = getRecurrence(event);
    const waitingRoomEnabled = userSettings?.in_meeting?.waiting_room ?? false;
    const advancedWaitingRoomSettings = userSettings?.meeting_security?.waiting_room_settings;
    const hasAdvancedWaitingRoomSettings = waitingRoomEnabled && !!advancedWaitingRoomSettings && typeof advancedWaitingRoomSettings === "object" && Object.keys(advancedWaitingRoomSettings).length > 0;
    return {
      topic: event.title,
      type: 2,
      // Means that this is a scheduled meeting
      start_time: dayjs(event.startTime).utc().format(),
      duration: (new Date(event.endTime).getTime() - new Date(event.startTime).getTime()) / 6e4,
      //schedule_for: "string",   TODO: Used when scheduling the meeting for someone else (needed?)
      timezone: event.organizer.timeZone,
      password: userSettings?.schedule_meeting?.default_password_for_scheduled_meetings ?? void 0,
      agenda: truncateAgenda(event.description),
      settings: {
        host_video: true,
        participant_video: true,
        cn_meeting: false,
        // TODO: true if host meeting in China
        in_meeting: false,
        // TODO: true if host meeting in India
        join_before_host: !waitingRoomEnabled,
        mute_upon_entry: false,
        watermark: false,
        use_pmi: false,
        approval_type: 2,
        audio: "both",
        auto_recording: userSettings?.recording?.auto_recording || "none",
        enforce_login: false,
        registrants_email_notification: true,
        waiting_room: waitingRoomEnabled,
        ...hasAdvancedWaitingRoomSettings && {
          waiting_room_settings: advancedWaitingRoomSettings
        }
      },
      ...recurrence
    };
  };
  const handleZoomResponseJsonParseError = async ({
    error,
    clonedResponse
  }) => {
    log.error(
      "Error in JSON parsing Zoom API response",
      index$2.safeStringify({
        error: index$2.safeStringify(error),
        // Log Raw response body here.
        responseBody: await clonedResponse.text(),
        status: clonedResponse.status
      })
    );
    return null;
  };
  const fetchZoomApi = async (endpoint, options) => {
    const auth = new markTokenAsExpired.OAuthManager({
      credentialSyncVariables: {
        APP_CREDENTIAL_SHARING_ENABLED: index$2.APP_CREDENTIAL_SHARING_ENABLED,
        CREDENTIAL_SYNC_ENDPOINT: index$2.CREDENTIAL_SYNC_ENDPOINT,
        CREDENTIAL_SYNC_SECRET: index$2.CREDENTIAL_SYNC_SECRET,
        CREDENTIAL_SYNC_SECRET_HEADER_NAME: index$2.CREDENTIAL_SYNC_SECRET_HEADER_NAME
      },
      resourceOwner: {
        type: "user",
        id: credential.userId
      },
      appSlug: CalEventParser.metadata$5.slug,
      currentTokenObject: tokenResponse,
      fetchNewTokenObject: async ({ refreshToken }) => {
        if (!refreshToken) {
          return null;
        }
        const clientCredentials = await getZoomAppKeys();
        const { client_id, client_secret } = clientCredentials;
        const authHeader = `Basic ${Buffer.from(`${client_id}:${client_secret}`).toString("base64")}`;
        return fetch("https://zoom.us/oauth/token", {
          method: "POST",
          headers: {
            Authorization: authHeader,
            "Content-Type": "application/x-www-form-urlencoded"
          },
          body: new URLSearchParams({
            refresh_token: refreshToken,
            grant_type: "refresh_token"
          })
        });
      },
      isTokenObjectUnusable: async function(response) {
        const myLog = index$2.logger.getSubLogger({ prefix: ["zoomvideo:isTokenObjectUnusable"] });
        myLog.info(index$2.safeStringify({ status: response.status, ok: response.ok }));
        if (!response.ok) {
          let responseBody;
          const responseToUseInCaseOfError = response.clone();
          try {
            responseBody = await response.json();
          } catch (e) {
            return await handleZoomResponseJsonParseError({
              error: e,
              clonedResponse: responseToUseInCaseOfError
            });
          }
          myLog.debug(index$2.safeStringify({ responseBody }));
          if (responseBody.error === "invalid_grant") {
            return { reason: responseBody.error };
          }
        }
        return null;
      },
      isAccessTokenUnusable: async function(response) {
        const myLog = index$2.logger.getSubLogger({ prefix: ["zoomvideo:isAccessTokenUnusable"] });
        myLog.info(index$2.safeStringify({ status: response.status, ok: response.ok }));
        if (!response.ok) {
          let responseBody;
          const responseToUseInCaseOfError = response.clone();
          try {
            responseBody = await response.json();
          } catch (e) {
            return await handleZoomResponseJsonParseError({
              error: e,
              clonedResponse: responseToUseInCaseOfError
            });
          }
          myLog.debug(index$2.safeStringify({ responseBody }));
          if (responseBody.code === 124) {
            return { reason: responseBody.message ?? "" };
          }
        }
        return null;
      },
      invalidateTokenObject: () => markTokenAsExpired.invalidateCredential(credential.id),
      expireAccessToken: () => markTokenAsExpired.markTokenAsExpired(credential),
      updateTokenObject: async (newTokenObject) => {
        await index$2.prisma.credential.update({
          where: {
            id: credential.id
          },
          data: {
            key: newTokenObject
          }
        });
      }
    });
    const { json } = await auth.request({
      url: `https://api.zoom.us/v2/${endpoint}`,
      options: {
        method: "GET",
        ...options,
        headers: {
          ...options?.headers
        }
      }
    });
    return json;
  };
  return {
    getAvailability: async () => {
      try {
        const responseBody = await fetchZoomApi("users/me/meetings?type=scheduled&page_size=300");
        const data = zoomMeetingsSchema.parse(responseBody);
        return data.meetings.map((meeting) => ({
          start: meeting.start_time,
          end: new Date(new Date(meeting.start_time).getTime() + meeting.duration * 6e4).toISOString()
        }));
      } catch (err) {
        log.error("Failed to get availability", index$2.safeStringify(err));
        return [];
      }
    },
    createMeeting: async (event) => {
      try {
        const response = await fetchZoomApi("users/me/meetings", {
          method: "POST",
          headers: {
            "Content-Type": "application/json"
          },
          body: JSON.stringify(await translateEvent(event))
        });
        const result = zoomEventResultSchema.parse(response);
        if (result.id && result.join_url) {
          return {
            type: "zoom_video",
            id: result.id.toString(),
            password: result.password || "",
            url: result.join_url
          };
        }
        throw new Error(`Failed to create meeting. Response is ${JSON.stringify(result)}`);
      } catch (err) {
        log.error(
          "Zoom meeting creation failed",
          index$2.safeStringify({ error: index$2.safeStringify(err), event: piiFreeData.getPiiFreeCalendarEvent(event) })
        );
        throw new Error("Unexpected error");
      }
    },
    deleteMeeting: async (uid) => {
      try {
        await fetchZoomApi(`meetings/${uid}`, {
          method: "DELETE"
        });
        return Promise.resolve();
      } catch (err) {
        return Promise.reject(new Error("Failed to delete meeting"));
      }
    },
    updateMeeting: async (bookingRef, event) => {
      try {
        await fetchZoomApi(`meetings/${bookingRef.uid}`, {
          method: "PATCH",
          headers: {
            "Content-Type": "application/json"
          },
          body: JSON.stringify(await translateEvent(event))
        });
        const updatedMeeting = await fetchZoomApi(`meetings/${bookingRef.uid}`);
        const result = zoomEventResultSchema.parse(updatedMeeting);
        return {
          type: "zoom_video",
          id: result.id.toString(),
          password: result.password || "",
          url: result.join_url
        };
      } catch (err) {
        log.error(
          "Failed to update meeting",
          index$2.safeStringify({ error: err, event: piiFreeData.getPiiFreeCalendarEvent(event) })
        );
        return Promise.reject(new Error("Failed to update meeting"));
      }
    }
  };
};
const ZoomVideoApiAdapter$1 = ZoomVideoApiAdapter;
const index$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
  __proto__: null,
  VideoApiAdapter: ZoomVideoApiAdapter$1,
  getZoomAppKeys
}, Symbol.toStringTag, { value: "Module" }));
async function handler$1(req) {
  await index$2.prisma.user.findFirstOrThrow({
    where: {
      id: req.session?.user?.id
    },
    select: {
      id: true
    }
  });
  const { client_id } = await getZoomAppKeys();
  const state = encodeOAuthState.encodeOAuthState(req);
  const params = {
    response_type: "code",
    client_id,
    redirect_uri: `${index$2.WEBAPP_URL_FOR_OAUTH}/api/integrations/zoomvideo/callback`,
    state
  };
  const query = querystring.stringify(params);
  const url = `https://zoom.us/oauth/authorize?${query}`;
  return { url };
}
const add = defaultHandler.defaultHandler({
  GET: Promise.resolve({ default: defaultResponder.defaultResponder(handler$1) })
});
async function handler(req, res) {
  const state = decodeOAuthState.decodeOAuthState(req);
  const { code } = req.query;
  const { client_id, client_secret } = await getZoomAppKeys();
  const redirectUri = encodeURI(`${index$2.WEBAPP_URL_FOR_OAUTH}/api/integrations/zoomvideo/callback`);
  const authHeader = `Basic ${Buffer.from(`${client_id}:${client_secret}`).toString("base64")}`;
  const result = await fetch(
    `https://zoom.us/oauth/token?grant_type=authorization_code&code=${code}&redirect_uri=${redirectUri}`,
    {
      method: "POST",
      headers: {
        Authorization: authHeader
      }
    }
  );
  if (result.status !== 200) {
    let errorMessage = "Something is wrong with Zoom API";
    try {
      const responseBody2 = await result.json();
      errorMessage = responseBody2.error;
    } catch (e) {
      errorMessage = await result.clone().text();
    }
    res.status(400).json({ message: errorMessage });
    return;
  }
  const responseBody = await result.json();
  if (responseBody.error) {
    res.status(400).json({ message: responseBody.error });
    return;
  }
  responseBody.expiry_date = Math.round(Date.now() + responseBody.expires_in * 1e3);
  delete responseBody.expires_in;
  const userId = req.session?.user.id;
  if (!userId) {
    return res.status(404).json({ message: "No user found" });
  }
  const existingCredentialZoomVideo = await index$2.prisma.credential.findMany({
    select: {
      id: true
    },
    where: {
      type: "zoom_video",
      userId: req.session?.user.id,
      appId: "zoom"
    }
  });
  const credentialIdsToDelete = existingCredentialZoomVideo.map((item) => item.id);
  if (credentialIdsToDelete.length > 0) {
    await index$2.prisma.credential.deleteMany({ where: { id: { in: credentialIdsToDelete }, userId } });
  }
  await createOAuthAppCredential.createOAuthAppCredential({ appId: "zoom", type: "zoom_video" }, responseBody, req);
  res.redirect(
    getSafeRedirectUrl.getSafeRedirectUrl(state?.returnTo) ?? getInstalledAppPath.getInstalledAppPath({ variant: "conferencing", slug: "zoom" })
  );
}
const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
  __proto__: null,
  add,
  callback: handler
}, Symbol.toStringTag, { value: "Module" }));
exports.metadata = CalEventParser.metadata$5;
exports.api = index;
exports.lib = index$1;
