import { config } from "@/lib/config";
import { db } from "@/lib/db";
import { orders } from "@/schema/orders";
import { products } from "@/schema/products";
import { stripeWebhooks } from "@/schema/stripe-webhooks";
import { subscriptions } from "@/schema/subscriptions";
import { users } from "@/schema/users";
import { and, eq } from "drizzle-orm";

const stripe = require("stripe")(config.STRIPE_SECRET_KEY);

const endpointSecret = config.STRIPE_WEBHOOK_SECRET;

export async function POST(req: Request) {
  const payload = await req.text();

  await db.insert(stripeWebhooks).values({
    payload: payload,
  });

  const sig = req.headers.get("stripe-signature");

  let event;

  try {
    event = stripe.webhooks.constructEvent(payload, sig, endpointSecret);
  } catch (err: any) {
    console.error(err);
    return Response.json(
      { err: `webhook error: ${err.message}` },
      { status: 400 }
    );
  }

  switch (event.type) {
    case "checkout.session.completed":
      await handleCheckoutSessionCompleted(event);
      break;
    case "invoice.paid":
      await handleInvoicePaid(event);
      break;
    case "invoice.payment_failed":
      await handleInvoicePaymentFailed(event);
      break;
    default:
    // Unhandled event type
  }

  return Response.json({ message: "success" });
}

async function handleCheckoutSessionCompleted(event: any) {
  const email = event.data.object.customer_email;

  const user = await db.query.users.findFirst({
    where: eq(users.email, email),
  });

  if (!user) {
    throw new Error(
      "user not found during handle checkout session completed: " + email
    );
  }

  // process dynamic order
  const orderId = event.data.object.metadata.order_id;
  if (orderId) {
    await db.insert(orders).values({
      id: orderId,
      amountTotal: event.data.object.amount_total,
      userId: user.id,
      note: "a dynamic order",
    });
    return;
  }

  // process one-time purchase
  const sessionWithLineItems = await stripe.checkout.sessions.retrieve(
    event.data.object.id,
    {
      expand: ["line_items"],
    }
  );
  const lineItems = sessionWithLineItems.line_items;

  for (const item of lineItems.data) {
    const stripeProductId = item.price.product;
    const amountTotal = item.amount_total;

    const product = await db.query.products.findFirst({
      where: eq(products.stripeProductId, stripeProductId),
    });

    if (product && product.slug === "one-time") {
      await db.insert(orders).values({
        productId: product.id,
        amountTotal: amountTotal,
        userId: user.id,
        note: "a one time purchase",
      });
    }
  }
}

async function handleInvoicePaid(event: any) {
  const email = event.data.object.customer_email;

  const user = await db.query.users.findFirst({
    where: eq(users.email, email),
  });

  if (!user) {
    throw new Error("user not found during invoice paid: " + email);
  }

  const data = event.data.object.lines.data;
  const total = event.data.object.total;
  const stripeCustomerId = event.data.object.customer;

  for (const d of data) {
    const stripeProductId = d.price.product;
    const product = await db.query.products.findFirst({
      where: eq(products.stripeProductId, stripeProductId),
    });
    if (product?.slug === "monthly") {
      const startDate = new Date(d.period.start * 1000);
      const endDate = new Date(d.period.end * 1000);
      const newSubscription = {
        userId: user.id,
        stripeCustomerId: stripeCustomerId,
        productId: product.id,
        startDate: startDate,
        endDate: endDate,
      };

      const subscription = await db.query.subscriptions.findFirst({
        where: and(eq(users.id, user.id), eq(products.id, product.id)),
      });

      if (subscription) {
        await db
          .update(subscriptions)
          .set({
            endDate: endDate,
            updatedAt: new Date(),
          })
          .where(eq(subscriptions.id, subscription.id));
      } else {
        await db.insert(subscriptions).values(newSubscription);
      }

      await db.insert(orders).values({
        productId: product.id,
        amountTotal: total,
        userId: user.id,
        note: "a subscription order",
      });
    }
  }
}

async function handleInvoicePaymentFailed(event: any) {
  console.log("handle invoice payment failed");
  console.log(event);
}
