// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type {NextApiRequest, NextApiResponse} from "next";
import {app} from "@/firebase";
import {getFirestore, setDoc, doc, query, collection, where, getDocs, getDoc, deleteDoc} from "firebase/firestore";
import {withIronSessionApiRoute} from "iron-session/next";
import {sessionOptions} from "@/lib/session";
import {Code, Group, Type} from "@/interfaces/user";
import {PERMISSIONS} from "@/constants/userPermissions";
import {uuidv4} from "@firebase/util";
import {prepareMailer, prepareMailOptions} from "@/email";

const db = getFirestore(app);

export default withIronSessionApiRoute(handler, sessionOptions);

async function handler(req: NextApiRequest, res: NextApiResponse) {
	if (req.method === "GET") return get(req, res);
	if (req.method === "POST") return post(req, res);
	if (req.method === "DELETE") return del(req, res);

	return res.status(404).json({ok: false});
}

async function get(req: NextApiRequest, res: NextApiResponse) {
	if (!req.session.user) {
		res.status(401).json({ok: false, reason: "You must be logged in to generate a code!"});
		return;
	}

	const {creator} = req.query as {creator?: string};
	const q = query(collection(db, "codes"), where("creator", "==", creator || ""));
	const snapshot = await getDocs(creator ? q : collection(db, "codes"));

	res.status(200).json(snapshot.docs.map((doc) => doc.data()));
}

async function post(req: NextApiRequest, res: NextApiResponse) {
	if (!req.session.user) {
		res.status(401).json({ok: false, reason: "You must be logged in to generate a code!"});
		return;
	}

	const {type, codes, infos, expiryDate} = req.body as {
		type: Type;
		codes: string[];
		infos?: {email: string; name: string; passport_id?: string}[];
		expiryDate: null | Date;
	};
	const permission = PERMISSIONS.generateCode[type];

	if (!permission.includes(req.session.user.type)) {
		res.status(403).json({
			ok: false,
			reason: "Your account type does not have permissions to generate a code for that type of user!",
		});
		return;
	}

	const codesGeneratedByUserSnapshot = await getDocs(query(collection(db, "codes"), where("creator", "==", req.session.user.id)));
	const creatorGroupsSnapshot = await getDocs(query(collection(db, "groups"), where("admin", "==", req.session.user.id)));

	const creatorGroups = (
		creatorGroupsSnapshot.docs.map((x) => ({
			...x.data(),
		})) as Group[]
	).filter((x) => x.name === "Students" || x.name === "Teachers" || x.name === "Corporate");

	const usersInGroups = creatorGroups.flatMap((x) => x.participants);
	const userCodes = codesGeneratedByUserSnapshot.docs.map((x) => ({
		...x.data(),
	})) as Code[];

	if (req.session.user.type === "corporate") {
		const totalCodes = userCodes.filter((x) => !x.userId || !usersInGroups.includes(x.userId)).length + usersInGroups.length + codes.length;
		const allowedCodes = req.session.user.corporateInformation?.companyInformation.userAmount || 0;

		if (totalCodes > allowedCodes) {
			res.status(403).json({
				ok: false,
				reason: `You have or would have exceeded your amount of allowed codes, you currently are allowed to generate ${
					allowedCodes - codesGeneratedByUserSnapshot.docs.length
				} codes.`,
			});
			return;
		}
	}

	const codePromises = codes.map(async (code, index) => {
		const codeRef = doc(db, "codes", code);
		let codeInformation = {
			type,
			code,
			creator: req.session.user!.id,
			creationDate: new Date().toISOString(),
			expiryDate,
		};

		if (infos && infos.length > index) {
			const {email, name, passport_id} = infos[index];
			const previousCode = userCodes.find((x) => x.email === email) as Code;

			const transport = prepareMailer();
			const mailOptions = prepareMailOptions(
				{
					type,
					code: previousCode ? previousCode.code : code,
					environment: process.env.ENVIRONMENT,
				},
				[email.toLowerCase().trim()],
				"EnCoach Registration",
				"main",
			);

			try {
				await transport.sendMail(mailOptions);

				if (!previousCode) {
					await setDoc(
						codeRef,
						{
							...codeInformation,
							email: email.trim().toLowerCase(),
							name: name.trim(),
							...(passport_id ? {passport_id: passport_id.trim()} : {}),
						},
						{merge: true},
					);
				}

				return true;
			} catch (e) {
				return false;
			}
		} else {
			await setDoc(codeRef, codeInformation);
		}
	});

	Promise.all(codePromises).then((results) => {
		res.status(200).json({ok: true, valid: results.filter((x) => x).length});
	});
}

async function del(req: NextApiRequest, res: NextApiResponse) {
	if (!req.session.user) {
		res.status(401).json({ok: false, reason: "You must be logged in to generate a code!"});
		return;
	}

	const codes = req.query.code as string[];

	for (const code of codes) {
		const snapshot = await getDoc(doc(db, "codes", code as string));
		if (!snapshot.exists()) continue;

		await deleteDoc(snapshot.ref);
	}

	res.status(200).json({codes});
}
