import {CorporateUser, Type, User} from "@/interfaces/user";
import {getGroupsForUser, getParticipantGroups, getUserGroups, getUsersGroups} from "./groups.be";
import {uniq} from "lodash";
import {getUserCodes} from "./codes.be";
import client from "@/lib/mongodb";
import {WithEntity} from "@/interfaces/entity";
import {getEntity} from "./entities.be";
import {getRole} from "./roles.be";

const db = client.db(process.env.MONGODB_DB);

export async function getUsers() {
	return await db
		.collection("users")
		.find<User>({}, {projection: {_id: 0}})
		.toArray();
}

export async function getUserWithEntity(id: string): Promise<WithEntity<User> | undefined> {
	const user = await db.collection("users").findOne<User>({id: id}, {projection: {_id: 0}});
	if (!user) return undefined;

	const entities = await Promise.all(
		user.entities.map(async (e) => {
			const entity = await getEntity(e.id);
			const role = await getRole(e.role);

			return {entity, role};
		}),
	);

	return {...user, entities};
}

export async function getUser(id: string): Promise<User | undefined> {
	const user = await db.collection("users").findOne<User>({id: id}, {projection: {_id: 0}});
	return !!user ? user : undefined;
}

export async function getSpecificUsers(ids: string[]) {
	if (ids.length === 0) return [];

	return await db
		.collection("users")
		.find<User>({id: {$in: ids}}, {projection: {_id: 0}})
		.toArray();
}

export async function getEntityUsers(id: string, limit?: number) {
	return await db
		.collection("users")
		.find<User>({"entities.id": id})
		.limit(limit || 0)
		.toArray();
}

export async function countEntityUsers(id: string) {
	return await db.collection("users").countDocuments({"entities.id": id});
}

export async function getEntitiesUsers(ids: string[], limit?: number) {
	return await db
		.collection("users")
		.find<User>({"entities.id": {$in: ids}})
		.limit(limit || 0)
		.toArray();
}

export async function countEntitiesUsers(ids: string[]) {
	return await db.collection("users").countDocuments({"entities.id": {$in: ids}});
}

export async function getLinkedUsers(
	userID?: string,
	userType?: Type,
	type?: Type,
	page?: number,
	size?: number,
	sort?: string,
	direction?: "asc" | "desc",
) {
	const filters = {
		...(!!type ? {type} : {}),
	};

	if (!userID || userType === "admin" || userType === "developer") {
		const users = await db
			.collection("users")
			.find<User>(filters)
			.sort(sort ? {[sort]: direction === "desc" ? -1 : 1} : {})
			.skip(page && size ? page * size : 0)
			.limit(size || 0)
			.toArray();

		const total = await db.collection("users").countDocuments(filters);
		return {users, total};
	}

	const adminGroups = await getUserGroups(userID);
	const groups = await getUsersGroups(adminGroups.flatMap((x) => x.participants));
	const belongingGroups = await getParticipantGroups(userID);

	const participants = uniq([
		...adminGroups.flatMap((x) => x.participants),
		...(userType === "mastercorporate" ? groups.flat().flatMap((x) => x.participants) : []),
		...(userType === "teacher" ? belongingGroups.flatMap((x) => x.participants) : []),
	]);

	//  ⨯ [FirebaseError: Invalid Query. A non-empty array is required for 'in' filters.] {
	if (participants.length === 0) return {users: [], total: 0};

	const users = await db
		.collection("users")
		.find<User>({...filters, id: {$in: participants}})
		.skip(page && size ? page * size : 0)
		.limit(size || 0)
		.toArray();
	const total = await db.collection("users").countDocuments({...filters, id: {$in: participants}});

	return {users, total};
}

export async function getUserBalance(user: User) {
	const codes = await getUserCodes(user.id);
	if (user.type !== "corporate" && user.type !== "mastercorporate") return codes.length;

	const groups = await getGroupsForUser(user.id);
	const participants = uniq(groups.flatMap((x) => x.participants));

	if (user.type === "corporate") return participants.length + codes.filter((x) => !participants.includes(x.userId || "")).length;

	const participantUsers = await Promise.all(participants.map(getUser));
	const corporateUsers = participantUsers.filter((x) => x?.type === "corporate") as CorporateUser[];

	return (
		corporateUsers.reduce((acc, curr) => acc + curr.corporateInformation?.companyInformation?.userAmount || 0, 0) +
		corporateUsers.length +
		codes.filter((x) => !participants.includes(x.userId || "") && !corporateUsers.map((u) => u.id).includes(x.userId || "")).length
	);
}
