import { stringify } from "querystring";
import { p as prisma, J as WEBAPP_URL, l as logger } from "./user--mpkKOwW.js";
import { d as defaultHandler } from "./defaultHandler-yvm7aMmf.js";
import { d as defaultResponder } from "./defaultResponder-GZldAmhY.js";
import { z } from "zod";
import { g as getAppKeysFromSlug } from "./getAppKeysFromSlug-IDaQR-Xd.js";
import { g as getInstalledAppPath } from "./getInstalledAppPath-d831DYzR.js";
import { d as decodeOAuthState } from "./decodeOAuthState-KhwGAxqw.js";
import { a as appConfig } from "./config--B3GYrdT.js";
const getBasecampKeys = async () => {
  const appKeys = await getAppKeysFromSlug("basecamp3");
  return appKeysSchema.parse(appKeys);
};
const appKeysSchema = z.object({
  client_id: z.string().min(1),
  client_secret: z.string().min(1),
  user_agent: z.string().min(1)
});
async function handler$1(req) {
  await prisma.user.findFirstOrThrow({
    where: {
      id: req.session?.user?.id
    },
    select: {
      id: true
    }
  });
  const { client_id } = await getBasecampKeys();
  const params = {
    type: "web_server",
    client_id
  };
  const query = stringify(params);
  const url = `https://launchpad.37signals.com/authorization/new?${query}&redirect_uri=${WEBAPP_URL}/api/integrations/basecamp3/callback`;
  return { url };
}
const add = defaultHandler({
  GET: Promise.resolve({ default: defaultResponder(handler$1) })
});
async function handler(req, res) {
  const { code } = req.query;
  const { client_id, client_secret, user_agent } = await getAppKeysFromSlug("basecamp3");
  const redirectUri = `${WEBAPP_URL}/api/integrations/basecamp3/callback`;
  const params = new URLSearchParams({
    type: "web_server",
    client_id,
    client_secret,
    redirect_uri: redirectUri,
    code
  });
  const accessTokenResponse = await fetch(
    `https://launchpad.37signals.com/authorization/token?${params.toString()}`,
    {
      method: "POST"
    }
  );
  if (accessTokenResponse.status !== 200) {
    let errorMessage = "Error with Basecamp 3 API";
    try {
      const responseBody = await accessTokenResponse.json();
      errorMessage = responseBody.error;
    } catch (e) {
    }
    res.status(400).json({ message: errorMessage });
    return;
  }
  const tokenResponseBody = await accessTokenResponse.json();
  if (tokenResponseBody.error) {
    res.status(400).json({ message: tokenResponseBody.error });
    return;
  }
  tokenResponseBody["expires_at"] = Date.now() + 1e3 * 3600 * 24 * 14;
  const userAuthResponse = await fetch("https://launchpad.37signals.com/authorization.json", {
    headers: {
      "User-Agent": user_agent,
      Authorization: `Bearer ${tokenResponseBody.access_token}`
    }
  });
  if (userAuthResponse.status !== 200) {
    let errorMessage = "Error with Basecamp 3 API";
    try {
      const body = await userAuthResponse.json();
      errorMessage = body.error;
    } catch (e) {
    }
    res.status(400).json({ message: errorMessage });
    return;
  }
  const authResponseBody = await userAuthResponse.json();
  const userId = req.session?.user.id;
  if (!userId) {
    return res.status(404).json({ message: "No user found" });
  }
  await prisma.user.update({
    where: {
      id: req.session?.user.id
    },
    data: {
      credentials: {
        create: {
          type: appConfig.type,
          key: { ...tokenResponseBody, account: authResponseBody.accounts[0] },
          appId: appConfig.slug
        }
      }
    }
  });
  decodeOAuthState(req);
  res.redirect(getInstalledAppPath({ variant: appConfig.variant, slug: appConfig.slug }));
}
const index$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
  __proto__: null,
  add,
  callback: handler
}, Symbol.toStringTag, { value: "Module" }));
const refreshAccessToken = async (credential) => {
  const { client_id: clientId, client_secret: clientSecret, user_agent: userAgent } = await getBasecampKeys();
  const credentialKey = credential.key;
  const tokenInfo = await fetch(
    `https://launchpad.37signals.com/authorization/token?type=refresh&refresh_token=${credentialKey.refresh_token}&client_id=${clientId}&redirect_uri=${WEBAPP_URL}&client_secret=${clientSecret}`,
    { method: "POST", headers: { "User-Agent": userAgent } }
  );
  const tokenInfoJson = await tokenInfo.json();
  tokenInfoJson["expires_at"] = Date.now() + 1e3 * 3600 * 24 * 14;
  const newCredential = await prisma.credential.update({
    where: { id: credential.id },
    data: {
      key: { ...credentialKey, ...tokenInfoJson }
    }
  });
  return newCredential.key;
};
function hasFileExtension(url) {
  const fileName = url.substring(url.lastIndexOf("/") + 1);
  return fileName.includes(".") && !fileName.substring(fileName.lastIndexOf(".")).includes("/");
}
function getFileExtension(url) {
  if (!hasFileExtension(url))
    return "ics";
  const fileName = url.substring(url.lastIndexOf("/") + 1);
  return fileName.substring(fileName.lastIndexOf(".") + 1);
}
class BasecampCalendarService {
  constructor(credential) {
    this.credentials = {};
    this.headers = {};
    this.userAgent = "";
    this.integrationName = "";
    this.accessToken = "";
    this.scheduleId = 0;
    this.userId = 0;
    this.projectId = 0;
    this.basecampAuth = async (credential2) => {
      const credentialKey = credential2.key;
      this.scheduleId = credentialKey.scheduleId;
      this.userId = credentialKey.account.id;
      this.projectId = credentialKey.projectId;
      const isTokenValid = (credentialToken) => {
        const isValid = credentialToken.access_token && credentialToken.expires_at > Date.now();
        if (isValid)
          this.accessToken = credentialToken.access_token;
        return isValid;
      };
      const refreshAccessToken$1 = async (credentialToken) => {
        try {
          const newCredentialKey = await refreshAccessToken(credentialToken);
          this.accessToken = newCredentialKey.access_token;
        } catch (err) {
          this.log.error(err);
        }
      };
      return {
        configureToken: () => isTokenValid(credentialKey) ? Promise.resolve() : refreshAccessToken$1(credential2)
      };
    };
    this.getUserTimezoneFromDB = async (id) => {
      const user = await prisma.user.findUnique({
        where: {
          id
        },
        select: {
          timeZone: true
        }
      });
      return user?.timeZone;
    };
    this.getUserId = (selectedCalendars) => {
      if (selectedCalendars.length === 0) {
        return null;
      }
      return selectedCalendars[0].userId || null;
    };
    this.isValidFormat = (url) => {
      const allowedExtensions = ["eml", "ics"];
      const urlExtension = getFileExtension(url);
      if (!allowedExtensions.includes(urlExtension)) {
        console.error(`Unsupported calendar object format: ${urlExtension}`);
        return false;
      }
      return true;
    };
    this.integrationName = "basecamp3";
    getAppKeysFromSlug("basecamp3").then(({ user_agent }) => {
      this.userAgent = user_agent;
    });
    this.auth = this.basecampAuth(credential).then((c) => c);
    this.log = logger.getSubLogger({ prefix: [`[[lib] ${this.integrationName}`] });
  }
  async getBasecampDescription(event) {
    const timeZone = await this.getUserTimezoneFromDB(event.organizer?.id);
    const date = new Date(event.startTime).toDateString();
    const startTime = new Date(event.startTime).toLocaleTimeString("en-US", {
      hour: "numeric",
      hour12: true,
      minute: "numeric"
    });
    const endTime = new Date(event.endTime).toLocaleTimeString("en-US", {
      hour: "numeric",
      hour12: true,
      minute: "numeric"
    });
    const baseString = `<div>Event title: ${event.title}<br/>Date and time: ${date}, ${startTime} - ${endTime} ${timeZone}<br/>View on Cal.com: <a target="_blank" rel="noreferrer" class="autolinked" data-behavior="truncate" href="https://app.cal.com/booking/${event.uid}">https://app.cal.com/booking/${event.uid}</a> `;
    const guestString = `<br/>Guests: ${event.attendees.reduce((acc, attendee) => {
      return `${acc}<br/><a target="_blank" rel="noreferrer" class="autolinked" data-behavior="truncate" href="mailto:${attendee.email}">${attendee.email}</a>`;
    }, "")}`;
    const videoString = event.videoCallData ? `<br/>Join on video: ${event.videoCallData.url}</div>` : "</div>";
    return baseString + guestString + videoString;
  }
  async createEvent(event) {
    try {
      const auth = await this.auth;
      await auth.configureToken();
      const description = await this.getBasecampDescription(event);
      const basecampEvent = await fetch(
        `https://3.basecampapi.com/${this.userId}/buckets/${this.projectId}/schedules/${this.scheduleId}/entries.json`,
        {
          method: "POST",
          headers: {
            "User-Agent": this.userAgent,
            Authorization: `Bearer ${this.accessToken}`,
            "Content-Type": "application/json"
          },
          body: JSON.stringify({
            description,
            summary: `Cal.com: ${event.title}`,
            starts_at: new Date(event.startTime).toISOString(),
            ends_at: new Date(event.endTime).toISOString()
          })
        }
      );
      const meetingJson = await basecampEvent.json();
      const id = meetingJson.id;
      this.log.debug("event:creation:ok", { json: meetingJson });
      return Promise.resolve({
        id,
        uid: id,
        type: this.integrationName,
        password: "",
        url: "",
        additionalInfo: { meetingJson }
      });
    } catch (err) {
      this.log.debug("event:creation:notOk", err);
      return Promise.reject({ error: "Unable to book basecamp meeting" });
    }
  }
  async updateEvent(uid, event) {
    try {
      const auth = await this.auth;
      await auth.configureToken();
      const description = await this.getBasecampDescription(event);
      const basecampEvent = await fetch(
        `https://3.basecampapi.com/${this.userId}/buckets/${this.projectId}/schedule_entries/${uid}.json`,
        {
          method: "PUT",
          headers: {
            "User-Agent": this.userAgent,
            Authorization: `Bearer ${this.accessToken}`,
            "Content-Type": "application/json"
          },
          body: JSON.stringify({
            description,
            summary: `Cal.com: ${event.title}`,
            starts_at: new Date(event.startTime).toISOString(),
            ends_at: new Date(event.endTime).toISOString()
          })
        }
      );
      const meetingJson = await basecampEvent.json();
      const id = meetingJson.id;
      return {
        uid: id,
        type: event.type,
        id,
        password: "",
        url: "",
        additionalInfo: { meetingJson }
      };
    } catch (reason) {
      this.log.error(reason);
      throw reason;
    }
  }
  async deleteEvent(uid) {
    try {
      const auth = await this.auth;
      await auth.configureToken();
      const deletedEventResponse = await fetch(
        `https://3.basecampapi.com/${this.userId}/buckets/${this.projectId}/recordings/${uid}/status/trashed.json`,
        {
          method: "PUT",
          headers: {
            "User-Agent": this.userAgent,
            Authorization: `Bearer ${this.accessToken}`,
            "Content-Type": "application/json"
          }
        }
      );
      if (deletedEventResponse.ok) {
        Promise.resolve("Deleted basecamp meeting");
      } else
        Promise.reject("Error cancelling basecamp event");
    } catch (reason) {
      this.log.error(reason);
      throw reason;
    }
  }
  async getAvailability(_dateFrom, _dateTo, _selectedCalendars) {
    return Promise.resolve([]);
  }
  async listCalendars(_event) {
    return Promise.resolve([]);
  }
}
const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
  __proto__: null,
  CalendarService: BasecampCalendarService
}, Symbol.toStringTag, { value: "Module" }));
export {
  index$1 as api,
  index as lib
};
