import type { NextApiRequest, NextApiResponse } from "next";
import type Stripe from "stripe";
import { env } from "~/env";
import stripe from "~/lib/stripe";
import { appRouter } from "~/server/api/root";
import { addApiAuthToContext, createTRPCContext, t } from "~/server/api/trpc";
import { zOrdineId } from "~/server/utils/zod_types";

/**
 * /api/stripe-webhook
 * Endpoint per gestire i webhook di Stripe. Gestisce eventi come la creazione, il successo, l'annullamento e il fallimento dei payment intent.
 * Verifica la firma del webhook e chiama le funzioni appropriate nel router tRPC per aggiornare lo stato degli ordini in base agli eventi ricevuti.
 */
const handler = async (
	req: NextApiRequest,
	res: NextApiResponse,
): Promise<void> => {
	if (req.method === "POST") {
		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;
		}
		const ctx = await createTRPCContext({ req: req, res: res });
		addApiAuthToContext(ctx);
		const createCaller = t.createCallerFactory(appRouter);
		const caller = createCaller(ctx);

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

					if (!metadata) {
						throw new Error("No metadata in payment intent");
					}
					const metadataOrdineId = metadata.ordineId;
					const ordineId = zOrdineId.safeParse(metadataOrdineId);
					if (!metadata.ordineId || !ordineId.success) {
						throw new Error("No ordineId or userId in metadata");
					}

					await caller.stripe.whIntentCreated({
						ordineId: ordineId.data,
						pIntentId: id,
					});

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

					if (!metadata) {
						throw new Error("No metadata in payment intent");
					}
					const metadataOrdineId = metadata.ordineId;
					const ordineId = zOrdineId.safeParse(metadataOrdineId);
					if (!metadata.ordineId || !ordineId.success) {
						throw new Error("No ordineId or userId in metadata");
					}
					await caller.stripe.whIntentSucceeded({
						ordineId: ordineId.data,
						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}`);
					break;
			}

			res.status(200).json({ received: true });
		} catch (error) {
			console.error("Error handling webhook event:", error);
			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;
