import { HttpStatusCode } from "@turbostarter/shared/constants";
import { logger } from "@turbostarter/shared/logger";
import { HttpException } from "@turbostarter/shared/utils";

import { checkoutStatusChangeHandler } from "../checkout";
import { env } from "../env";
import { subscriptionStatusChangeHandler } from "../subscription";

import { LEMON_SQUEEZY_SIGNATURE_HEADER } from "./constants";
import { validateSignature } from "./signing";
import { webhookHasData, webhookHasMeta } from "./type-guards";

import type { BillingProviderStrategy } from "../../types";

export const webhookHandler: BillingProviderStrategy["webhookHandler"] = async (
  req,
  callbacks,
) => {
  const body = await req.text();
  const sig = req.headers.get(LEMON_SQUEEZY_SIGNATURE_HEADER);

  if (!sig) {
    throw new HttpException(HttpStatusCode.BAD_REQUEST, {
      code: "billing:error.webhook.signatureNotFound",
    });
  }

  await validateSignature(sig, env.LEMON_SQUEEZY_SIGNING_SECRET, body);

  const data = JSON.parse(body);

  if (!webhookHasMeta(data)) {
    throw new HttpException(HttpStatusCode.BAD_REQUEST, {
      code: "billing:error.webhook.metaInvalid",
    });
  }

  const type = data.meta.event_name;

  logger.info(`🔔  Webhook received: ${type}`);

  if (!webhookHasData(data)) {
    throw new HttpException(HttpStatusCode.BAD_REQUEST, {
      code: "billing:error.webhook.dataInvalid",
    });
  }

  await callbacks?.onEvent?.(data);

  switch (type) {
    case "subscription_created":
      await callbacks?.onSubscriptionCreated?.(data.data.id);
      await subscriptionStatusChangeHandler({
        id: data.data.id,
      });
      break;
    case "subscription_updated":
      await callbacks?.onSubscriptionUpdated?.(data.data.id);
      await subscriptionStatusChangeHandler({
        id: data.data.id,
      });
      break;
    case "subscription_expired":
      await callbacks?.onSubscriptionDeleted?.(data.data.id);
      await subscriptionStatusChangeHandler({
        id: data.data.id,
      });
      break;
    case "order_created":
      await callbacks?.onCheckoutSessionCompleted?.(data.data.id);
      await checkoutStatusChangeHandler({
        id: data.data.id,
      });
      break;
  }

  return new Response(JSON.stringify({ received: true }), {
    status: 200,
    headers: {
      "Content-Type": "application/json",
    },
  });
};
