/**
 * Get a list of groups of which the specified user is a member.
 *
 * Get a list of the groups to which this member belongs,
 * filtered by group membership status and role.
 * Usage examples: Used with no arguments specified,
 *
 *    bp_get_user_groups( bp_loggedin_user_id() );
 *
 * returns an array of the groups in which the logged-in user
 * is an unpromoted member. To fetch an array of all groups that
 * the current user belongs to, in any membership role,
 * member, moderator or administrator, use
 *
 *    bp_get_user_groups( $user_id, array(
 *        'is_admin' => null,
 *        'is_mod' => null,
 *    ) );
 *
 * @since 2.6.0
 *
 * @param int $user_id ID of the user.
 * @param array $args {
 *     Array of optional args.
 *     @param bool|null   $is_confirmed Whether to return only confirmed memberships. Pass `null` to disable this
 *                                      filter. Default: true.
 *     @param bool|null   $is_banned    Whether to return only banned memberships. Pass `null` to disable this filter.
 *                                      Default: false.
 *     @param bool|null   $is_admin     Whether to return only admin memberships. Pass `null` to disable this filter.
 *                                      Default: false.
 *     @param bool|null   $is_mod       Whether to return only mod memberships. Pass `null` to disable this filter.
 *                                      Default: false.
 *     @param bool|null   $invite_sent  Whether to return only memberships with 'invite_sent'. Pass `null` to disable
 *                                      this filter. Default: false.
 *     @param string      $orderby      Field to order by. Accepts 'id' (membership ID), 'group_id', 'date_modified'.
 *                                      Default: 'group_id'.
 *     @param string      $order        Sort order. Accepts 'ASC' or 'DESC'. Default: 'ASC'.
 * }
 * @return array Array of matching group memberships, keyed by group ID.
 */
function bp_get_user_groups($user_id, $args = array())
{
    $r = bp_parse_args($args, array('is_confirmed' => true, 'is_banned' => false, 'is_admin' => false, 'is_mod' => false, 'invite_sent' => null, 'orderby' => 'group_id', 'order' => 'ASC'), 'get_user_groups');
    $user_id = intval($user_id);
    $membership_ids = wp_cache_get($user_id, 'bp_groups_memberships_for_user');
    if (false === $membership_ids) {
        $membership_ids = BP_Groups_Member::get_membership_ids_for_user($user_id);
        wp_cache_set($user_id, $membership_ids, 'bp_groups_memberships_for_user');
    }
    // Prime the membership cache.
    $uncached_membership_ids = bp_get_non_cached_ids($membership_ids, 'bp_groups_memberships');
    if (!empty($uncached_membership_ids)) {
        $uncached_memberships = BP_Groups_Member::get_memberships_by_id($uncached_membership_ids);
        foreach ($uncached_memberships as $uncached_membership) {
            wp_cache_set($uncached_membership->id, $uncached_membership, 'bp_groups_memberships');
        }
    }
    // Assemble filter array for use in `wp_list_filter()`.
    $filters = wp_array_slice_assoc($r, array('is_confirmed', 'is_banned', 'is_admin', 'is_mod', 'invite_sent'));
    foreach ($filters as $filter_name => $filter_value) {
        if (is_null($filter_value)) {
            unset($filters[$filter_name]);
        }
    }
    // Populate group membership array from cache, and normalize.
    $groups = array();
    $int_keys = array('id', 'group_id', 'user_id', 'inviter_id');
    $bool_keys = array('is_admin', 'is_mod', 'is_confirmed', 'is_banned', 'invite_sent');
    foreach ($membership_ids as $membership_id) {
        $membership = wp_cache_get($membership_id, 'bp_groups_memberships');
        // Sanity check.
        if (!isset($membership->group_id)) {
            continue;
        }
        // Integer values.
        foreach ($int_keys as $index) {
            $membership->{$index} = intval($membership->{$index});
        }
        // Boolean values.
        foreach ($bool_keys as $index) {
            $membership->{$index} = (bool) $membership->{$index};
        }
        foreach ($filters as $filter_name => $filter_value) {
            if (!isset($membership->{$filter_name}) || $filter_value != $membership->{$filter_name}) {
                continue 2;
            }
        }
        $group_id = (int) $membership->group_id;
        $groups[$group_id] = $membership;
    }
    // By default, results are ordered by membership id.
    if ('group_id' === $r['orderby']) {
        ksort($groups);
    } elseif (in_array($r['orderby'], array('id', 'date_modified'))) {
        $groups = bp_sort_by_key($groups, $r['orderby']);
    }
    // By default, results are ordered ASC.
    if ('DESC' === strtoupper($r['order'])) {
        // `true` to preserve keys.
        $groups = array_reverse($groups, true);
    }
    return $groups;
}