import { v4 } from "uuid";
import z from "zod";
import { x as IS_PRODUCTION, p as prisma, l as logger, q as WEBAPP_URL, a as safeStringify } from "./i18n-49-r55rr.js";
import { E as ErrorCode } from "./errorCodes-E8kmN1h5.js";
import { p as paymentOptionEnum } from "./zod-utils-0_1_B2QB.js";
class Paypal {
  constructor(opts) {
    this.accessToken = null;
    this.expiresAt = null;
    this.fetcher = async (endpoint, init) => {
      await this.getAccessToken();
      return fetch(`${this.url}${endpoint}`, {
        method: "get",
        ...init,
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${this.accessToken}`,
          ...init?.headers
        }
      });
    };
    this.url = IS_PRODUCTION ? "https://api-m.paypal.com" : "https://api-m.sandbox.paypal.com";
    this.clientId = opts.clientId;
    this.secretKey = opts.secretKey;
  }
  async getAccessToken() {
    if (this.accessToken && this.expiresAt && this.expiresAt > Date.now()) {
      return;
    }
    const headers = {
      "Content-Type": "application/x-www-form-urlencoded",
      Authorization: `Basic ${Buffer.from(`${this.clientId}:${this.secretKey}`).toString("base64")}`
    };
    const body = new URLSearchParams();
    body.append("grant_type", "client_credentials");
    try {
      const response = await fetch(`${this.url}/v1/oauth2/token`, {
        method: "POST",
        headers,
        body
      });
      if (response.ok) {
        const { access_token, expires_in } = await response.json();
        this.accessToken = access_token;
        this.expiresAt = Date.now() + expires_in;
      } else if (response?.status) {
        console.error(`Request failed with status ${response.status}`);
      }
    } catch (error) {
      console.error(error);
    }
  }
  // Orders
  async createOrder({
    referenceId,
    amount,
    currency,
    returnUrl,
    cancelUrl,
    intent = "CAPTURE"
  }) {
    const createOrderRequestBody = {
      intent,
      purchase_units: [
        {
          reference_id: referenceId,
          amount: {
            currency_code: currency,
            value: (amount / 100).toString()
          }
        }
      ],
      payment_source: {
        paypal: {
          experience_context: {
            user_action: "PAY_NOW",
            return_url: returnUrl,
            cancel_url: cancelUrl
          }
        }
      }
    };
    try {
      const response = await this.fetcher("/v2/checkout/orders", {
        method: "POST",
        headers: {
          "PayPal-Request-Id": v4()
        },
        body: JSON.stringify(createOrderRequestBody)
      });
      if (response.ok) {
        const createOrderResponse = await response.json();
        return createOrderResponse;
      } else {
        console.error(`Request failed with status ${response.status}`);
      }
    } catch (error) {
      console.error(error);
    }
    return {};
  }
  async captureOrder(orderId) {
    try {
      const captureResult = await this.fetcher(`/v2/checkout/orders/${orderId}/capture`, {
        method: "POST"
      });
      if (captureResult.ok) {
        const result = await captureResult.json();
        if (result?.status === "COMPLETED") {
          const payment = await prisma.payment.findFirst({
            where: {
              externalId: orderId
            },
            select: {
              id: true,
              bookingId: true,
              data: true
            }
          });
          if (!payment) {
            throw new Error("Payment not found");
          }
          await prisma.payment.update({
            where: {
              id: payment?.id
            },
            data: {
              success: true,
              data: Object.assign(
                {},
                { ...payment?.data, capture: result.id }
              )
            }
          });
          await prisma.booking.update({
            where: {
              id: payment.bookingId
            },
            data: {
              status: "ACCEPTED"
            }
          });
          return true;
        }
      }
    } catch (error) {
      console.error(error);
      throw error;
    }
    return false;
  }
  async createWebhook() {
    const body = {
      url: `${WEBAPP_URL}/api/integrations/paypal/webhook`,
      event_types: [
        {
          name: "CHECKOUT.ORDER.APPROVED"
        },
        {
          name: "CHECKOUT.ORDER.COMPLETED"
        }
      ]
    };
    try {
      const response = await this.fetcher(`/v1/notifications/webhooks`, {
        method: "POST",
        body: JSON.stringify(body)
      });
      if (!response.ok) {
        const message = `${response.statusText}: ${JSON.stringify(await response.json())}`;
        throw new Error(message);
      }
      const result = await response.json();
      return result.id;
    } catch (e) {
      logger.error("Error creating webhook", e);
    }
    return false;
  }
  async listWebhooks() {
    try {
      const response = await this.fetcher(`/v1/notifications/webhooks`);
      if (response.ok) {
        const { webhooks } = await response.json();
        return webhooks.filter((webhook) => {
          return webhook.url.includes("api/integrations/paypal/webhook");
        }).map((webhook) => webhook.id);
      }
    } catch (error) {
      console.error(error);
      return [];
    }
    return [];
  }
  async deleteWebhook(webhookId) {
    try {
      const response = await this.fetcher(`/v1/notifications/webhooks/${webhookId}`, {
        method: "DELETE"
      });
      if (response.ok) {
        return true;
      }
    } catch (error) {
      console.error(error);
    }
    return false;
  }
  async test() {
    try {
      await this.getAccessToken();
    } catch (error) {
      console.error(error);
      return false;
    }
    return true;
  }
  async verifyWebhook(options) {
    const parseRequest = webhookEventVerifyRequestSchema.safeParse(options);
    if (!parseRequest.success) {
      console.error(parseRequest.error);
      throw new Error("Request is malformed");
    }
    const stringy = JSON.stringify({
      auth_algo: options.body.auth_algo,
      cert_url: options.body.cert_url,
      transmission_id: options.body.transmission_id,
      transmission_sig: options.body.transmission_sig,
      transmission_time: options.body.transmission_time,
      webhook_id: options.body.webhook_id
    });
    const bodyToString = `${stringy.slice(0, -1)},"webhook_event":${options.body.webhook_event}}`;
    try {
      const response = await this.fetcher(`/v1/notifications/verify-webhook-signature`, {
        method: "POST",
        body: bodyToString
      });
      if (!response.ok) {
        throw response;
      }
      const data = await response.json();
      if (data.verification_status !== "SUCCESS") {
        throw data;
      }
    } catch (err) {
      console.error(err);
      throw err;
    }
  }
}
const Paypal$1 = Paypal;
const webhookEventVerifyRequestSchema = z.object({
  body: z.object({
    auth_algo: z.string(),
    cert_url: z.string(),
    transmission_id: z.string(),
    transmission_sig: z.string(),
    transmission_time: z.string(),
    webhook_event: z.string(),
    webhook_id: z.string()
  }).required()
});
const log = logger.getSubLogger({ prefix: ["payment-service:paypal"] });
const paypalCredentialKeysSchema = z.object({
  client_id: z.string(),
  secret_key: z.string(),
  webhook_id: z.string()
});
class PaymentService {
  constructor(credentials) {
    const keyParsing = paypalCredentialKeysSchema.safeParse(credentials.key);
    if (keyParsing.success) {
      this.credentials = keyParsing.data;
    } else {
      this.credentials = null;
    }
  }
  async create(payment, bookingId) {
    try {
      const booking = await prisma.booking.findUnique({
        select: {
          uid: true,
          title: true
        },
        where: {
          id: bookingId
        }
      });
      if (!booking || !this.credentials) {
        throw new Error();
      }
      const uid = v4();
      const paypalClient = new Paypal$1({
        clientId: this.credentials.client_id,
        secretKey: this.credentials.secret_key
      });
      const orderResult = await paypalClient.createOrder({
        referenceId: uid,
        amount: payment.amount,
        currency: payment.currency,
        returnUrl: `${WEBAPP_URL}/api/integrations/paypal/capture?bookingUid=${booking.uid}`,
        cancelUrl: `${WEBAPP_URL}/payment/${uid}`
      });
      const paymentData = await prisma.payment.create({
        data: {
          uid,
          app: {
            connect: {
              slug: "paypal"
            }
          },
          booking: {
            connect: {
              id: bookingId
            }
          },
          amount: payment.amount,
          externalId: orderResult.id,
          currency: payment.currency,
          data: Object.assign({}, { order: orderResult }),
          fee: 0,
          refunded: false,
          success: false
        }
      });
      if (!paymentData) {
        throw new Error();
      }
      return paymentData;
    } catch (error) {
      log.error("Paypal: Payment could not be created for bookingId", bookingId, safeStringify(error));
      throw new Error(ErrorCode.PaymentCreationFailure);
    }
  }
  async update() {
    throw new Error("Method not implemented.");
  }
  async refund() {
    throw new Error("Method not implemented.");
  }
  async collectCard(payment, bookingId, paymentOption, _bookerEmail) {
    if (paymentOptionEnum.parse(paymentOption) !== "HOLD") {
      throw new Error("Payment option is not compatible with create method");
    }
    try {
      const booking = await prisma.booking.findUnique({
        select: {
          uid: true,
          title: true
        },
        where: {
          id: bookingId
        }
      });
      if (!booking || !this.credentials) {
        throw new Error();
      }
      const uid = v4();
      const paypalClient = new Paypal$1({
        clientId: this.credentials.client_id,
        secretKey: this.credentials.secret_key
      });
      const preference = await paypalClient.createOrder({
        referenceId: uid,
        amount: payment.amount,
        currency: payment.currency,
        returnUrl: `${WEBAPP_URL}/booking/${booking.uid}`,
        cancelUrl: `${WEBAPP_URL}/payment/${uid}`,
        intent: "AUTHORIZE"
      });
      const paymentData = await prisma.payment.create({
        data: {
          uid,
          app: {
            connect: {
              slug: "paypal"
            }
          },
          booking: {
            connect: {
              id: bookingId
            }
          },
          amount: payment.amount,
          currency: payment.currency,
          data: Object.assign({}, preference),
          fee: 0,
          refunded: false,
          success: false,
          paymentOption: paymentOption || "ON_BOOKING",
          externalId: preference?.id
        }
      });
      if (!paymentData) {
        throw new Error();
      }
      return paymentData;
    } catch (error) {
      log.error(
        "Paypal: Payment method could not be collected for bookingId",
        bookingId,
        safeStringify(error)
      );
      throw new Error("Paypal: Payment method could not be collected");
    }
  }
  chargeCard() {
    throw new Error("Method not implemented.");
  }
  getPaymentPaidStatus() {
    throw new Error("Method not implemented.");
  }
  getPaymentDetails() {
    throw new Error("Method not implemented.");
  }
  afterPayment() {
    return Promise.resolve();
  }
  deletePayment() {
    return Promise.resolve(false);
  }
  isSetupAlready() {
    return !!this.credentials;
  }
}
export {
  PaymentService,
  paypalCredentialKeysSchema
};
