import { type ActionFunction } from "@remix-run/node";
import { badRequest, notFound } from "remix-utils";
import { z } from "zod";

import db from "~/utils/db.server";
import logger from "~/utils/logger.server";
import { validate } from "~/utils/validation.server";
import { requireLoggedIn } from "~/utils/auth.server";

const action: ActionFunction = async ({ request }) => {
	const formData = await request.clone().formData();
	const action = formData.get("_action");
	if (!action) {
		const errorMessage = "POST /notifications-subscription without any _action";
		logger.error(errorMessage);
		return badRequest({ errorMessage });
	}

	switch (action as Action) {
		case "subscribe":
			return subscribe(request);
		case "unsubscribe":
			return unsubscribe(request);
		default:
			const errorMessage = `POST /notifications-subscription with an invalid _action=${action}`;
			logger.error(errorMessage);
			return badRequest({ errorMessage });
	}
};

export default action;

async function subscribe(request: Request) {
	const { organization } = await requireLoggedIn(request);
	const formData = await request.formData();
	const body = {
		subscription: JSON.parse(formData.get("subscription")?.toString() ?? "{}"),
	};
	const validation = validate(validations.subscribe, body);
	if (validation.errors) {
		return badRequest(validation.errors);
	}

	const { subscription } = validation.data;
	const membership = await db.membership.findFirst({
		where: { id: organization.membershipId },
	});
	if (!membership) {
		return notFound("Phone number not found");
	}

	try {
		await db.notificationSubscription.create({
			data: {
				membershipId: membership.id,
				endpoint: subscription.endpoint,
				expirationTime: subscription.expirationTime,
				keys_p256dh: subscription.keys.p256dh,
				keys_auth: subscription.keys.auth,
			},
		});
	} catch (error: any) {
		if (error.code !== "P2002") {
			throw error;
		}

		logger.warn(`Duplicate insertion of subscription with endpoint=${subscription.endpoint}`);
	}

	return null;
}

async function unsubscribe(request: Request) {
	const formData = await request.formData();
	const body = {
		subscriptionEndpoint: formData.get("subscriptionEndpoint"),
	};
	const validation = validate(validations.unsubscribe, body);
	if (validation.errors) {
		return badRequest(validation.errors);
	}

	const endpoint = validation.data.subscriptionEndpoint;
	try {
		await db.notificationSubscription.delete({ where: { endpoint } });
	} catch (error: any) {
		if (error.code !== "P2025") {
			throw error;
		}

		logger.warn(`Could not delete subscription with endpoint=${endpoint} because it has already been deleted`);
	}

	return null;
}

type Action = "subscribe" | "unsubscribe";

const validations = {
	subscribe: z.object({
		subscription: z.object({
			endpoint: z.string(),
			expirationTime: z.number().nullable(),
			keys: z.object({
				p256dh: z.string(),
				auth: z.string(),
			}),
		}),
	}),
	unsubscribe: z.object({
		subscriptionEndpoint: z.string(),
	}),
} as const;