import { randomUUID } from "node:crypto";
import { db } from "@kk/db";
import { adminRequest, registration } from "@kk/db/schema";
import { user } from "@kk/db/schema/auth";
import { drinkkaart, drinkkaartTopup } from "@kk/db/schema/drinkkaart";
import { env } from "@kk/env/server";
import type { RouterClient } from "@orpc/server";
import { and, count, desc, eq, gte, isNull, like, lte, sum } from "drizzle-orm";
import { z } from "zod";
import {
	sendCancellationEmail,
	sendConfirmationEmail,
	sendUpdateEmail,
} from "../email";
import { adminProcedure, protectedProcedure, publicProcedure } from "../index";
import { generateQrSecret } from "../lib/drinkkaart-utils";
import { drinkkaartRouter } from "./drinkkaart";

// ---------------------------------------------------------------------------
// Shared helpers
// ---------------------------------------------------------------------------

/** Drink card price in euros: €5 base + €2 per extra guest. */
function drinkCardEuros(guestCount: number): number {
	return 5 + guestCount * 2;
}

/** Drink card price in cents for payment processing. */
function drinkCardCents(guestCount: number): number {
	return drinkCardEuros(guestCount) * 100;
}

/** Parses the stored guest JSON blob, returning an empty array on failure. */
function parseGuestsJson(raw: string | null): Array<{
	firstName: string;
	lastName: string;
	email?: string;
	phone?: string;
	birthdate?: string;
	postcode?: string;
}> {
	if (!raw) return [];
	try {
		const parsed = JSON.parse(raw);
		return Array.isArray(parsed) ? parsed : [];
	} catch {
		return [];
	}
}

/**
 * Credits a watcher's drinkCardValue to their drinkkaart account.
 *
 * Safe to call multiple times — the `drinkkaartCreditedAt IS NULL` guard and the
 * `lemonsqueezy_order_id` UNIQUE constraint together prevent double-crediting even
 * if called concurrently (webhook + signup racing each other).
 *
 * Returns a status string describing the outcome.
 */
export async function creditRegistrationToAccount(
	email: string,
	userId: string,
): Promise<{
	credited: boolean;
	amountCents: number;
	status:
		| "credited"
		| "already_credited"
		| "no_paid_registration"
		| "zero_value";
}> {
	// Find the most recent paid watcher registration for this email that hasn't
	// been credited yet and has a non-zero drink card value.
	const rows = await db
		.select()
		.from(registration)
		.where(
			and(
				eq(registration.email, email),
				eq(registration.registrationType, "watcher"),
				eq(registration.paymentStatus, "paid"),
				isNull(registration.cancelledAt),
				isNull(registration.drinkkaartCreditedAt),
			),
		)
		.orderBy(desc(registration.createdAt))
		.limit(1);

	const reg = rows[0];

	if (!reg) {
		// Either no paid registration exists, or it was already credited.
		// Distinguish between the two for better logging.
		const credited = await db
			.select({ id: registration.id })
			.from(registration)
			.where(
				and(
					eq(registration.email, email),
					eq(registration.registrationType, "watcher"),
					eq(registration.paymentStatus, "paid"),
					isNull(registration.cancelledAt),
				),
			)
			.limit(1)
			.then((r) => r[0]);

		return {
			credited: false,
			amountCents: 0,
			status: credited ? "already_credited" : "no_paid_registration",
		};
	}

	const drinkCardValueEuros = reg.drinkCardValue ?? 0;
	if (drinkCardValueEuros === 0) {
		return { credited: false, amountCents: 0, status: "zero_value" };
	}

	const amountCents = drinkCardValueEuros * 100;

	// Get or create the drinkkaart for this user.
	let card = await db
		.select()
		.from(drinkkaart)
		.where(eq(drinkkaart.userId, userId))
		.limit(1)
		.then((r) => r[0]);

	if (!card) {
		const now = new Date();
		card = {
			id: randomUUID(),
			userId,
			balance: 0,
			version: 0,
			qrSecret: generateQrSecret(),
			createdAt: now,
			updatedAt: now,
		};
		await db.insert(drinkkaart).values(card);
	}

	const balanceBefore = card.balance;
	const balanceAfter = balanceBefore + amountCents;

	// Optimistic-lock update — if concurrent, the second caller will see
	// drinkkaartCreditedAt IS NOT NULL and return "already_credited" above.
	const updateResult = await db
		.update(drinkkaart)
		.set({
			balance: balanceAfter,
			version: card.version + 1,
			updatedAt: new Date(),
		})
		.where(
			and(eq(drinkkaart.id, card.id), eq(drinkkaart.version, card.version)),
		);

	if (updateResult.rowsAffected === 0) {
		// Lost the optimistic lock race — the other caller will handle it.
		return { credited: false, amountCents: 0, status: "already_credited" };
	}

	// Record the topup. Re-use the registration's lemonsqueezyOrderId so the
	// topup row is clearly linked to the original payment. We prefix with
	// "reg_" to distinguish from direct drinkkaart top-up orders if needed.
	const topupPaymentId = reg.lemonsqueezyOrderId
		? `reg_${reg.lemonsqueezyOrderId}`
		: null;

	await db.insert(drinkkaartTopup).values({
		id: randomUUID(),
		drinkkaartId: card.id,
		userId,
		amountCents,
		balanceBefore,
		balanceAfter,
		type: "payment",
		lemonsqueezyOrderId: topupPaymentId,
		adminId: null,
		reason: "Drinkkaart bij registratie",
		paidAt: reg.paidAt ?? new Date(),
	});

	// Mark the registration as credited — prevents any future double-credit.
	await db
		.update(registration)
		.set({ drinkkaartCreditedAt: new Date() })
		.where(eq(registration.id, reg.id));

	return { credited: true, amountCents, status: "credited" };
}

/** Fetches a single registration by management token, throws if not found or cancelled. */
async function getActiveRegistration(token: string) {
	const rows = await db
		.select()
		.from(registration)
		.where(
			and(
				eq(registration.managementToken, token),
				isNull(registration.cancelledAt),
			),
		)
		.limit(1);
	const row = rows[0];
	if (!row) throw new Error("Inschrijving niet gevonden of al geannuleerd");
	return row;
}

// ---------------------------------------------------------------------------
// Schemas
// ---------------------------------------------------------------------------

const registrationTypeSchema = z.enum(["performer", "watcher"]);

const guestSchema = z.object({
	firstName: z.string().min(1),
	lastName: z.string().min(1),
	email: z.string().email().optional().or(z.literal("")),
	phone: z.string().optional(),
	birthdate: z.string().min(1),
	postcode: z.string().min(1),
});

const coreRegistrationFields = {
	firstName: z.string().min(1),
	lastName: z.string().min(1),
	email: z.string().email(),
	phone: z.string().optional(),
	postcode: z.string().min(1),
	birthdate: z.string().min(1),
	registrationType: registrationTypeSchema.default("watcher"),
	artForm: z.string().optional(),
	experience: z.string().optional(),
	isOver16: z.boolean().optional(),
	guests: z.array(guestSchema).max(9).optional(),
	extraQuestions: z.string().optional(),
	giftAmount: z.number().int().min(0).default(0),
};

const submitRegistrationSchema = z.object(coreRegistrationFields);

const updateRegistrationSchema = z.object({
	token: z.string().uuid(),
	...coreRegistrationFields,
});

const getRegistrationsSchema = z.object({
	search: z.string().optional(),
	registrationType: registrationTypeSchema.optional(),
	artForm: z.string().optional(),
	fromDate: z.string().datetime().optional(),
	toDate: z.string().datetime().optional(),
	page: z.number().int().min(1).default(1),
	pageSize: z.number().int().min(1).max(100).default(50),
});

// ---------------------------------------------------------------------------
// Router
// ---------------------------------------------------------------------------

export const appRouter = {
	healthCheck: publicProcedure.handler(() => "OK"),
	drinkkaart: drinkkaartRouter,

	privateData: protectedProcedure.handler(({ context }) => ({
		message: "This is private",
		user: context.session?.user,
	})),

	submitRegistration: publicProcedure
		.input(submitRegistrationSchema)
		.handler(async ({ input }) => {
			const managementToken = randomUUID();
			const isPerformer = input.registrationType === "performer";
			const guests = isPerformer ? [] : (input.guests ?? []);

			await db.insert(registration).values({
				id: randomUUID(),
				firstName: input.firstName,
				lastName: input.lastName,
				email: input.email,
				phone: input.phone || null,
				postcode: input.postcode,
				birthdate: input.birthdate,
				registrationType: input.registrationType,
				artForm: isPerformer ? input.artForm || null : null,
				experience: isPerformer ? input.experience || null : null,
				isOver16: isPerformer ? (input.isOver16 ?? false) : false,
				drinkCardValue: isPerformer ? 0 : drinkCardEuros(guests.length),
				guests: guests.length > 0 ? JSON.stringify(guests) : null,
				extraQuestions: input.extraQuestions || null,
				giftAmount: input.giftAmount,
				managementToken,
			});

			await sendConfirmationEmail({
				to: input.email,
				firstName: input.firstName,
				managementToken,
				wantsToPerform: isPerformer,
				artForm: input.artForm,
				giftAmount: input.giftAmount,
				drinkCardValue: isPerformer ? 0 : drinkCardEuros(guests.length),
			}).catch((err) =>
				console.error("Failed to send confirmation email:", err),
			);

			return { success: true, managementToken };
		}),

	getRegistrationByToken: publicProcedure
		.input(z.object({ token: z.string().uuid() }))
		.handler(async ({ input }) => {
			const rows = await db
				.select()
				.from(registration)
				.where(eq(registration.managementToken, input.token))
				.limit(1);

			const row = rows[0];
			if (!row) throw new Error("Inschrijving niet gevonden");
			if (row.cancelledAt) throw new Error("Deze inschrijving is geannuleerd");

			return row;
		}),

	updateRegistration: publicProcedure
		.input(updateRegistrationSchema)
		.handler(async ({ input }) => {
			const row = await getActiveRegistration(input.token);

			const isPerformer = input.registrationType === "performer";
			const guests = isPerformer ? [] : (input.guests ?? []);

			// Validate gift amount cannot be changed after payment
			const hasPaidGift =
				(row.paymentStatus === "paid" ||
					row.paymentStatus === "extra_payment_pending") &&
				(row.giftAmount ?? 0) > 0;
			if (hasPaidGift && input.giftAmount !== row.giftAmount) {
				throw new Error(
					"Gift kan niet worden aangepast na betaling. Neem contact op met de organisatie.",
				);
			}

			// Detect whether an already-paid watcher is adding extra guests so we
			// can charge them the delta instead of the full amount.
			const isPaid = row.paymentStatus === "paid";
			const oldGuestCount = isPerformer
				? 0
				: parseGuestsJson(row.guests).length;
			const newGuestCount = guests.length;
			const extraGuests =
				!isPerformer && isPaid ? newGuestCount - oldGuestCount : 0;

			// Determine the new paymentStatus and paymentAmount (delta in cents).
			// Only flag extra_payment_pending when the watcher genuinely owes more.
			const newPaymentStatus =
				isPaid && extraGuests > 0 ? "extra_payment_pending" : row.paymentStatus;
			const newPaymentAmount =
				newPaymentStatus === "extra_payment_pending"
					? extraGuests * 2 * 100 // €2 per extra guest, in cents
					: row.paymentAmount;

			await db
				.update(registration)
				.set({
					firstName: input.firstName,
					lastName: input.lastName,
					email: input.email,
					phone: input.phone || null,
					postcode: input.postcode,
					birthdate: input.birthdate,
					registrationType: input.registrationType,
					artForm: isPerformer ? input.artForm || null : null,
					experience: isPerformer ? input.experience || null : null,
					isOver16: isPerformer ? (input.isOver16 ?? false) : false,
					drinkCardValue: isPerformer ? 0 : drinkCardEuros(guests.length),
					guests: guests.length > 0 ? JSON.stringify(guests) : null,
					extraQuestions: input.extraQuestions || null,
					giftAmount: input.giftAmount,
					paymentStatus: newPaymentStatus,
					paymentAmount: newPaymentAmount,
				})
				.where(eq(registration.managementToken, input.token));

			await sendUpdateEmail({
				to: input.email,
				firstName: input.firstName,
				managementToken: input.token,
				wantsToPerform: isPerformer,
				artForm: input.artForm,
				giftAmount: input.giftAmount,
				drinkCardValue: isPerformer ? 0 : drinkCardEuros(guests.length),
			}).catch((err) => console.error("Failed to send update email:", err));

			return { success: true };
		}),

	cancelRegistration: publicProcedure
		.input(z.object({ token: z.string().uuid() }))
		.handler(async ({ input }) => {
			const row = await getActiveRegistration(input.token);

			await db
				.update(registration)
				.set({ cancelledAt: new Date() })
				.where(eq(registration.managementToken, input.token));

			await sendCancellationEmail({
				to: row.email,
				firstName: row.firstName,
			}).catch((err) =>
				console.error("Failed to send cancellation email:", err),
			);

			return { success: true };
		}),

	getRegistrations: adminProcedure
		.input(getRegistrationsSchema)
		.handler(async ({ input }) => {
			const conditions = [];

			if (input.search) {
				const term = `%${input.search}%`;
				conditions.push(
					and(
						like(registration.firstName, term),
						like(registration.lastName, term),
						like(registration.email, term),
					),
				);
			}
			if (input.registrationType)
				conditions.push(
					eq(registration.registrationType, input.registrationType),
				);
			if (input.artForm)
				conditions.push(eq(registration.artForm, input.artForm));
			if (input.fromDate)
				conditions.push(gte(registration.createdAt, new Date(input.fromDate)));
			if (input.toDate)
				conditions.push(lte(registration.createdAt, new Date(input.toDate)));

			const where = conditions.length > 0 ? and(...conditions) : undefined;

			const [data, countResult] = await Promise.all([
				db
					.select()
					.from(registration)
					.where(where)
					.orderBy(desc(registration.createdAt))
					.limit(input.pageSize)
					.offset((input.page - 1) * input.pageSize),
				db.select({ count: count() }).from(registration).where(where),
			]);

			const total = countResult[0]?.count ?? 0;

			return {
				data,
				pagination: {
					page: input.page,
					pageSize: input.pageSize,
					total,
					totalPages: Math.ceil(total / input.pageSize),
				},
			};
		}),

	getRegistrationStats: adminProcedure.handler(async () => {
		const today = new Date();
		today.setHours(0, 0, 0, 0);

		const [totalResult, todayResult, artFormResult, typeResult, giftResult] =
			await Promise.all([
				db.select({ count: count() }).from(registration),
				db
					.select({ count: count() })
					.from(registration)
					.where(gte(registration.createdAt, today)),
				db
					.select({ artForm: registration.artForm, count: count() })
					.from(registration)
					.where(eq(registration.registrationType, "performer"))
					.groupBy(registration.artForm),
				db
					.select({
						registrationType: registration.registrationType,
						count: count(),
					})
					.from(registration)
					.groupBy(registration.registrationType),
				db.select({ total: sum(registration.giftAmount) }).from(registration),
			]);

		return {
			total: totalResult[0]?.count ?? 0,
			today: todayResult[0]?.count ?? 0,
			byArtForm: artFormResult.map((r) => ({
				artForm: r.artForm,
				count: r.count,
			})),
			byType: typeResult.map((r) => ({
				registrationType: r.registrationType,
				count: r.count,
			})),
			totalGiftRevenue: giftResult[0]?.total ?? 0,
		};
	}),

	exportRegistrations: adminProcedure.handler(async () => {
		const data = await db
			.select()
			.from(registration)
			.orderBy(desc(registration.createdAt));

		const headers = [
			"ID",
			"First Name",
			"Last Name",
			"Email",
			"Phone",
			"Postcode",
			"Birthdate",
			"Type",
			"Art Form",
			"Experience",
			"Is Over 16",
			"Drink Card Value",
			"Gift Amount",
			"Guest Count",
			"Guests",
			"Payment Status",
			"Paid At",
			"Extra Questions",
			"Created At",
		];

		const rows = data.map((r) => {
			const guests = parseGuestsJson(r.guests);
			const guestSummary = guests
				.map(
					(g) =>
						`${g.firstName} ${g.lastName}${g.birthdate ? ` (${g.birthdate})` : ""}${g.postcode ? ` [${g.postcode}]` : ""}${g.email ? ` <${g.email}>` : ""}${g.phone ? ` (${g.phone})` : ""}`,
				)
				.join(" | ");
			return [
				r.id,
				r.firstName,
				r.lastName,
				r.email,
				r.phone || "",
				r.postcode || "",
				r.birthdate || "",
				r.registrationType,
				r.artForm || "",
				r.experience || "",
				r.isOver16 ? "Yes" : "No",
				String(r.drinkCardValue ?? 0),
				String(r.giftAmount ?? 0),
				String(guests.length),
				guestSummary,
				r.paymentStatus === "paid" ? "Paid" : "Pending",
				r.paidAt ? r.paidAt.toISOString() : "",
				r.extraQuestions || "",
				r.createdAt.toISOString(),
			];
		});

		const csvContent = [
			headers.join(","),
			...rows.map((row) =>
				row.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(","),
			),
		].join("\n");

		return {
			csv: csvContent,
			filename: `registrations-${new Date().toISOString().split("T")[0]}.csv`,
		};
	}),

	// ---------------------------------------------------------------------------
	// User account
	// ---------------------------------------------------------------------------

	claimRegistrationCredit: protectedProcedure.handler(async ({ context }) => {
		const result = await creditRegistrationToAccount(
			context.session.user.email,
			context.session.user.id,
		);
		return result;
	}),

	getMyRegistrations: protectedProcedure.handler(async ({ context }) => {
		const email = context.session.user.email;

		const rows = await db
			.select()
			.from(registration)
			.where(
				and(eq(registration.email, email), isNull(registration.cancelledAt)),
			)
			.orderBy(desc(registration.createdAt));

		return rows.map((row) => ({
			...row,
			guests: parseGuestsJson(row.guests),
		}));
	}),

	// ---------------------------------------------------------------------------
	// Admin access requests
	// ---------------------------------------------------------------------------

	requestAdminAccess: protectedProcedure.handler(async ({ context }) => {
		const userId = context.session.user.id;

		if (context.session.user.role === "admin") {
			return { success: false, message: "Je bent al een admin" };
		}

		const existing = await db
			.select()
			.from(adminRequest)
			.where(eq(adminRequest.userId, userId))
			.limit(1)
			.then((rows) => rows[0]);

		if (existing) {
			if (existing.status === "pending")
				return {
					success: false,
					message: "Je hebt al een aanvraag openstaan",
				};
			if (existing.status === "approved")
				return {
					success: false,
					message: "Je aanvraag is al goedgekeurd, log opnieuw in",
				};
			// Rejected — allow re-request
			await db
				.update(adminRequest)
				.set({
					status: "pending",
					requestedAt: new Date(),
					reviewedAt: null,
					reviewedBy: null,
				})
				.where(eq(adminRequest.userId, userId));
			return { success: true, message: "Nieuwe aanvraag ingediend" };
		}

		await db.insert(adminRequest).values({
			id: randomUUID(),
			userId,
			status: "pending",
		});

		return { success: true, message: "Admin toegang aangevraagd" };
	}),

	getAdminRequests: adminProcedure.handler(async () => {
		return db
			.select({
				id: adminRequest.id,
				userId: adminRequest.userId,
				status: adminRequest.status,
				requestedAt: adminRequest.requestedAt,
				reviewedAt: adminRequest.reviewedAt,
				reviewedBy: adminRequest.reviewedBy,
				userName: user.name,
				userEmail: user.email,
			})
			.from(adminRequest)
			.leftJoin(user, eq(adminRequest.userId, user.id))
			.orderBy(desc(adminRequest.requestedAt));
	}),

	approveAdminRequest: adminProcedure
		.input(z.object({ requestId: z.string() }))
		.handler(async ({ input, context }) => {
			const req = await db
				.select()
				.from(adminRequest)
				.where(eq(adminRequest.id, input.requestId))
				.limit(1)
				.then((rows) => rows[0]);

			if (!req) throw new Error("Aanvraag niet gevonden");
			if (req.status !== "pending")
				throw new Error("Deze aanvraag is al behandeld");

			await db
				.update(adminRequest)
				.set({
					status: "approved",
					reviewedAt: new Date(),
					reviewedBy: context.session.user.id,
				})
				.where(eq(adminRequest.id, input.requestId));

			await db
				.update(user)
				.set({ role: "admin" })
				.where(eq(user.id, req.userId));

			return { success: true, message: "Admin toegang goedgekeurd" };
		}),

	rejectAdminRequest: adminProcedure
		.input(z.object({ requestId: z.string() }))
		.handler(async ({ input, context }) => {
			const req = await db
				.select()
				.from(adminRequest)
				.where(eq(adminRequest.id, input.requestId))
				.limit(1)
				.then((rows) => rows[0]);

			if (!req) throw new Error("Aanvraag niet gevonden");
			if (req.status !== "pending")
				throw new Error("Deze aanvraag is al behandeld");

			await db
				.update(adminRequest)
				.set({
					status: "rejected",
					reviewedAt: new Date(),
					reviewedBy: context.session.user.id,
				})
				.where(eq(adminRequest.id, input.requestId));

			return { success: true, message: "Admin toegang geweigerd" };
		}),

	// ---------------------------------------------------------------------------
	// Checkout
	// ---------------------------------------------------------------------------

	getCheckoutUrl: publicProcedure
		.input(
			z.object({
				token: z.string().uuid(),
				redirectUrl: z.string().optional(),
			}),
		)
		.handler(async ({ input }) => {
			if (
				!env.LEMON_SQUEEZY_API_KEY ||
				!env.LEMON_SQUEEZY_STORE_ID ||
				!env.LEMON_SQUEEZY_VARIANT_ID
			) {
				throw new Error("LemonSqueezy is niet geconfigureerd");
			}

			const rows = await db
				.select()
				.from(registration)
				.where(
					and(
						eq(registration.managementToken, input.token),
						isNull(registration.cancelledAt),
					),
				)
				.limit(1);

			const row = rows[0];
			if (!row) throw new Error("Inschrijving niet gevonden");
			if (row.paymentStatus === "paid")
				throw new Error("Betaling is al voltooid");
			if (
				row.paymentStatus !== "pending" &&
				row.paymentStatus !== "extra_payment_pending"
			)
				throw new Error("Onverwachte betalingsstatus");

			const isPerformer = row.registrationType === "performer";
			const guests = parseGuestsJson(row.guests);
			const giftTotal = row.giftAmount ?? 0;

			// Performers only pay if they have a gift; watchers pay drink card + gift
			if (isPerformer && giftTotal === 0) {
				throw new Error("Artiesten hoeven niet te betalen");
			}

			// For an extra_payment_pending registration, charge only the delta
			// (stored in paymentAmount in cents). For a fresh pending registration
			// charge the full drink card price. Always add any gift amount.
			const isExtraPayment = row.paymentStatus === "extra_payment_pending";
			const drinkCardTotal = isExtraPayment
				? (row.paymentAmount ?? 0)
				: isPerformer
					? 0
					: drinkCardCents(guests.length);
			const amountInCents = drinkCardTotal + giftTotal;

			const productDescription = isExtraPayment
				? `Extra bijdrage${giftTotal > 0 ? " + gift" : ""}`
				: isPerformer
					? `Vrijwillige gift${drinkCardTotal > 0 ? " + drinkkaart" : ""}`
					: `Toegangskaart${giftTotal > 0 ? " + gift" : ""}`;

			const redirectUrl =
				input.redirectUrl ?? `${env.CORS_ORIGIN}/manage/${input.token}`;

			const response = await fetch(
				"https://api.lemonsqueezy.com/v1/checkouts",
				{
					method: "POST",
					headers: {
						Accept: "application/vnd.api+json",
						"Content-Type": "application/vnd.api+json",
						Authorization: `Bearer ${env.LEMON_SQUEEZY_API_KEY}`,
					},
					body: JSON.stringify({
						data: {
							type: "checkouts",
							attributes: {
								custom_price: amountInCents,
								product_options: {
									name: "Kunstenkamp Evenement",
									description: productDescription,
									redirect_url: redirectUrl,
								},
								checkout_data: {
									email: row.email,
									name: `${row.firstName} ${row.lastName}`,
									custom: {
										registration_token: input.token,
									},
								},
								checkout_options: {
									embed: false,
									locale: "nl",
								},
							},
							relationships: {
								store: {
									data: {
										type: "stores",
										id: env.LEMON_SQUEEZY_STORE_ID,
									},
								},
								variant: {
									data: {
										type: "variants",
										id: env.LEMON_SQUEEZY_VARIANT_ID,
									},
								},
							},
						},
					}),
				},
			);

			if (!response.ok) {
				const errorData = await response.json();
				console.error("LemonSqueezy checkout error:", errorData);
				throw new Error("Kon checkout niet aanmaken");
			}

			const checkoutData = (await response.json()) as {
				data?: { attributes?: { url?: string } };
			};
			const checkoutUrl = checkoutData.data?.attributes?.url;

			if (!checkoutUrl) throw new Error("Geen checkout URL ontvangen");

			return { checkoutUrl };
		}),
};

export type AppRouter = typeof appRouter;
export type AppRouterClient = RouterClient<typeof appRouter>;
