import { createHmac, randomUUID } from "node:crypto";
import { creditRegistrationToAccount } from "@kk/api/routers/index";
import { db } from "@kk/db";
import { drinkkaart, drinkkaartTopup, registration } from "@kk/db/schema";
import { user } from "@kk/db/schema/auth";
import { env } from "@kk/env/server";
import { createFileRoute } from "@tanstack/react-router";
import { and, eq } from "drizzle-orm";

// LemonSqueezy webhook payload types (order_created event)
interface LemonSqueezyOrderCreatedPayload {
	meta: {
		event_name: string;
		custom_data?: {
			type?: string;
			registration_token?: string;
			drinkkaartId?: string;
			userId?: string;
		};
	};
	data: {
		id: string;
		attributes: {
			status: string;
			customer_id: number;
			total: number; // amount in cents
		};
	};
}

function verifyWebhookSignature(
	payload: string,
	signature: string,
	secret: string,
): boolean {
	const hmac = createHmac("sha256", secret);
	hmac.update(payload);
	const digest = hmac.digest("hex");
	return signature === digest;
}

async function handleWebhook({ request }: { request: Request }) {
	if (!env.LEMON_SQUEEZY_WEBHOOK_SECRET) {
		console.error("LEMON_SQUEEZY_WEBHOOK_SECRET not configured");
		return new Response("Payment provider not configured", { status: 500 });
	}

	const payload = await request.text();
	const signature = request.headers.get("X-Signature");

	if (!signature) {
		return new Response("Missing signature", { status: 401 });
	}

	if (
		!verifyWebhookSignature(
			payload,
			signature,
			env.LEMON_SQUEEZY_WEBHOOK_SECRET,
		)
	) {
		return new Response("Invalid signature", { status: 401 });
	}

	let event: LemonSqueezyOrderCreatedPayload;
	try {
		event = JSON.parse(payload) as LemonSqueezyOrderCreatedPayload;
	} catch {
		return new Response("Invalid JSON", { status: 400 });
	}

	// Only handle order_created events
	if (event.meta.event_name !== "order_created") {
		return new Response("Event ignored", { status: 200 });
	}

	const orderId = event.data.id;
	const customerId = String(event.data.attributes.customer_id);
	const amountCents = event.data.attributes.total;
	const customData = event.meta.custom_data;

	try {
		// -------------------------------------------------------------------------
		// Branch: Drinkkaart top-up
		// -------------------------------------------------------------------------
		if (customData?.type === "drinkkaart_topup") {
			const { drinkkaartId, userId } = customData;
			if (!drinkkaartId || !userId) {
				console.error(
					"Missing drinkkaartId or userId in drinkkaart_topup custom_data",
				);
				return new Response("Missing drinkkaart data", { status: 400 });
			}

			// Idempotency: skip if already processed
			const existing = await db
				.select({ id: drinkkaartTopup.id })
				.from(drinkkaartTopup)
				.where(eq(drinkkaartTopup.lemonsqueezyOrderId, orderId))
				.limit(1)
				.then((r) => r[0]);

			if (existing) {
				console.log(`Drinkkaart topup already processed for order ${orderId}`);
				return new Response("OK", { status: 200 });
			}

			const card = await db
				.select()
				.from(drinkkaart)
				.where(eq(drinkkaart.id, drinkkaartId))
				.limit(1)
				.then((r) => r[0]);

			if (!card) {
				console.error(`Drinkkaart not found: ${drinkkaartId}`);
				return new Response("Not found", { status: 404 });
			}

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

			// Optimistic lock update
			const result = await db
				.update(drinkkaart)
				.set({
					balance: balanceAfter,
					version: card.version + 1,
					updatedAt: new Date(),
				})
				.where(
					and(
						eq(drinkkaart.id, drinkkaartId),
						eq(drinkkaart.version, card.version),
					),
				);

			if (result.rowsAffected === 0) {
				// Return 500 so LemonSqueezy retries; idempotency check prevents double-credit
				console.error(
					`Drinkkaart optimistic lock conflict for ${drinkkaartId}`,
				);
				return new Response("Conflict — please retry", { status: 500 });
			}

			await db.insert(drinkkaartTopup).values({
				id: randomUUID(),
				drinkkaartId,
				userId,
				amountCents,
				balanceBefore,
				balanceAfter,
				type: "payment",
				lemonsqueezyOrderId: orderId,
				adminId: null,
				reason: null,
				paidAt: new Date(),
			});

			console.log(
				`Drinkkaart topup successful: drinkkaart=${drinkkaartId}, amount=${amountCents}c, order=${orderId}`,
			);

			return new Response("OK", { status: 200 });
		}

		// -------------------------------------------------------------------------
		// Branch: Registration payment
		// -------------------------------------------------------------------------
		const registrationToken = customData?.registration_token;
		if (!registrationToken) {
			console.error("No registration token in order custom_data");
			return new Response("Missing registration token", { status: 400 });
		}

		// Fetch the registration row
		const regRow = await db
			.select()
			.from(registration)
			.where(eq(registration.managementToken, registrationToken))
			.limit(1)
			.then((r) => r[0]);

		if (!regRow) {
			console.error(`Registration not found for token ${registrationToken}`);
			return new Response("Registration not found", { status: 404 });
		}

		// Mark the registration as paid
		await db
			.update(registration)
			.set({
				paymentStatus: "paid",
				paymentAmount: 0,
				lemonsqueezyOrderId: orderId,
				lemonsqueezyCustomerId: customerId,
				paidAt: new Date(),
			})
			.where(eq(registration.managementToken, registrationToken));

		console.log(
			`Payment successful for registration ${registrationToken}, order ${orderId}`,
		);

		// If this is a watcher with a drink card value, try to credit their
		// drinkkaart immediately — but only if they already have an account.
		if (
			regRow.registrationType === "watcher" &&
			(regRow.drinkCardValue ?? 0) > 0
		) {
			const accountUser = await db
				.select({ id: user.id })
				.from(user)
				.where(eq(user.email, regRow.email))
				.limit(1)
				.then((r) => r[0]);

			if (accountUser) {
				const creditResult = await creditRegistrationToAccount(
					regRow.email,
					accountUser.id,
				).catch((err) => {
					console.error(
						`Failed to credit drinkkaart for ${regRow.email} after registration payment:`,
						err,
					);
					return null;
				});

				if (creditResult?.credited) {
					console.log(
						`Drinkkaart credited ${creditResult.amountCents}c for user ${accountUser.id} (registration ${registrationToken})`,
					);
				} else if (creditResult) {
					console.log(
						`Drinkkaart credit skipped for ${regRow.email}: ${creditResult.status}`,
					);
				}
			} else {
				console.log(
					`No account for ${regRow.email} — drinkkaart credit deferred until signup`,
				);
			}
		}

		return new Response("OK", { status: 200 });
	} catch (error) {
		console.error("Webhook processing error:", error);
		return new Response("Internal error", { status: 500 });
	}
}

export const Route = createFileRoute("/api/webhook/lemonsqueezy")({
	server: {
		handlers: {
			POST: handleWebhook,
		},
	},
});
