import {
	getPermission,
	Member,
	PublicMemberProjection,
	Role,
} from "@fosscord/util";
import { LazyRequest } from "../schema/LazyRequest";
import { Send } from "../util/Send";
import { OPCODES } from "../util/Constants";
import { WebSocket, Payload } from "@fosscord/gateway";
import { check } from "./instanceOf";
import "missing-native-js-functions";

// TODO: check permission and only show roles/members that have access to this channel
// TODO: config: to list all members (even those who are offline) sorted by role, or just those who are online
// TODO: rewrite typeorm

export async function onLazyRequest(this: WebSocket, { d }: Payload) {
	// TODO: check data
	check.call(this, LazyRequest, d);
	const { guild_id, typing, channels, activities } = d as LazyRequest;

	const permissions = await getPermission(this.user_id, guild_id);
	permissions.hasThrow("VIEW_CHANNEL");

	var members = await Member.find({
		where: { guild_id: guild_id },
		relations: ["roles", "user"],
		select: PublicMemberProjection,
	});

	const roles = await Role.find({
		where: { guild_id: guild_id },
		order: {
			position: "DESC",
		},
	});

	const groups = [] as any[];
	var member_count = 0;
	const items = [];

	for (const role of roles) {
		// @ts-ignore
		const [role_members, other_members] = partition(members, (m: Member) =>
			m.roles.find((r) => r.id === role.id)
		);
		const group = {
			count: role_members.length,
			id: role.id === guild_id ? "online" : role.id,
		};

		items.push({ group });
		groups.push(group);

		for (const member of role_members) {
			member.roles = member.roles.filter((x: Role) => x.id !== guild_id);
			items.push({
				member: {
					...member,
					roles: member.roles.map((x: Role) => x.id),
				},
			});
		}
		members = other_members;
		member_count += role_members.length;
	}

	return Send(this, {
		op: OPCODES.Dispatch,
		s: this.sequence++,
		t: "GUILD_MEMBER_LIST_UPDATE",
		d: {
			ops: [
				{
					range: [0, 99],
					op: "SYNC",
					items,
				},
			],
			online_count: member_count, // TODO count online count
			member_count,
			id: "everyone",
			guild_id,
			groups,
		},
	});
}

function partition<T>(array: T[], isValid: Function) {
	// @ts-ignore
	return array.reduce(
		// @ts-ignore
		([pass, fail], elem) => {
			return isValid(elem)
				? [[...pass, elem], fail]
				: [pass, [...fail, elem]];
		},
		[[], []]
	);
}
