import type { NextApiRequest, NextApiResponse } from "next";
import Stripe from "stripe";
import { env } from "~/env";
import type { PaymentsId } from "~/schemas/public/Payments";
import { appRouter } from "~/server/api/root";
import { createTRPCContext, t } from "~/server/api/trpc";

const handler = async (
	req: NextApiRequest,
	res: NextApiResponse,
): Promise<void> => {
	const stripe = new Stripe(env.STRIPE_SECRET_KEY);

	if (req.method === "POST") {
		const ctx = await createTRPCContext({ req: req, res: res });
		const createCaller = t.createCallerFactory(appRouter);
		const caller = createCaller(ctx);

		const sig = req.headers["stripe-signature"];

		let event: Stripe.Event;
		if (!sig) {
			res.status(400).send("Webhook Error: Missing Stripe signature");
			return;
		}
		try {
			const body = await buffer(req);
			event = stripe.webhooks.constructEvent(
				body,
				sig,
				env.STRIPE_WEBHOOK_SECRET,
			);
		} catch (err) {
			console.error("Error verifying webhook signature:", err);
			res.status(400).send(`Webhook Error: ${(err as Error).message}`);
			return;
		}

		// Handle the event
		switch (event.type) {
			case "payment_intent.created": {
				const paymentIntent = event.data.object;
				const { id, metadata } = paymentIntent;

				if (!metadata) {
					res.status(400).send("No metadata in payment intent");
					return;
				}
				if (!metadata.paymentId || !(typeof metadata.paymentId === "string")) {
					res.status(400).send("No orderId or userId in metadata");
					return;
				}

				await caller.stripe.whIntentCreated({
					paymentId: metadata.paymentId as PaymentsId,
					pIntentId: id,
				});

				break;
			}
			case "payment_intent.succeeded": {
				const paymentIntent = event.data.object;
				const { id, metadata } = paymentIntent;

				if (!metadata) {
					res.status(400).send("No metadata in payment intent");
					return;
				}
				if (!metadata.paymentId || !(typeof metadata.paymentId === "string")) {
					res.status(400).send("No orderId or userId in metadata");
					return;
				}
				await caller.stripe.whIntentSucceeded({
					paymentId: metadata.paymentId as PaymentsId,
					pIntentId: id,
					pm_id:
						typeof paymentIntent.payment_method === "string"
							? paymentIntent.payment_method
							: "",
				});

				break;
			}
			case "payment_intent.canceled": {
				const paymentIntent = event.data.object;
				await caller.stripe.whIntentFailed({
					pIntentId: paymentIntent.id,
				});

				break;
			}
			case "payment_intent.payment_failed": {
				const paymentIntent = event.data.object;
				await caller.stripe.whIntentFailed({
					pIntentId: paymentIntent.id,
				});
				break;
			}
			case "payment_intent.processing": {
				const paymentIntent = event.data.object;

				console.log("PaymentIntent processing", paymentIntent);

				break;
			}

			default:
				console.log(`Unhandled event type: ${event.type}`);
		}

		res.status(200).json({ received: true });
	} else {
		res.setHeader("Allow", "POST");
		res.status(405).end("Method Not Allowed");
	}
};

export const config = {
	api: {
		bodyParser: false,
	},
};

const buffer = (req: NextApiRequest) => {
	return new Promise<Buffer>((resolve, reject) => {
		const chunks: Buffer[] = [];

		req.on("data", (chunk: Buffer) => {
			chunks.push(chunk);
		});

		req.on("end", () => {
			resolve(Buffer.concat(chunks));
		});

		req.on("error", reject);
	});
};

export default handler;
