import {db} from '@db/instance';
import {
  mapHuMemberToDB,
  mapMembershipFromDB,
  mapMembershipToDB,
} from '@modules/chats/utils';
import {Membership} from '@modules/chats/interfaces';
import {INACTIVE_USER_STATUSES} from '@modules/chats/constants';
import {persistedUserId} from '@services/localStorage';
import {asyncMap} from '@shared/utils';

import {
  deleteUserAllMemberships,
  getAdminsFromConversation,
  getOtherMemberships,
  getUserMembership,
  getUsersFromChannel,
  insertMemberships,
} from '../repositories/groupMemberships';
import {insertUsers} from '../repositories/users';

export const getShortGroupMemberships = async (channel: string) => {
  if (!persistedUserId) {
    return {memberships: [], isUserAdmin: false};
  }

  const userMembership = await getUserMembership(channel, persistedUserId);
  const otherMemberships = await getOtherMemberships({
    channel,
    ignoreUserId: persistedUserId,
    limit: 8,
  });
  const memberships = userMembership
    ? [userMembership, ...otherMemberships]
    : otherMemberships;

  return {
    memberships: memberships.map(mapMembershipFromDB),
    isUserAdmin: !!userMembership?.group_memberships?.is_admin,
  };
};

export const getUsersFromChannelService = async ({
  page = 1,
  search,
  limit = 20,
  channel,
}: {
  limit?: number;
  search?: string;
  page?: number;
  channel: string;
}) => {
  const result = await getUsersFromChannel({page, search, limit, channel});
  return result.map(mapMembershipFromDB);
};

export const isCurrentUserTheOnlyAdmin = async (channel: string) => {
  if (!persistedUserId) {
    return false;
  }
  const admins = await getAdminsFromConversation(channel);
  return admins.length === 1 && admins.at(0)?.user_id === persistedUserId;
};

/**
 * Save users and memberships in a single transaction, and remove memberships for inactive users.
 * It should be done in a single transaction to avoid inconsistencies in queries related to the users and memberships.
 */
export const processUsersAndMemberships = async (
  memberships: Membership[],
  channel: string,
) => {
  return await db.instance.transaction(async tx => {
    const chunkSize = 200;
    const users = memberships.map(membership => membership.user);
    const inactiveUsers = users.filter(
      user => user.status in INACTIVE_USER_STATUSES,
    );

    // Save users in batches
    const userPromises: Promise<unknown>[] = [];
    for (let i = 0; i < users.length; i += chunkSize) {
      const usersToDB = users.slice(i, i + chunkSize).map(mapHuMemberToDB);
      userPromises.push(insertUsers(usersToDB, tx));
    }
    await Promise.all(userPromises);

    // Save memberships in batches
    const membershipPromises: Promise<unknown>[] = [];
    for (let i = 0; i < memberships.length; i += chunkSize) {
      const membershipsToDB = memberships
        .slice(i, i + chunkSize)
        .map(membership => mapMembershipToDB({channel, ...membership}));
      membershipPromises.push(insertMemberships(membershipsToDB, tx));
    }
    await Promise.all(membershipPromises);

    // Delete memberships for inactive users
    if (inactiveUsers.length) {
      await asyncMap(inactiveUsers, user =>
        deleteUserAllMemberships(user.id, tx),
      );
    }
  });
};
