import { OrgService } from "@/internal/orgs/OrgService.js";
import { AuthType, LoggerAction, Organization } from "@autumn/shared";
import express from "express";
import stripe, { Stripe } from "stripe";
import { handleCheckoutSessionCompleted } from "./webhookHandlers/handleCheckoutCompleted.js";
import { handleSubscriptionUpdated } from "./webhookHandlers/handleSubUpdated.js";
import { handleSubDeleted } from "./webhookHandlers/handleSubDeleted.js";
import { handleSubCreated } from "./webhookHandlers/handleSubCreated.js";
import { getStripeWebhookSecret } from "@/internal/orgs/orgUtils.js";
import { handleInvoicePaid } from "./webhookHandlers/handleInvoicePaid.js";
import { handleRequestError } from "@/utils/errorUtils.js";
import { handleInvoiceCreated } from "./webhookHandlers/handleInvoiceCreated/handleInvoiceCreated.js";
import chalk from "chalk";
import { handleInvoiceFinalized } from "./webhookHandlers/handleInvoiceFinalized.js";
import { handleSubscriptionScheduleCanceled } from "./webhookHandlers/handleSubScheduleCanceled.js";
import { createLogtailWithContext } from "../logtail/logtailUtils.js";
import { handleCusDiscountDeleted } from "./webhookHandlers/handleCusDiscountDeleted.js";
import { ExtendedRequest } from "@/utils/models/Request.js";
import { createStripeCli } from "./utils.js";

export const stripeWebhookRouter = express.Router();

const logStripeWebhook = ({
  req,
  event,
}: {
  req: ExtendedRequest;
  event: Stripe.Event;
}) => {
  console.log(
    `${chalk.yellow("STRIPE").padEnd(18)} ${event.type.padEnd(30)} ${req.org.slug} | ${event.id}`,
  );
};

stripeWebhookRouter.post(
  "/:orgId/:env",
  express.raw({ type: "application/json" }),
  async (request: any, response: any) => {
    const sig = request.headers["stripe-signature"];
    let event;

    const { orgId, env } = request.params;
    const { db } = request;

    let org: Organization;

    const data = await OrgService.getWithFeatures({
      db: request.db,
      orgId,
      env,
      allowNotFound: true,
    });

    if (!data) {
      response.status(200).send(`Org ${orgId} not found`);
      return;
    }

    request.org = data.org;
    request.features = data.features;
    request.env = env;
    org = data.org;

    if (!org.stripe_config) {
      console.log(`Org ${orgId} does not have a stripe config`);
      response.status(200).send(`Org ${orgId} does not have a stripe config`);
      return;
    }

    const webhookSecret = getStripeWebhookSecret(org, env);
    try {
      event = stripe.webhooks.constructEvent(request.body, sig, webhookSecret);
    } catch (err: any) {
      response.status(400).send(`Webhook Error: ${err.message}`);
      return;
    }

    try {
      request.body = JSON.parse(request.body);
      request.authType = AuthType.Stripe;
    } catch (error) {
      console.log("Error parsing body", error);
    }

    logStripeWebhook({ req: request, event });

    const logger = createLogtailWithContext({
      action: LoggerAction.StripeWebhook,
      event_type: event.type,
      data: event.data,
      org_id: orgId,
      org_slug: org.slug,
      env,
    });

    const stripeCli = createStripeCli({ org, env });
    try {
      switch (event.type) {
        case "customer.subscription.created":
          await handleSubCreated({
            db,
            org,
            subData: event.data.object,
            env,
            logger,
          });
          break;

        case "customer.subscription.updated":
          const subscription = event.data.object;
          await handleSubscriptionUpdated({
            req: request,
            db,
            org,
            subscription,
            previousAttributes: event.data.previous_attributes,
            env,
            logger,
          });
          break;

        case "customer.subscription.deleted":
          await handleSubDeleted({
            req: request,
            stripeCli,
            data: event.data.object,
            logger,
          });
          break;

        case "checkout.session.completed":
          const checkoutSession = event.data.object;
          await handleCheckoutSessionCompleted({
            req: request,
            db,
            data: checkoutSession,
            org,
            env,
            logger,
          });
          break;

        // Triggered when payment through Stripe is successful
        case "invoice.paid":
          const invoice = event.data.object;
          await handleInvoicePaid({
            db,
            org,
            invoiceData: invoice,
            env,
            event,
            req: request,
          });
          break;

        case "invoice.created":
          const createdInvoice = event.data.object;
          await handleInvoiceCreated({
            db,
            org,
            data: createdInvoice,
            env,
          });
          break;

        case "invoice.finalized":
          const finalizedInvoice = event.data.object;
          await handleInvoiceFinalized({
            db,
            org,
            data: finalizedInvoice,
            env,
            logger,
          });
          break;
        case "subscription_schedule.canceled":
          const canceledSchedule = event.data.object;
          await handleSubscriptionScheduleCanceled({
            db,
            org,
            env,
            schedule: canceledSchedule,
            logger,
          });
          break;

        case "customer.discount.deleted":
          await handleCusDiscountDeleted({
            db,
            org,
            discount: event.data.object,
            env,
            logger,
            res: response,
          });
          return;
          break;
      }
    } catch (error) {
      handleRequestError({
        req: request,
        error,
        res: response,
        action: "stripe webhook",
      });
      return;
    }

    // DO NOT DELETE -- RESPONSIBLE FOR SENDING SUCCESSFUL RESPONSE TO STRIPE...
    response.status(200).send();
  },
);
