import type { ActionFunctionArgs } from "react-router";
import { ASSOCIATION } from "~/features/associations/associations-constants";
import { associationsPageActionSchema } from "~/features/associations/associations-schemas";
import { requireUser } from "~/features/auth/core/user.server";
import { requirePermission } from "~/modules/permissions/guards.server";
import {
	badRequestIfFalsy,
	errorToastIfFalsy,
	parseRequestPayload,
	successToast,
} from "~/utils/remix.server";
import { assertUnreachable } from "~/utils/types";
import * as AssociationRepository from "../AssociationRepository.server";

export const action = async ({ request }: ActionFunctionArgs) => {
	const user = requireUser();
	const data = await parseRequestPayload({
		request,
		schema: associationsPageActionSchema,
	});

	switch (data._action) {
		case "REMOVE_MEMBER": {
			await validateHasManagePermissions(data.associationId);

			errorToastIfFalsy(
				data.userId !== user.id,
				"Cannot remove yourself from the association",
			);

			await AssociationRepository.removeMember({
				userId: data.userId,
				associationId: data.associationId,
			});

			break;
		}
		case "DELETE_ASSOCIATION": {
			await validateHasManagePermissions(data.associationId);

			await AssociationRepository.del(data.associationId);

			break;
		}
		case "REFRESH_INVITE_CODE": {
			await validateHasManagePermissions(data.associationId);

			await AssociationRepository.refreshInviteCode(data.associationId);

			return successToast("Invite code reset");
		}
		case "JOIN_ASSOCIATION": {
			const associationToJoin = badRequestIfFalsy(
				await AssociationRepository.findByInviteCode(data.inviteCode, {
					withMembers: true,
				}),
			);
			errorToastIfFalsy(
				associationToJoin.members?.every((member) => member.id !== user.id),
				"You are already a member of this association",
			);
			errorToastIfFalsy(
				associationToJoin.members!.length <
					ASSOCIATION.MAX_ASSOCIATION_MEMBER_COUNT,
				"Association is full",
			);

			const maxAssociationCount = user.roles.includes("SUPPORTER")
				? ASSOCIATION.MAX_COUNT_SUPPORTER
				: ASSOCIATION.MAX_COUNT_REGULAR_USER;

			errorToastIfFalsy(
				(await AssociationRepository.findByMemberUserId(user.id)).actual
					.length < maxAssociationCount,
				`Regular users can only be a member of ${maxAssociationCount} associations (supporters ${ASSOCIATION.MAX_COUNT_SUPPORTER})`,
			);

			await AssociationRepository.addMember({
				userId: user.id,
				associationId: associationToJoin.id,
			});

			break;
		}
		case "LEAVE_ASSOCIATION": {
			const association = badRequestIfFalsy(
				await AssociationRepository.findById(data.associationId, {
					withMembers: true,
				}),
			);

			errorToastIfFalsy(
				!association.permissions.MANAGE.includes(user.id),
				"You cannot leave an association you manage",
			);

			await AssociationRepository.removeMember({
				userId: user.id,
				associationId: data.associationId,
			});

			return successToast("Left association");
		}
		default: {
			assertUnreachable(data);
		}
	}

	return null;
};

async function validateHasManagePermissions(associationId: number) {
	const association = badRequestIfFalsy(
		await AssociationRepository.findById(associationId, { withMembers: true }),
	);

	requirePermission(association, "MANAGE");
}
