<template>
    <el-dialog
        :z-index="userDialogIndex"
        class="x-dialog x-user-dialog"
        v-model="userDialog.visible"
        :show-close="false"
        top="10vh"
        width="940px">
        <div v-loading="userDialog.loading">
            <UserSummaryHeader
                :get-user-state-text="getUserStateText"
                :copy-user-display-name="copyUserDisplayName"
                :toggle-badge-visibility="toggleBadgeVisibility"
                :toggle-badge-showcased="toggleBadgeShowcased"
                :user-dialog-command="userDialogCommand" />

            <TabsUnderline
                v-model="userDialogLastActiveTab"
                :items="userDialogTabs"
                :unmount-on-hide="false"
                @update:modelValue="userDialogTabClick">
                <template #Info>
                    <template v-if="isFriendOnline(userDialog.friend) || currentUser.id === userDialog.id">
                        <div
                            v-if="userDialog.ref.location"
                            style="
                                display: flex;
                                flex-direction: column;
                                margin-bottom: 10px;
                                padding-bottom: 10px;
                                border-bottom: 1px solid #e4e7ed14;
                            ">
                            <div style="flex: none">
                                <template v-if="isRealInstance(userDialog.$location.tag)">
                                    <div class="flex items-center mb-1">
                                        <Launch :location="userDialog.$location.tag" />
                                        <InviteYourself
                                            :location="userDialog.$location.tag"
                                            :shortname="userDialog.$location.shortName"
                                            style="margin-left: 5px" />
                                        <TooltipWrapper
                                            side="top"
                                            :content="t('dialog.user.info.refresh_instance_info')"
                                            ><Button
                                                class="rounded-full ml-1 w-6 h-6 text-xs text-muted-foreground hover:text-foreground"
                                                size="icon"
                                                variant="outline"
                                                @click="refreshInstancePlayerCount(userDialog.$location.tag)"
                                                ><RefreshCw class="h-4 w-4" />
                                            </Button>
                                        </TooltipWrapper>
                                        <LastJoin
                                            :location="userDialog.$location.tag"
                                            :currentlocation="lastLocation.location" />
                                        <InstanceInfo
                                            :location="userDialog.$location.tag"
                                            :instance="userDialog.instance.ref"
                                            :friendcount="userDialog.instance.friendCount" />
                                    </div>
                                </template>
                                <Location
                                    :location="userDialog.ref.location"
                                    :traveling="userDialog.ref.travelingToLocation"
                                    style="display: block; margin-top: 5px" />
                            </div>
                            <div class="x-friend-list" style="flex: 1; margin-top: 10px; max-height: 150px">
                                <div
                                    v-if="userDialog.$location.userId"
                                    class="x-friend-item x-friend-item-border"
                                    @click="showUserDialog(userDialog.$location.userId)">
                                    <template v-if="userDialog.$location.user">
                                        <div class="avatar" :class="userStatusClass(userDialog.$location.user)">
                                            <img :src="userImage(userDialog.$location.user, true)" loading="lazy" />
                                        </div>
                                        <div class="detail">
                                            <span
                                                class="name"
                                                :style="{ color: userDialog.$location.user.$userColour }"
                                                v-text="userDialog.$location.user.displayName"></span>
                                            <span class="extra">{{ t('dialog.user.info.instance_creator') }}</span>
                                        </div>
                                    </template>
                                    <span v-else v-text="userDialog.$location.userId"></span>
                                </div>
                                <div
                                    v-for="user in userDialog.users"
                                    :key="user.id"
                                    class="x-friend-item x-friend-item-border"
                                    @click="showUserDialog(user.id)">
                                    <div class="avatar" :class="userStatusClass(user)">
                                        <img :src="userImage(user, true)" loading="lazy" />
                                    </div>
                                    <div class="detail">
                                        <span
                                            class="name"
                                            :style="{ color: user.$userColour }"
                                            v-text="user.displayName"></span>
                                        <span v-if="user.location === 'traveling'" class="extra">
                                            <Loader2 class="is-loading" style="margin-right: 3px" />
                                            <Timer :epoch="user.$travelingToTime" />
                                        </span>
                                        <span v-else class="extra">
                                            <Timer :epoch="user.$location_at" />
                                        </span>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </template>

                    <div class="x-friend-list" style="max-height: none">
                        <div
                            v-if="userDialog.note && !hideUserNotes"
                            class="x-friend-item"
                            style="width: 100%; cursor: pointer">
                            <div class="detail" @click="isEditNoteAndMemoDialogVisible = true">
                                <span class="name">{{ t('dialog.user.info.note') }}</span>
                                <pre
                                    class="extra"
                                    style="
                                        font-family: inherit;
                                        font-size: 12px;
                                        white-space: pre-wrap;
                                        margin: 0 0.5em 0 0;
                                        max-height: 210px;
                                        overflow-y: auto;
                                    "
                                    >{{ userDialog.note }}</pre
                                >
                            </div>
                        </div>
                        <div
                            v-if="userDialog.memo && !hideUserMemos"
                            class="x-friend-item"
                            style="width: 100%; cursor: pointer">
                            <div class="detail" @click="isEditNoteAndMemoDialogVisible = true">
                                <span class="name">{{ t('dialog.user.info.memo') }}</span>
                                <pre
                                    class="extra"
                                    style="
                                        font-family: inherit;
                                        font-size: 12px;
                                        white-space: pre-wrap;
                                        margin: 0 0.5em 0 0;
                                        max-height: 210px;
                                        overflow-y: auto;
                                    "
                                    >{{ userDialog.memo }}</pre
                                >
                            </div>
                        </div>
                        <div class="x-friend-item" style="width: 100%; cursor: default">
                            <div class="detail">
                                <span
                                    v-if="
                                        userDialog.id !== currentUser.id &&
                                        userDialog.ref.profilePicOverride &&
                                        userDialog.ref.currentAvatarImageUrl
                                    "
                                    class="name">
                                    {{ t('dialog.user.info.avatar_info_last_seen') }}
                                </span>
                                <span v-else class="name">{{ t('dialog.user.info.avatar_info') }}</span>
                                <div class="extra">
                                    <AvatarInfo
                                        :imageurl="userDialog.ref.currentAvatarImageUrl"
                                        :userid="userDialog.id"
                                        :avatartags="userDialog.ref.currentAvatarTags"
                                        style="display: inline-block" />
                                    <TooltipWrapper
                                        v-if="
                                            userDialog.ref.profilePicOverride && !userDialog.ref.currentAvatarImageUrl
                                        "
                                        side="top"
                                        :content="t('dialog.user.info.vrcplus_hides_avatar')">
                                        <AlertTriangle />
                                    </TooltipWrapper>
                                </div>
                            </div>
                        </div>
                        <div class="x-friend-item" style="width: 100%; cursor: default">
                            <div class="detail">
                                <span class="name" style="margin-bottom: 5px">{{
                                    t('dialog.user.info.represented_group')
                                }}</span>
                                <div
                                    v-if="
                                        userDialog.isRepresentedGroupLoading ||
                                        (userDialog.representedGroup && userDialog.representedGroup.isRepresenting)
                                    "
                                    class="extra">
                                    <div style="display: inline-block; flex: none; margin-right: 5px">
                                        <el-image
                                            v-loading="userDialog.isRepresentedGroupLoading"
                                            class="x-link"
                                            :src="userDialog.representedGroup.$thumbnailUrl"
                                            style="
                                                flex: none;
                                                width: 60px;
                                                height: 60px;
                                                border-radius: 4px;
                                                object-fit: cover;
                                            "
                                            :style="{
                                                background: userDialog.isRepresentedGroupLoading ? '#f5f7fa' : ''
                                            }"
                                            @load="userDialog.isRepresentedGroupLoading = false"
                                            @click="showFullscreenImageDialog(userDialog.representedGroup.iconUrl)">
                                            <template #error></template>
                                        </el-image>
                                    </div>
                                    <span
                                        v-if="userDialog.representedGroup.isRepresenting"
                                        style="vertical-align: top; cursor: pointer"
                                        @click="showGroupDialog(userDialog.representedGroup.groupId)">
                                        <span
                                            v-if="userDialog.representedGroup.ownerId === userDialog.id"
                                            style="margin-right: 5px"
                                            >👑</span
                                        >
                                        <span
                                            style="margin-right: 5px"
                                            v-text="userDialog.representedGroup.name"></span>
                                        <span>({{ userDialog.representedGroup.memberCount }})</span>
                                    </span>
                                </div>
                                <div v-else class="extra">-</div>
                            </div>
                        </div>
                        <div class="x-friend-item" style="width: 100%; cursor: default">
                            <div class="detail">
                                <span class="name">{{ t('dialog.user.info.bio') }}</span>
                                <pre
                                    class="extra"
                                    style="
                                        font-family: inherit;
                                        font-size: 12px;
                                        white-space: pre-wrap;
                                        margin: 0 0.5em 0 0;
                                        max-height: 210px;
                                        overflow-y: auto;
                                    "
                                    >{{ bioCache.translated || userDialog.ref.bio || '-' }}</pre
                                >
                                <div style="float: right">
                                    <Button
                                        v-if="translationApi && userDialog.ref.bio"
                                        class="w-3 h-6 text-xs mr-0.5"
                                        size="icon-sm"
                                        variant="ghost"
                                        @click="translateBio">
                                        <Spinner v-if="translateLoading" class="size-1" />
                                        <Languages v-else class="h-3 w-3" />
                                    </Button>
                                    <Button
                                        class="w-3 h-6 text-xs"
                                        size="icon-sm"
                                        variant="ghost"
                                        v-if="userDialog.id === currentUser.id"
                                        style="margin-left: 5px; padding: 0"
                                        @click="showBioDialog"
                                        ><Pencil class="h-3 w-3" />
                                    </Button>
                                </div>
                                <div style="margin-top: 5px" class="flex items-center">
                                    <TooltipWrapper v-for="(link, index) in userDialog.ref.bioLinks" :key="index">
                                        <template #content>
                                            <span v-text="link"></span>
                                        </template>
                                        <!-- onerror="this.onerror=null;this.class='icon-error'" -->
                                        <img
                                            :src="getFaviconUrl(link)"
                                            style="
                                                width: 16px;
                                                height: 16px;
                                                vertical-align: middle;
                                                margin-right: 5px;
                                                cursor: pointer;
                                            "
                                            @click.stop="openExternalLink(link)"
                                            loading="lazy" />
                                    </TooltipWrapper>
                                </div>
                            </div>
                        </div>
                        <template v-if="currentUser.id !== userDialog.id">
                            <div class="x-friend-item" style="cursor: default">
                                <div class="detail">
                                    <span class="name">
                                        {{ t('dialog.user.info.last_seen') }}
                                        <TooltipWrapper side="top" :content="t('dialog.user.info.accuracy_notice')">
                                            <AlertTriangle style="margin-left: 3px" />
                                        </TooltipWrapper>
                                    </span>
                                    <span class="extra">{{ formatDateFilter(userDialog.lastSeen, 'long') }}</span>
                                </div>
                            </div>

                            <div class="x-friend-item" @click="showPreviousInstancesUserDialog(userDialog.ref)">
                                <div class="detail">
                                    <div
                                        class="name"
                                        style="display: flex; justify-content: space-between; align-items: center">
                                        <div>
                                            {{ t('dialog.user.info.join_count') }}
                                            <TooltipWrapper side="top" :content="t('dialog.user.info.accuracy_notice')">
                                                <AlertTriangle style="margin-left: 3px" />
                                            </TooltipWrapper>
                                        </div>

                                        <TooltipWrapper
                                            side="top"
                                            :content="t('dialog.user.info.open_previous_instance')">
                                            <MoreHorizontal style="margin-right: 16px" />
                                        </TooltipWrapper>
                                    </div>
                                    <span v-if="userDialog.joinCount === 0" class="extra">-</span>
                                    <span v-else class="extra" v-text="userDialog.joinCount"></span>
                                </div>
                            </div>

                            <div class="x-friend-item" style="cursor: default">
                                <div class="detail">
                                    <span class="name">
                                        {{ t('dialog.user.info.time_together') }}
                                        <TooltipWrapper side="top" :content="t('dialog.user.info.accuracy_notice')">
                                            <AlertTriangle style="margin-left: 3px" />
                                        </TooltipWrapper>
                                    </span>
                                    <span v-if="userDialog.timeSpent === 0" class="extra">-</span>
                                    <span v-else class="extra">{{ timeToText(userDialog.timeSpent) }}</span>
                                </div>
                            </div>
                        </template>
                        <template v-else>
                            <TooltipWrapper
                                :disabled="currentUser.id !== userDialog.id"
                                side="top"
                                :content="t('dialog.user.info.open_previous_instance')">
                                <div class="x-friend-item" @click="showPreviousInstancesUserDialog(userDialog.ref)">
                                    <div class="detail">
                                        <span class="name">
                                            {{ t('dialog.user.info.play_time') }}
                                            <TooltipWrapper side="top" :content="t('dialog.user.info.accuracy_notice')">
                                                <AlertTriangle style="margin-left: 3px" />
                                            </TooltipWrapper>
                                        </span>
                                        <span v-if="userDialog.timeSpent === 0" class="extra">-</span>
                                        <span v-else class="extra">{{ timeToText(userDialog.timeSpent) }}</span>
                                    </div>
                                </div>
                            </TooltipWrapper>
                        </template>
                        <div class="x-friend-item" style="cursor: default">
                            <TooltipWrapper :side="currentUser.id !== userDialog.id ? 'bottom' : 'top'">
                                <template #content>
                                    <span>{{ formatDateFilter(userOnlineForTimestamp(userDialog), 'short') }}</span>
                                </template>
                                <div class="detail">
                                    <span
                                        v-if="userDialog.ref.state === 'online' && userDialog.ref.$online_for"
                                        class="name">
                                        {{ t('dialog.user.info.online_for') }}
                                        <TooltipWrapper side="top" :content="t('dialog.user.info.accuracy_notice')">
                                            <AlertTriangle style="margin-left: 3px" />
                                        </TooltipWrapper>
                                    </span>
                                    <span v-else class="name">
                                        {{ t('dialog.user.info.offline_for') }}
                                        <TooltipWrapper side="top" :content="t('dialog.user.info.accuracy_notice')">
                                            <AlertTriangle style="margin-left: 3px" />
                                        </TooltipWrapper>
                                    </span>
                                    <span class="extra">{{ userOnlineFor(userDialog.ref) }}</span>
                                </div>
                            </TooltipWrapper>
                        </div>
                        <div class="x-friend-item" style="cursor: default">
                            <TooltipWrapper :side="currentUser.id !== userDialog.id ? 'bottom' : 'top'">
                                <template #content>
                                    <span
                                        >{{ t('dialog.user.info.last_login') }}
                                        {{ formatDateFilter(userDialog.ref.last_login, 'long') }}</span
                                    >
                                    <br />
                                    <span
                                        >{{ t('dialog.user.info.last_activity') }}
                                        {{ formatDateFilter(userDialog.ref.last_activity, 'long') }}</span
                                    >
                                </template>
                                <div class="detail">
                                    <span class="name">{{ t('dialog.user.info.last_activity') }}</span>
                                    <span v-if="userDialog.ref.last_activity" class="extra">{{
                                        timeToText(Date.now() - Date.parse(userDialog.ref.last_activity))
                                    }}</span>
                                    <span v-else class="extra">-</span>
                                </div>
                            </TooltipWrapper>
                        </div>
                        <div class="x-friend-item" style="cursor: default">
                            <div class="detail">
                                <span class="name">{{ t('dialog.user.info.date_joined') }}</span>
                                <span class="extra" v-text="userDialog.ref.date_joined"></span>
                            </div>
                        </div>
                        <div v-if="currentUser.id !== userDialog.id" class="x-friend-item" style="cursor: default">
                            <TooltipWrapper side="top" :disabled="!userDialog.dateFriendedInfo.length">
                                <template v-if="userDialog.dateFriendedInfo.length" #content>
                                    <template v-for="ref in userDialog.dateFriendedInfo" :key="ref.type">
                                        <span>{{ ref.type }}: {{ formatDateFilter(ref.created_at, 'long') }}</span
                                        ><br />
                                    </template>
                                </template>
                                <div class="detail">
                                    <span v-if="userDialog.unFriended" class="name">
                                        {{ t('dialog.user.info.unfriended') }}
                                        <TooltipWrapper side="top" :content="t('dialog.user.info.accuracy_notice')">
                                            <AlertTriangle style="margin-left: 3px" />
                                        </TooltipWrapper>
                                    </span>
                                    <span v-else class="name">
                                        {{ t('dialog.user.info.friended') }}
                                        <TooltipWrapper side="top" :content="t('dialog.user.info.accuracy_notice')">
                                            <AlertTriangle style="margin-left: 3px" />
                                        </TooltipWrapper>
                                    </span>
                                    <span class="extra">{{ formatDateFilter(userDialog.dateFriended, 'long') }}</span>
                                </div>
                            </TooltipWrapper>
                        </div>
                        <template v-if="currentUser.id === userDialog.id">
                            <div class="x-friend-item" @click="toggleAvatarCopying">
                                <div class="detail">
                                    <span class="name">{{ t('dialog.user.info.avatar_cloning') }}</span>
                                    <span
                                        v-if="currentUser.allowAvatarCopying"
                                        class="extra"
                                        style="color: var(--el-color-success)"
                                        >{{ t('dialog.user.info.avatar_cloning_allow') }}</span
                                    >
                                    <span v-else class="extra" style="color: var(--el-color-danger)">{{
                                        t('dialog.user.info.avatar_cloning_deny')
                                    }}</span>
                                </div>
                            </div>
                            <div class="x-friend-item" @click="toggleAllowBooping">
                                <div class="detail">
                                    <span class="name">{{ t('dialog.user.info.booping') }}</span>
                                    <span
                                        v-if="currentUser.isBoopingEnabled"
                                        class="extra"
                                        style="color: var(--el-color-success)"
                                        >{{ t('dialog.user.info.avatar_cloning_allow') }}</span
                                    >
                                    <span v-else class="extra" style="color: var(--el-color-danger)">{{
                                        t('dialog.user.info.avatar_cloning_deny')
                                    }}</span>
                                </div>
                            </div>
                            <div class="x-friend-item" @click="toggleSharedConnectionsOptOut">
                                <div class="detail">
                                    <span class="name">{{ t('dialog.user.info.show_mutual_friends') }}</span>
                                    <span
                                        v-if="!currentUser.hasSharedConnectionsOptOut"
                                        class="extra"
                                        style="color: var(--el-color-success)"
                                        >{{ t('dialog.user.info.avatar_cloning_allow') }}</span
                                    >
                                    <span v-else class="extra" style="color: var(--el-color-danger)">{{
                                        t('dialog.user.info.avatar_cloning_deny')
                                    }}</span>
                                </div>
                            </div>
                        </template>
                        <template v-else>
                            <div class="x-friend-item" style="cursor: default">
                                <div class="detail">
                                    <span class="name">{{ t('dialog.user.info.avatar_cloning') }}</span>
                                    <span
                                        v-if="userDialog.ref.allowAvatarCopying"
                                        class="extra"
                                        style="color: var(--el-color-success)"
                                        >{{ t('dialog.user.info.avatar_cloning_allow') }}</span
                                    >
                                    <span v-else class="extra" style="color: var(--el-color-danger)">{{
                                        t('dialog.user.info.avatar_cloning_deny')
                                    }}</span>
                                </div>
                            </div>
                        </template>
                        <div
                            v-if="userDialog.ref.id === currentUser.id"
                            class="x-friend-item"
                            @click="getVRChatCredits()">
                            <div class="detail">
                                <span class="name">{{ t('view.profile.profile.vrchat_credits') }}</span>
                                <span class="extra">{{ vrchatCredit ?? t('view.profile.profile.refresh') }}</span>
                            </div>
                        </div>
                        <div
                            v-if="userDialog.ref.id === currentUser.id && currentUser.homeLocation"
                            class="x-friend-item"
                            style="width: 100%"
                            @click="showWorldDialog(currentUser.homeLocation)">
                            <div class="detail">
                                <span class="name">{{ t('dialog.user.info.home_location') }}</span>
                                <span class="extra">
                                    <span v-text="userDialog.$homeLocationName"></span>
                                    <Button
                                        class="rounded-full ml-1 text-xs"
                                        size="icon-sm"
                                        variant="outline"
                                        @click.stop="resetHome()"
                                        ><Trash2 class="h-4 w-4" />
                                    </Button>
                                </span>
                            </div>
                        </div>
                        <div class="x-friend-item" style="width: 100%; cursor: default">
                            <div class="detail">
                                <span class="name">{{ t('dialog.user.info.id') }}</span>
                                <span class="extra">
                                    {{ userDialog.id }}
                                    <TooltipWrapper side="top" :content="t('dialog.user.info.id_tooltip')">
                                        <DropdownMenu>
                                            <DropdownMenuTrigger as-child>
                                                <Button
                                                    class="rounded-full ml-1 text-xs"
                                                    size="icon-sm"
                                                    variant="outline"
                                                    @click.stop
                                                    ><Copy class="h-4 w-4" />
                                                </Button>
                                            </DropdownMenuTrigger>
                                            <DropdownMenuContent>
                                                <DropdownMenuItem @click="copyUserId(userDialog.id)">
                                                    {{ t('dialog.user.info.copy_id') }}
                                                </DropdownMenuItem>
                                                <DropdownMenuItem @click="copyUserURL(userDialog.id)">
                                                    {{ t('dialog.user.info.copy_url') }}
                                                </DropdownMenuItem>
                                                <DropdownMenuItem
                                                    @click="copyUserDisplayName(userDialog.ref.displayName)">
                                                    {{ t('dialog.user.info.copy_display_name') }}
                                                </DropdownMenuItem>
                                            </DropdownMenuContent>
                                        </DropdownMenu>
                                    </TooltipWrapper>
                                </span>
                            </div>
                        </div>
                    </div>
                </template>

                <template v-if="userDialog.id !== currentUser.id && !currentUser.hasSharedConnectionsOptOut" #mutual>
                    <div style="display: flex; align-items: center; justify-content: space-between">
                        <div style="display: flex; align-items: center">
                            <Button
                                class="rounded-full"
                                variant="outline"
                                size="icon-sm"
                                :disabled="userDialog.isMutualFriendsLoading"
                                @click="getUserMutualFriends(userDialog.id)">
                                <Spinner v-if="userDialog.isMutualFriendsLoading" />
                                <RefreshCw v-else />
                            </Button>
                            <span style="margin-left: 5px">{{
                                t('dialog.user.groups.total_count', { count: userDialog.mutualFriends.length })
                            }}</span>
                        </div>
                        <div style="display: flex; align-items: center">
                            <span style="margin-right: 5px">{{ t('dialog.user.groups.sort_by') }}</span>
                            <Select
                                :model-value="userDialogMutualFriendSortingKey"
                                :disabled="userDialog.isMutualFriendsLoading"
                                @update:modelValue="setUserDialogMutualFriendSortingByKey">
                                <SelectTrigger size="sm" @click.stop>
                                    <SelectValue />
                                </SelectTrigger>
                                <SelectContent>
                                    <SelectItem
                                        v-for="(item, key) in userDialogMutualFriendSortingOptions"
                                        :key="String(key)"
                                        :value="String(key)">
                                        {{ t(item.name) }}
                                    </SelectItem>
                                </SelectContent>
                            </Select>
                        </div>
                    </div>
                    <div
                        v-if="mutualFriendsError"
                        @click="openExternalLink('https://docs.vrchat.com/docs/vrchat-202542#mutuals')"
                        style="
                            margin-top: 20px;
                            display: flex;
                            justify-content: center;
                            align-items: center;
                            color: #f56c6c;
                            cursor: pointer;
                        ">
                        <AlertTriangle style="margin-right: 5px" />
                        <span>Mutual Friends unavailable due to VRChat staged rollout, click for more info</span>
                    </div>
                    <ul
                        class="x-friend-list"
                        style="margin-top: 10px; overflow: auto; max-height: 250px; min-width: 130px">
                        <li
                            v-for="user in userDialog.mutualFriends"
                            :key="user.id"
                            class="x-friend-item x-friend-item-border"
                            @click="showUserDialog(user.id)">
                            <div class="avatar">
                                <img :src="userImage(user)" loading="lazy" />
                            </div>
                            <div class="detail">
                                <span
                                    class="name"
                                    :style="{ color: user.$userColour }"
                                    v-text="user.displayName"></span>
                            </div>
                        </li>
                    </ul>
                </template>

                <template #Groups>
                    <div style="display: flex; align-items: center; justify-content: space-between">
                        <div style="display: flex; align-items: center">
                            <Button
                                class="rounded-full"
                                variant="outline"
                                size="icon-sm"
                                :disabled="userDialog.isGroupsLoading"
                                @click="getUserGroups(userDialog.id)">
                                <Spinner v-if="userDialog.isGroupsLoading" />
                                <RefreshCw v-else />
                            </Button>
                            <span style="margin-left: 5px">{{
                                t('dialog.user.groups.total_count', { count: userGroups.groups.length })
                            }}</span>
                            <template v-if="userDialogGroupEditMode">
                                <span
                                    style="margin-left: 10px; color: var(--el-text-color-secondary); font-size: 10px"
                                    >{{ t('dialog.user.groups.hold_shift') }}</span
                                >
                            </template>
                        </div>
                        <div style="display: flex; align-items: center">
                            <template v-if="!userDialogGroupEditMode">
                                <span style="margin-right: 5px">{{ t('dialog.user.groups.sort_by') }}</span>
                                <Select
                                    :model-value="userDialogGroupSortingKey"
                                    :disabled="userDialog.isGroupsLoading"
                                    @update:modelValue="setUserDialogGroupSortingByKey">
                                    <SelectTrigger size="sm" @click.stop>
                                        <SelectValue />
                                    </SelectTrigger>
                                    <SelectContent>
                                        <SelectItem
                                            v-for="(item, key) in userDialogGroupSortingOptions"
                                            :key="String(key)"
                                            :value="String(key)"
                                            :disabled="
                                                item === userDialogGroupSortingOptions.inGame &&
                                                userDialog.id !== currentUser.id
                                            ">
                                            {{ t(item.name) }}
                                        </SelectItem>
                                    </SelectContent>
                                </Select>
                            </template>
                            <Button
                                variant="outline"
                                size="sm"
                                v-if="userDialogGroupEditMode"
                                @click="exitEditModeCurrentUserGroups">
                                {{ t('dialog.user.groups.exit_edit_mode') }}
                            </Button>
                            <Button
                                size="sm"
                                variant="outline"
                                v-else-if="currentUser.id === userDialog.id"
                                class="ml-2"
                                @click="editModeCurrentUserGroups">
                                {{ t('dialog.user.groups.edit_mode') }}
                            </Button>
                        </div>
                    </div>
                    <div v-loading="userDialog.isGroupsLoading" style="margin-top: 10px">
                        <template v-if="userDialogGroupEditMode">
                            <div class="x-friend-list" style="margin-top: 10px; margin-bottom: 15px; max-height: unset">
                                <!-- Bulk actions dropdown (shown only in edit mode) -->
                                <Select :model-value="bulkGroupActionValue" @update:modelValue="handleBulkGroupAction">
                                    <SelectTrigger size="sm" style="margin-right: 5px; margin-bottom: 5px" @click.stop>
                                        <SelectValue :placeholder="t('dialog.group.actions.manage_selected')" />
                                    </SelectTrigger>
                                    <SelectContent>
                                        <SelectItem value="visibility:visible">
                                            {{ t('dialog.group.actions.visibility_everyone') }}
                                        </SelectItem>
                                        <SelectItem value="visibility:friends">
                                            {{ t('dialog.group.actions.visibility_friends') }}
                                        </SelectItem>
                                        <SelectItem value="visibility:hidden">
                                            {{ t('dialog.group.actions.visibility_hidden') }}
                                        </SelectItem>
                                        <SelectItem value="leave">
                                            {{ t('dialog.user.groups.leave_group_tooltip') }}
                                        </SelectItem>
                                    </SelectContent>
                                </Select>

                                <!-- Select All button -->
                                <Button
                                    size="sm"
                                    variant="outline"
                                    style="padding: 7px 15px; margin-bottom: 5px"
                                    @click="selectAllGroups">
                                    {{
                                        userDialogGroupAllSelected
                                            ? t('dialog.group.actions.deselect_all')
                                            : t('dialog.group.actions.select_all')
                                    }}
                                </Button>

                                <div
                                    v-for="group in userDialogGroupEditGroups"
                                    :key="group.id"
                                    class="x-friend-item x-friend-item-border"
                                    style="width: 100%"
                                    @click="showGroupDialog(group.id)">
                                    <!-- Manual checkbox -->
                                    <div
                                        style="
                                            margin-left: 5px;
                                            margin-right: 5px;
                                            transform: scale(0.8);
                                            transform-origin: left center;
                                        "
                                        @click.stop>
                                        <Checkbox
                                            :model-value="userDialogGroupEditSelectedGroupIds.includes(group.id)"
                                            @update:modelValue="() => toggleGroupSelection(group.id)" />
                                    </div>

                                    <div style="margin-right: 3px; margin-left: 5px" @click.stop>
                                        <Button
                                            size="icon-sm"
                                            variant="outline"
                                            style="
                                                display: block;
                                                padding: 7px;
                                                font-size: 9px;
                                                margin-left: 0;
                                                rotate: 180deg;
                                            "
                                            @click="moveGroupTop(group.id)">
                                            <DownloadIcon />
                                        </Button>
                                        <Button
                                            size="icon-sm"
                                            variant="outline"
                                            style="display: block; padding: 7px; font-size: 9px; margin-left: 0"
                                            @click="moveGroupBottom(group.id)">
                                            <DownloadIcon />
                                        </Button>
                                    </div>
                                    <div style="margin-right: 10px" @click.stop>
                                        <Button
                                            size="icon-sm"
                                            variant="outline"
                                            style="display: block; padding: 7px; font-size: 9px; margin-left: 0"
                                            @click="moveGroupUp(group.id)">
                                            <ArrowUp />
                                        </Button>
                                        <Button
                                            size="icon-sm"
                                            variant="outline"
                                            style="display: block; padding: 7px; font-size: 9px; margin-left: 0"
                                            @click="moveGroupDown(group.id)">
                                            <ArrowDown />
                                        </Button>
                                    </div>
                                    <div class="avatar">
                                        <img :src="group.iconUrl" loading="lazy" />
                                    </div>
                                    <div class="detail">
                                        <span class="name" v-text="group.name"></span>
                                        <span class="extra">
                                            <TooltipWrapper
                                                v-if="group.isRepresenting"
                                                side="top"
                                                :content="t('dialog.group.members.representing')">
                                                <Tag style="margin-right: 5px" />
                                            </TooltipWrapper>
                                            <TooltipWrapper v-if="group.myMember?.visibility !== 'visible'" side="top">
                                                <template #content>
                                                    <span
                                                        >{{ t('dialog.group.members.visibility') }}
                                                        {{ group.myMember.visibility }}</span
                                                    >
                                                </template>
                                                <Eye style="margin-right: 5px" />
                                            </TooltipWrapper>
                                            <span>({{ group.memberCount }})</span>
                                        </span>
                                    </div>
                                    <Select
                                        v-if="group.myMember?.visibility"
                                        :model-value="group.myMember.visibility"
                                        :disabled="group.privacy !== 'default'"
                                        @update:modelValue="(value) => setGroupVisibility(group.id, value)">
                                        <SelectTrigger size="sm" @click.stop>
                                            <SelectValue
                                                :placeholder="
                                                    group.myMember.visibility === 'visible'
                                                        ? t('dialog.group.tags.visible')
                                                        : group.myMember.visibility === 'friends'
                                                          ? t('dialog.group.tags.friends')
                                                          : group.myMember.visibility === 'hidden'
                                                            ? t('dialog.group.tags.hidden')
                                                            : group.myMember.visibility
                                                " />
                                        </SelectTrigger>
                                        <SelectContent>
                                            <SelectItem value="visible">
                                                {{ t('dialog.group.actions.visibility_everyone') }}
                                            </SelectItem>
                                            <SelectItem value="friends">
                                                {{ t('dialog.group.actions.visibility_friends') }}
                                            </SelectItem>
                                            <SelectItem value="hidden">
                                                {{ t('dialog.group.actions.visibility_hidden') }}
                                            </SelectItem>
                                        </SelectContent>
                                    </Select>
                                    <!--//- JSON is missing isSubscribedToAnnouncements, can't be implemented-->
                                    <!-- <Button size="sm" variant="outline"
                                        @click.stop="
                                            setGroupSubscription(group.id, !group.myMember.isSubscribedToAnnouncements)
                                        ">
                                        <span v-if="group.myMember.isSubscribedToAnnouncements"
                                            ><BellOff style="margin-left: 5px" />
                                            {{ t('dialog.group.tags.subscribed') }}</span
                                        >
                                        <span v-else
                                            ><Bell style="margin-left: 5px" />
                                            {{ t('dialog.group.tags.unsubscribed') }}</span
                                        >
                                    </Button> -->
                                    <TooltipWrapper side="right" :content="t('dialog.user.groups.leave_group_tooltip')">
                                        <Button
                                            class="rounded-full h-6 w-6"
                                            size="icon-sm"
                                            variant="outline"
                                            v-if="shiftHeld"
                                            style="color: var(--el-color-danger); margin-left: 5px"
                                            @click.stop="leaveGroup(group.id)">
                                            <LogOut />
                                        </Button>
                                        <Button
                                            class="rounded-full h-6 w-6 text-red-600"
                                            size="icon-sm"
                                            variant="outline"
                                            v-else
                                            style="margin-left: 5px"
                                            @click.stop="leaveGroupPrompt(group.id)">
                                            <LogOut />
                                        </Button>
                                    </TooltipWrapper>
                                </div>
                            </div>
                        </template>
                        <template v-else>
                            <template v-if="userGroups.ownGroups.length > 0">
                                <span style="font-weight: bold; font-size: 16px">{{
                                    t('dialog.user.groups.own_groups')
                                }}</span>
                                <span style="color: var(--el-text-color-secondary); font-size: 12px; margin-left: 5px"
                                    >{{ userGroups.ownGroups.length }}/{{
                                        cachedConfig?.constants?.GROUPS?.MAX_OWNED
                                    }}</span
                                >
                                <div
                                    class="x-friend-list"
                                    style="margin-top: 10px; margin-bottom: 15px; min-height: 60px">
                                    <div
                                        v-for="group in userGroups.ownGroups"
                                        :key="group.id"
                                        class="x-friend-item x-friend-item-border"
                                        @click="showGroupDialog(group.id)">
                                        <div class="avatar">
                                            <img :src="group.iconUrl" loading="lazy" />
                                        </div>
                                        <div class="detail">
                                            <span class="name" v-text="group.name"></span>
                                            <span class="extra">
                                                <TooltipWrapper
                                                    v-if="group.isRepresenting"
                                                    side="top"
                                                    :content="t('dialog.group.members.representing')">
                                                    <Tag style="margin-right: 5px" />
                                                </TooltipWrapper>
                                                <TooltipWrapper v-if="group.memberVisibility !== 'visible'" side="top">
                                                    <template #content>
                                                        <span
                                                            >{{ t('dialog.group.members.visibility') }}
                                                            {{ group.memberVisibility }}</span
                                                        >
                                                    </template>
                                                    <Eye style="margin-right: 5px" />
                                                </TooltipWrapper>
                                                <span>({{ group.memberCount }})</span>
                                            </span>
                                        </div>
                                    </div>
                                </div>
                            </template>
                            <template v-if="userGroups.mutualGroups.length > 0">
                                <span style="font-weight: bold; font-size: 16px">{{
                                    t('dialog.user.groups.mutual_groups')
                                }}</span>
                                <span
                                    style="color: var(--el-text-color-secondary); font-size: 12px; margin-left: 5px"
                                    >{{ userGroups.mutualGroups.length }}</span
                                >
                                <div
                                    class="x-friend-list"
                                    style="margin-top: 10px; margin-bottom: 15px; min-height: 60px">
                                    <div
                                        v-for="group in userGroups.mutualGroups"
                                        :key="group.id"
                                        class="x-friend-item x-friend-item-border"
                                        @click="showGroupDialog(group.id)">
                                        <div class="avatar">
                                            <img :src="group.iconUrl" loading="lazy" />
                                        </div>
                                        <div class="detail">
                                            <span class="name" v-text="group.name"></span>
                                            <span class="extra">
                                                <TooltipWrapper
                                                    v-if="group.isRepresenting"
                                                    side="top"
                                                    :content="t('dialog.group.members.representing')">
                                                    <Tag style="margin-right: 5px" />
                                                </TooltipWrapper>
                                                <TooltipWrapper v-if="group.memberVisibility !== 'visible'" side="top">
                                                    <template #content>
                                                        <span
                                                            >{{ t('dialog.group.members.visibility') }}
                                                            {{ group.memberVisibility }}</span
                                                        >
                                                    </template>
                                                    <Eye style="margin-right: 5px" />
                                                </TooltipWrapper>
                                                <span>({{ group.memberCount }})</span>
                                            </span>
                                        </div>
                                    </div>
                                </div>
                            </template>
                            <template v-if="userGroups.remainingGroups.length > 0">
                                <span style="font-weight: bold; font-size: 16px">{{
                                    t('dialog.user.groups.groups')
                                }}</span>
                                <span style="color: var(--el-text-color-secondary); font-size: 12px; margin-left: 5px">
                                    {{ userGroups.remainingGroups.length }}
                                    <template v-if="currentUser.id === userDialog.id">
                                        /
                                        <template v-if="isLocalUserVrcPlusSupporter">
                                            {{ cachedConfig?.constants?.GROUPS?.MAX_JOINED_PLUS }}
                                        </template>
                                        <template v-else>
                                            {{ cachedConfig?.constants?.GROUPS?.MAX_JOINED }}
                                        </template>
                                    </template>
                                </span>
                                <div
                                    class="x-friend-list"
                                    style="margin-top: 10px; margin-bottom: 15px; min-height: 60px">
                                    <div
                                        v-for="group in userGroups.remainingGroups"
                                        :key="group.id"
                                        class="x-friend-item x-friend-item-border"
                                        @click="showGroupDialog(group.id)">
                                        <div class="avatar">
                                            <img :src="group.iconUrl" loading="lazy" />
                                        </div>
                                        <div class="detail">
                                            <span class="name" v-text="group.name"></span>
                                            <span class="extra">
                                                <TooltipWrapper
                                                    v-if="group.isRepresenting"
                                                    side="top"
                                                    :content="t('dialog.group.members.representing')">
                                                    <Tag style="margin-right: 5px" />
                                                </TooltipWrapper>
                                                <TooltipWrapper v-if="group.memberVisibility !== 'visible'" side="top">
                                                    <template #content>
                                                        <span
                                                            >{{ t('dialog.group.members.visibility') }}
                                                            {{ group.memberVisibility }}</span
                                                        >
                                                    </template>
                                                    <Eye style="margin-right: 5px" />
                                                </TooltipWrapper>
                                                <span>({{ group.memberCount }})</span>
                                            </span>
                                        </div>
                                    </div>
                                </div>
                            </template>
                        </template>
                    </div>
                </template>

                <template #Worlds>
                    <div style="display: flex; align-items: center; justify-content: space-between">
                        <div style="display: flex; align-items: center">
                            <Button
                                class="rounded-full"
                                variant="outline"
                                size="icon-sm"
                                :disabled="userDialog.isWorldsLoading"
                                @click="refreshUserDialogWorlds()">
                                <Spinner v-if="userDialog.isWorldsLoading" />
                                <RefreshCw v-else />
                            </Button>
                            <span style="margin-left: 5px">{{
                                t('dialog.user.worlds.total_count', { count: userDialog.worlds.length })
                            }}</span>
                        </div>
                        <div style="display: flex; align-items: center">
                            <span class="mr-1">{{ t('dialog.user.worlds.sort_by') }}</span>
                            <Select
                                :model-value="userDialogWorldSortingKey"
                                :disabled="userDialog.isWorldsLoading"
                                @update:modelValue="setUserDialogWorldSortingByKey">
                                <SelectTrigger size="sm" @click.stop>
                                    <SelectValue />
                                </SelectTrigger>
                                <SelectContent>
                                    <SelectItem
                                        v-for="(item, key) in userDialogWorldSortingOptions"
                                        :key="String(key)"
                                        :value="String(key)">
                                        {{ t(item.name) }}
                                    </SelectItem>
                                </SelectContent>
                            </Select>
                            <span class="ml-2 mr-1">{{ t('dialog.user.worlds.order_by') }}</span>
                            <Select
                                :model-value="userDialogWorldOrderKey"
                                :disabled="userDialog.isWorldsLoading"
                                @update:modelValue="setUserDialogWorldOrderByKey">
                                <SelectTrigger size="sm" @click.stop>
                                    <SelectValue />
                                </SelectTrigger>
                                <SelectContent>
                                    <SelectItem
                                        v-for="(item, key) in userDialogWorldOrderOptions"
                                        :key="String(key)"
                                        :value="String(key)">
                                        {{ t(item.name) }}
                                    </SelectItem>
                                </SelectContent>
                            </Select>
                        </div>
                    </div>
                    <div
                        v-loading="userDialog.isWorldsLoading"
                        class="x-friend-list"
                        style="margin-top: 10px; min-height: 60px">
                        <div
                            v-for="world in userDialog.worlds"
                            :key="world.id"
                            class="x-friend-item x-friend-item-border"
                            @click="showWorldDialog(world.id)">
                            <div class="avatar">
                                <img :src="world.thumbnailImageUrl" loading="lazy" />
                            </div>
                            <div class="detail">
                                <span class="name" v-text="world.name"></span>
                                <span v-if="world.occupants" class="extra">({{ world.occupants }})</span>
                            </div>
                        </div>
                    </div>
                </template>

                <template #favorite-worlds>
                    <!-- <Button
                        variant="outline"
                        v-if="userFavoriteWorlds && userFavoriteWorlds.length > 0"
                        type="default"
                        :loading="userDialog.isFavoriteWorldsLoading"
                        size="small"
                        :icon="RefreshCw"
                        circle
                        style="position: absolute; right: 15px; bottom: 15px; z-index: 99"
                        @click="getUserFavoriteWorlds(userDialog.id)">
                    </Button> -->
                    <template v-if="userFavoriteWorlds && userFavoriteWorlds.length > 0">
                        <TabsUnderline
                            v-model="favoriteWorldsTab"
                            :items="favoriteWorldTabs"
                            :unmount-on-hide="false"
                            v-loading="userDialog.isFavoriteWorldsLoading"
                            class="zero-margin-tabs"
                            style="margin-top: 10px; height: 50vh">
                            <template
                                v-for="(list, index) in userFavoriteWorlds"
                                :key="`favorite-worlds-label-${index}`"
                                v-slot:[`label-${index}`]>
                                <span>
                                    <i
                                        class="x-status-icon"
                                        style="margin-right: 6px"
                                        :class="userFavoriteWorldsStatus(list[1])">
                                    </i>
                                    <span style="font-weight: bold; font-size: 14px" v-text="list[0]"></span>
                                    <span
                                        style="color: var(--el-text-color-secondary); font-size: 10px; margin-left: 5px"
                                        >{{ list[2].length }}/{{ favoriteLimits.maxFavoritesPerGroup.world }}</span
                                    >
                                </span>
                            </template>
                            <template
                                v-for="(list, index) in userFavoriteWorlds"
                                :key="`favorite-worlds-content-${index}`"
                                v-slot:[String(index)]>
                                <div
                                    class="x-friend-list"
                                    style="margin-top: 10px; margin-bottom: 15px; min-height: 60px; max-height: none">
                                    <div
                                        v-for="world in list[2]"
                                        :key="world.favoriteId"
                                        class="x-friend-item x-friend-item-border"
                                        @click="showWorldDialog(world.id)">
                                        <div class="avatar">
                                            <img :src="world.thumbnailImageUrl" loading="lazy" />
                                        </div>
                                        <div class="detail">
                                            <span class="name" v-text="world.name"></span>
                                            <span v-if="world.occupants" class="extra">({{ world.occupants }})</span>
                                        </div>
                                    </div>
                                </div>
                            </template>
                        </TabsUnderline>
                    </template>
                    <template v-else-if="!userDialog.isFavoriteWorldsLoading">
                        <div style="display: flex; justify-content: center; align-items: center; height: 100%">
                            <span style="font-size: 16px">No favorite worlds found.</span>
                        </div>
                    </template>
                </template>

                <template #Avatars>
                    <div style="display: flex; align-items: center; justify-content: space-between">
                        <div style="display: flex; align-items: center">
                            <Button
                                v-if="userDialog.ref.id === currentUser.id"
                                class="rounded-full"
                                variant="outline"
                                size="icon-sm"
                                :disabled="userDialog.isAvatarsLoading"
                                @click="refreshUserDialogAvatars()">
                                <Spinner v-if="userDialog.isAvatarsLoading" />
                                <RefreshCw v-else />
                            </Button>
                            <Button
                                v-else
                                class="rounded-full"
                                variant="outline"
                                size="icon-sm"
                                :disabled="userDialog.isAvatarsLoading"
                                @click="setUserDialogAvatarsRemote(userDialog.id)">
                                <Spinner v-if="userDialog.isAvatarsLoading" />
                                <RefreshCw v-else />
                            </Button>
                            <span style="margin-left: 5px">{{
                                t('dialog.user.avatars.total_count', { count: userDialogAvatars.length })
                            }}</span>
                        </div>
                        <div class="flex items-center">
                            <template v-if="userDialog.ref.id === currentUser.id">
                                <span class="mr-1">{{ t('dialog.user.avatars.sort_by') }}</span>
                                <Select
                                    :model-value="userDialog.avatarSorting"
                                    :disabled="userDialog.isWorldsLoading"
                                    @update:modelValue="changeUserDialogAvatarSorting">
                                    <SelectTrigger size="sm" @click.stop>
                                        <SelectValue
                                            :placeholder="
                                                t(`dialog.user.avatars.sort_by_${userDialog.avatarSorting}`)
                                            " />
                                    </SelectTrigger>
                                    <SelectContent>
                                        <SelectItem value="name">{{
                                            t('dialog.user.avatars.sort_by_name')
                                        }}</SelectItem>
                                        <SelectItem value="update">{{
                                            t('dialog.user.avatars.sort_by_update')
                                        }}</SelectItem>
                                        <SelectItem value="createdAt">{{
                                            t('dialog.user.avatars.sort_by_uploaded')
                                        }}</SelectItem>
                                    </SelectContent>
                                </Select>
                                <span class="ml-2 mr-1">{{ t('dialog.user.avatars.group_by') }}</span>
                                <Select
                                    :model-value="userDialog.avatarReleaseStatus"
                                    :disabled="userDialog.isWorldsLoading"
                                    @update:modelValue="(value) => (userDialog.avatarReleaseStatus = value)">
                                    <SelectTrigger size="sm" @click.stop>
                                        <SelectValue
                                            :placeholder="t(`dialog.user.avatars.${userDialog.avatarReleaseStatus}`)" />
                                    </SelectTrigger>
                                    <SelectContent>
                                        <SelectItem value="all">{{ t('dialog.user.avatars.all') }}</SelectItem>
                                        <SelectItem value="public">{{ t('dialog.user.avatars.public') }}</SelectItem>
                                        <SelectItem value="private">{{ t('dialog.user.avatars.private') }}</SelectItem>
                                    </SelectContent>
                                </Select>
                            </template>
                        </div>
                    </div>
                    <div class="x-friend-list" style="margin-top: 10px; min-height: 60px; max-height: 50vh">
                        <div
                            v-for="avatar in userDialogAvatars"
                            :key="avatar.id"
                            class="x-friend-item x-friend-item-border"
                            @click="showAvatarDialog(avatar.id)">
                            <div class="avatar">
                                <img v-if="avatar.thumbnailImageUrl" :src="avatar.thumbnailImageUrl" loading="lazy" />
                            </div>
                            <div class="detail">
                                <span class="name" v-text="avatar.name"></span>
                                <span
                                    v-if="avatar.releaseStatus === 'public'"
                                    class="extra"
                                    style="color: var(--el-color-success)"
                                    v-text="avatar.releaseStatus">
                                </span>
                                <span
                                    v-else-if="avatar.releaseStatus === 'private'"
                                    class="extra"
                                    style="color: var(--el-color-danger)"
                                    v-text="avatar.releaseStatus">
                                </span>
                                <span v-else class="extra" v-text="avatar.releaseStatus"></span>
                            </div>
                        </div>
                    </div>
                </template>

                <template #JSON>
                    <Button
                        class="rounded-full h-6 w-6 mr-2"
                        size="icon-sm"
                        variant="outline"
                        @click="refreshUserDialogTreeData()">
                        <RefreshCcw />
                    </Button>
                    <Button
                        class="rounded-full h-6 w-6"
                        size="icon-sm"
                        variant="outline"
                        @click="downloadAndSaveJson(userDialog.id, userDialog.ref)">
                        <Download />
                    </Button>
                    <vue-json-pretty
                        :data="userDialog.treeData"
                        :deep="2"
                        :theme="isDarkMode ? 'dark' : 'light'"
                        show-icon />
                </template>
            </TabsUnderline>
        </div>
        <SendInviteDialog
            v-model:sendInviteDialogVisible="sendInviteDialogVisible"
            v-model:sendInviteDialog="sendInviteDialog"
            @closeInviteDialog="closeInviteDialog" />
        <SendInviteRequestDialog
            v-model:sendInviteRequestDialogVisible="sendInviteRequestDialogVisible"
            v-model:sendInviteDialog="sendInviteDialog"
            @closeInviteDialog="closeInviteDialog" />
        <PreviousInstancesUserDialog v-model:previous-instances-user-dialog="previousInstancesUserDialog" />
        <SocialStatusDialog
            :social-status-dialog="socialStatusDialog"
            :social-status-history-table="socialStatusHistoryTable" />
        <LanguageDialog />
        <BioDialog :bio-dialog="bioDialog" />
        <PronounsDialog :pronouns-dialog="pronounsDialog" />
        <ModerateGroupDialog />
        <EditNoteAndMemoDialog v-model:visible="isEditNoteAndMemoDialogVisible" />
    </el-dialog>
</template>

<script setup>
    import {
        AlertTriangle,
        ArrowDown,
        ArrowUp,
        Copy,
        Download,
        DownloadIcon,
        Eye,
        Languages,
        Loader2,
        LogOut,
        MoreHorizontal,
        Pencil,
        RefreshCcw,
        RefreshCw,
        Tag,
        Trash2
    } from 'lucide-vue-next';
    import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
    import { computed, defineAsyncComponent, nextTick, ref, watch } from 'vue';
    import {
        DropdownMenu,
        DropdownMenuContent,
        DropdownMenuItem,
        DropdownMenuTrigger
    } from '@/components/ui/dropdown-menu';
    import { Button } from '@/components/ui/button';
    import { Checkbox } from '@/components/ui/checkbox';
    import { Spinner } from '@/components/ui/spinner';
    import { TabsUnderline } from '@/components/ui/tabs';
    import { storeToRefs } from 'pinia';
    import { toast } from 'vue-sonner';
    import { useI18n } from 'vue-i18n';

    import VueJsonPretty from 'vue-json-pretty';

    import {
        compareByDisplayName,
        compareByFriendOrder,
        compareByLastActiveRef,
        compareByMemberCount,
        compareByName,
        copyToClipboard,
        downloadAndSaveJson,
        formatDateFilter,
        getFaviconUrl,
        isFriendOnline,
        isRealInstance,
        openExternalLink,
        parseLocation,
        refreshInstancePlayerCount,
        timeToText,
        userImage,
        userOnlineFor,
        userOnlineForTimestamp,
        userStatusClass
    } from '../../../shared/utils';
    import {
        useAdvancedSettingsStore,
        useAppearanceSettingsStore,
        useAuthStore,
        useAvatarStore,
        useFavoriteStore,
        useFriendStore,
        useGalleryStore,
        useGroupStore,
        useInviteStore,
        useLocationStore,
        useModalStore,
        useModerationStore,
        useUiStore,
        useUserStore,
        useWorldStore
    } from '../../../stores';
    import {
        favoriteRequest,
        friendRequest,
        groupRequest,
        miscRequest,
        notificationRequest,
        playerModerationRequest,
        userRequest,
        worldRequest
    } from '../../../api';
    import { processBulk, request } from '../../../service/request';
    import { userDialogGroupSortingOptions, userDialogMutualFriendSortingOptions } from '../../../shared/constants';
    import { userDialogWorldOrderOptions, userDialogWorldSortingOptions } from '../../../shared/constants/';
    import { database } from '../../../service/database';
    import { getNextDialogIndex } from '../../../shared/utils/base/ui';

    import SendInviteDialog from '../InviteDialog/SendInviteDialog.vue';
    import UserSummaryHeader from './UserSummaryHeader.vue';

    const BioDialog = defineAsyncComponent(() => import('./BioDialog.vue'));
    const LanguageDialog = defineAsyncComponent(() => import('./LanguageDialog.vue'));
    const PreviousInstancesUserDialog = defineAsyncComponent(() => import('./PreviousInstancesUserDialog.vue'));
    const PronounsDialog = defineAsyncComponent(() => import('./PronounsDialog.vue'));
    const SendInviteRequestDialog = defineAsyncComponent(() => import('./SendInviteRequestDialog.vue'));
    const SocialStatusDialog = defineAsyncComponent(() => import('./SocialStatusDialog.vue'));
    const ModerateGroupDialog = defineAsyncComponent(() => import('../ModerateGroupDialog.vue'));
    const EditNoteAndMemoDialog = defineAsyncComponent(() => import('./EditNoteAndMemoDialog.vue'));

    const { t } = useI18n();
    const userDialogTabs = computed(() => {
        const tabs = [
            { value: 'Info', label: t('dialog.user.info.header') },
            { value: 'Groups', label: t('dialog.user.groups.header') },
            { value: 'Worlds', label: t('dialog.user.worlds.header') },
            { value: 'favorite-worlds', label: t('dialog.user.favorite_worlds.header') },
            { value: 'Avatars', label: t('dialog.user.avatars.header') },
            { value: 'JSON', label: t('dialog.user.json.header') }
        ];
        if (userDialog.value.id !== currentUser.value.id && !currentUser.value.hasSharedConnectionsOptOut) {
            tabs.splice(1, 0, { value: 'mutual', label: t('dialog.user.mutual_friends.header') });
        }
        return tabs;
    });
    const favoriteWorldTabs = computed(() =>
        (userFavoriteWorlds.value || []).map((list, index) => ({
            value: String(index),
            label: list?.[0] ?? ''
        }))
    );

    const modalStore = useModalStore();

    const { hideUserNotes, hideUserMemos, isDarkMode } = storeToRefs(useAppearanceSettingsStore());
    const { bioLanguage, avatarRemoteDatabase, translationApi, translationApiType } =
        storeToRefs(useAdvancedSettingsStore());
    const { translateText } = useAdvancedSettingsStore();
    const { userDialog, languageDialog, currentUser, isLocalUserVrcPlusSupporter } = storeToRefs(useUserStore());
    const {
        cachedUsers,
        showUserDialog,
        sortUserDialogAvatars,
        refreshUserDialogAvatars,
        refreshUserDialogTreeData,
        showSendBoopDialog,
        toggleSharedConnectionsOptOut
    } = useUserStore();
    const { favoriteLimits } = storeToRefs(useFavoriteStore());
    const { showFavoriteDialog, handleFavoriteWorldList } = useFavoriteStore();
    const { showAvatarDialog, lookupAvatars, showAvatarAuthorDialog } = useAvatarStore();
    const { cachedAvatars } = useAvatarStore();
    const { cachedWorlds, showWorldDialog } = useWorldStore();
    const {
        showGroupDialog,
        applyGroup,
        saveCurrentUserGroups,
        updateInGameGroupOrder,
        leaveGroup,
        leaveGroupPrompt,
        setGroupVisibility,
        handleGroupList,
        showModerateGroupDialog
    } = useGroupStore();
    const { currentUserGroups, inviteGroupDialog, inGameGroupOrder } = storeToRefs(useGroupStore());
    const { lastLocation, lastLocationDestination } = storeToRefs(useLocationStore());
    const { refreshInviteMessageTableData } = useInviteStore();
    const { friendLogTable } = storeToRefs(useFriendStore());
    const { getFriendRequest, handleFriendDelete } = useFriendStore();
    const { clearInviteImageUpload, showFullscreenImageDialog, showGalleryPage } = useGalleryStore();

    const { logout } = useAuthStore();
    const { cachedConfig } = storeToRefs(useAuthStore());
    const { applyPlayerModeration, handlePlayerModerationDelete } = useModerationStore();
    const { shiftHeld } = storeToRefs(useUiStore());

    watch(
        () => userDialog.value.loading,
        () => {
            if (userDialog.value.visible) {
                nextTick(() => {
                    userDialogIndex.value = getNextDialogIndex();
                });
                !userDialog.value.loading && loadLastActiveTab();
                if (userDialog.value.id !== bioCache.value.userId) {
                    bioCache.value = {
                        userId: null,
                        translated: null
                    };
                }
            }
        }
    );

    const userDialogIndex = ref(2000);

    const userDialogGroupEditMode = ref(false); // whether edit mode is active
    const userDialogGroupEditGroups = ref([]); // editable group list
    const userDialogGroupAllSelected = ref(false);
    const userDialogGroupEditSelectedGroupIds = ref([]); // selected groups in edit mode

    const userDialogLastActiveTab = ref('Info');
    const userDialogLastMutualFriends = ref('');
    const userDialogLastGroup = ref('');
    const userDialogLastAvatar = ref('');
    const userDialogLastWorld = ref('');
    const userDialogLastFavoriteWorld = ref('');
    const userFavoriteWorlds = ref([]);
    const userGroups = ref({
        groups: [],
        ownGroups: [],
        mutualGroups: [],
        remainingGroups: []
    });

    const favoriteWorldsTab = ref('0');

    const sendInviteDialogVisible = ref(false);
    const sendInviteDialog = ref({
        messageSlot: {},
        userId: '',
        params: {}
    });
    const sendInviteRequestDialogVisible = ref(false);

    const previousInstancesUserDialog = ref({
        visible: false,
        openFlg: false,
        userRef: {}
    });

    const socialStatusDialog = ref({
        visible: false,
        loading: false,
        status: '',
        statusDescription: ''
    });
    const socialStatusHistoryTable = ref({
        data: [],
        tableProps: {
            stripe: true,
            size: 'small'
        },
        layout: 'table'
    });

    const bioDialog = ref({
        visible: false,
        loading: false,
        bio: '',
        bioLinks: []
    });

    const translateLoading = ref(false);

    const pronounsDialog = ref({
        visible: false,
        loading: false,
        pronouns: ''
    });

    const bioCache = ref({
        userId: null,
        translated: null
    });

    const isEditNoteAndMemoDialogVisible = ref(false);
    const vrchatCredit = ref(null);
    const mutualFriendsError = ref(false);

    const userDialogAvatars = computed(() => {
        const { avatars, avatarReleaseStatus } = userDialog.value;
        if (avatarReleaseStatus === 'public' || avatarReleaseStatus === 'private') {
            return avatars.filter((avatar) => avatar.releaseStatus === avatarReleaseStatus);
        }
        return avatars;
    });

    function userFavoriteWorldsStatus(visibility) {
        const style = {};
        if (visibility === 'public') {
            style.green = true;
        } else if (visibility === 'friends') {
            style.blue = true;
        } else {
            style.red = true;
        }
        return style;
    }

    function getUserStateText(user) {
        let state = '';
        if (user.state === 'active') {
            state = t('dialog.user.status.active');
        } else if (user.state === 'offline') {
            state = t('dialog.user.status.offline');
        } else {
            return getUserStatusText(user.status);
        }
        if (user.status && user.status !== 'active') {
            state += ` (${getUserStatusText(user.status)})`;
        }
        return state;
    }

    function getUserStatusText(status) {
        if (status === 'active') {
            return t('dialog.user.status.active');
        }
        if (status === 'join me') {
            return t('dialog.user.status.join_me');
        }
        if (status === 'ask me') {
            return t('dialog.user.status.ask_me');
        }
        if (status === 'busy') {
            return t('dialog.user.status.busy');
        }
        return t('dialog.user.status.offline');
    }

    function handleUserDialogTab(tabName) {
        const userId = userDialog.value.id;
        if (tabName === 'Info') {
            if (vrchatCredit.value === null) {
                getVRChatCredits();
            }
        } else if (tabName === 'mutual') {
            if (userId === currentUser.value.id) {
                userDialogLastActiveTab.value = 'Info';
                return;
            }
            if (userDialogLastMutualFriends.value !== userId) {
                userDialogLastMutualFriends.value = userId;
                getUserMutualFriends(userId);
            }
        } else if (tabName === 'Groups') {
            if (userDialogLastGroup.value !== userId) {
                userDialogLastGroup.value = userId;
                getUserGroups(userId);
            }
        } else if (tabName === 'Avatars') {
            setUserDialogAvatars(userId);
            if (userDialogLastAvatar.value !== userId) {
                userDialogLastAvatar.value = userId;
                if (userId === currentUser.value.id) {
                    refreshUserDialogAvatars();
                } else {
                    setUserDialogAvatarsRemote(userId);
                }
            }
        } else if (tabName === 'Worlds') {
            setUserDialogWorlds(userId);
            if (userDialogLastWorld.value !== userId) {
                userDialogLastWorld.value = userId;
                refreshUserDialogWorlds();
            }
        } else if (tabName === 'favorite-worlds') {
            if (userDialogLastFavoriteWorld.value !== userId) {
                userDialogLastFavoriteWorld.value = userId;
                getUserFavoriteWorlds(userId);
            }
        } else if (tabName === 'JSON') {
            refreshUserDialogTreeData();
        }
    }

    function loadLastActiveTab() {
        handleUserDialogTab(userDialogLastActiveTab.value);
    }

    function userDialogTabClick(tabName) {
        if (tabName === userDialogLastActiveTab.value) {
            return;
        }
        handleUserDialogTab(tabName);
    }

    function showPronounsDialog() {
        const D = pronounsDialog.value;
        D.pronouns = currentUser.value.pronouns;
        D.visible = true;
    }

    function showLanguageDialog() {
        const D = languageDialog.value;
        D.visible = true;
    }

    function showSocialStatusDialog() {
        const D = socialStatusDialog.value;
        const { statusHistory } = currentUser.value;
        const statusHistoryArray = [];
        for (let i = 0; i < statusHistory.length; ++i) {
            const addStatus = {
                no: i + 1,
                status: statusHistory[i]
            };
            statusHistoryArray.push(addStatus);
        }
        socialStatusHistoryTable.value.data = statusHistoryArray;
        D.status = currentUser.value.status;
        D.statusDescription = currentUser.value.statusDescription;
        D.visible = true;
    }

    async function setUserDialogAvatarsRemote(userId) {
        if (avatarRemoteDatabase.value && userId !== currentUser.value.id) {
            userDialog.value.isAvatarsLoading = true;
            const data = await lookupAvatars('authorId', userId);
            const avatars = new Set();
            userDialogAvatars.value.forEach((avatar) => {
                avatars.add(avatar.id);
            });
            if (data && typeof data === 'object') {
                data.forEach((avatar) => {
                    if (avatar.id && !avatars.has(avatar.id)) {
                        if (avatar.authorId === userId) {
                            userDialog.value.avatars.push(avatar);
                        } else {
                            console.error(`Avatar authorId mismatch for ${avatar.id} - ${avatar.name}`);
                        }
                    }
                });
            }
            userDialog.value.avatarSorting = 'name';
            userDialog.value.avatarReleaseStatus = 'all';
            userDialog.value.isAvatarsLoading = false;
        }
        sortUserDialogAvatars(userDialog.value.avatars);
    }

    async function toggleBadgeVisibility(badge) {
        if (badge.hidden) {
            badge.showcased = false;
        }
        const args = await miscRequest.updateBadge({
            badgeId: badge.badgeId,
            hidden: badge.hidden,
            showcased: badge.showcased
        });
        handleBadgeUpdate(args);
    }

    async function toggleBadgeShowcased(badge) {
        if (badge.showcased) {
            badge.hidden = false;
        }
        const args = await miscRequest.updateBadge({
            badgeId: badge.badgeId,
            hidden: badge.hidden,
            showcased: badge.showcased
        });
        handleBadgeUpdate(args);
    }

    function handleBadgeUpdate(args) {
        if (args.json) {
            toast.success(t('message.badge.updated'));
        }
    }

    function setPlayerModeration(userId, type) {
        const D = userDialog.value;
        AppApi.SetVRChatUserModeration(currentUser.value.id, userId, type).then((result) => {
            if (result) {
                if (type === 4) {
                    D.isShowAvatar = false;
                    D.isHideAvatar = true;
                } else if (type === 5) {
                    D.isShowAvatar = true;
                    D.isHideAvatar = false;
                } else {
                    D.isShowAvatar = false;
                    D.isHideAvatar = false;
                }
            } else {
                toast.error(t('message.avatar.change_moderation_failed'));
            }
        });
    }

    function showSendInviteDialog(params, userId) {
        sendInviteDialog.value = {
            params,
            userId,
            messageSlot: {}
        };
        refreshInviteMessageTableData('message');
        clearInviteImageUpload();
        sendInviteDialogVisible.value = true;
    }

    function showSendInviteRequestDialog(params, userId) {
        sendInviteDialog.value = {
            params,
            userId,
            messageSlot: {}
        };
        refreshInviteMessageTableData('request');
        clearInviteImageUpload();
        sendInviteRequestDialogVisible.value = true;
    }

    function showInviteGroupDialog(groupId, userId) {
        inviteGroupDialog.value.groupId = groupId;
        inviteGroupDialog.value.userId = userId;
        inviteGroupDialog.value.visible = true;
    }

    function userDialogCommand(command) {
        let L;
        const D = userDialog.value;
        if (D.visible === false) {
            return;
        }
        if (command === 'RefreshCw') {
            showUserDialog(D.id);
        } else if (command === 'Share2') {
            copyUserURL(D.id);
        } else if (command === 'Add Favorite') {
            showFavoriteDialog('friend', D.id);
        } else if (command === 'Pencil Social Status') {
            showSocialStatusDialog();
        } else if (command === 'Pencil Language') {
            showLanguageDialog();
        } else if (command === 'Pencil Bio') {
            showBioDialog();
        } else if (command === 'Pencil Pronouns') {
            showPronounsDialog();
        } else if (command === 'Logout') {
            logout();
        } else if (command === 'Request Invite') {
            notificationRequest
                .sendRequestInvite(
                    {
                        platform: 'standalonewindows'
                    },
                    D.id
                )
                .then((args) => {
                    toast('Request invite sent');
                    return args;
                });
        } else if (command === 'Invite MessageSquare') {
            L = parseLocation(lastLocation.value.location);
            worldRequest
                .getCachedWorld({
                    worldId: L.worldId
                })
                .then((args) => {
                    showSendInviteDialog(
                        {
                            instanceId: lastLocation.value.location,
                            worldId: lastLocation.value.location,
                            worldName: args.ref.name
                        },
                        D.id
                    );
                });
        } else if (command === 'Request Invite MessageSquare') {
            showSendInviteRequestDialog(
                {
                    platform: 'standalonewindows'
                },
                D.id
            );
        } else if (command === 'Invite') {
            let currentLocation = lastLocation.value.location;
            if (lastLocation.value.location === 'traveling') {
                currentLocation = lastLocationDestination.value;
            }
            L = parseLocation(currentLocation);
            worldRequest
                .getCachedWorld({
                    worldId: L.worldId
                })
                .then((args) => {
                    notificationRequest
                        .sendInvite(
                            {
                                instanceId: L.tag,
                                worldId: L.tag,
                                worldName: args.ref.name
                            },
                            D.id
                        )
                        .then((_args) => {
                            toast('Invite sent');
                            return _args;
                        });
                });
        } else if (command === 'Show Avatar Author') {
            const { currentAvatarImageUrl } = D.ref;
            showAvatarAuthorDialog(D.id, D.$avatarInfo.ownerId, currentAvatarImageUrl);
        } else if (command === 'Show Fallback Avatar Details') {
            const { fallbackAvatar } = D.ref;
            if (fallbackAvatar) {
                showAvatarDialog(fallbackAvatar);
            } else {
                toast.error('No fallback avatar set');
            }
        } else if (command === 'Previous Instances') {
            showPreviousInstancesUserDialog(D.ref);
        } else if (command === 'Manage Gallery') {
            userDialog.value.visible = false;
            showGalleryPage();
        } else if (command === 'Invite To Group') {
            showInviteGroupDialog('', D.id);
        } else if (command === 'Send Boop') {
            showSendBoopDialog(D.id);
        } else if (command === 'Group Moderation') {
            showModerateGroupDialog(D.id);
        } else if (command === 'Hide Avatar') {
            if (D.isHideAvatar) {
                setPlayerModeration(D.id, 0);
            } else {
                setPlayerModeration(D.id, 4);
            }
        } else if (command === 'Show Avatar') {
            if (D.isShowAvatar) {
                setPlayerModeration(D.id, 0);
            } else {
                setPlayerModeration(D.id, 5);
            }
        } else if (command === 'Pencil Note Memo') {
            isEditNoteAndMemoDialogVisible.value = true;
        } else {
            const i18nPreFix = 'dialog.user.actions.';
            const formattedCommand = command.toLowerCase().replace(/ /g, '_');
            const displayCommandText = t(`${i18nPreFix}${formattedCommand}`).includes('i18nPreFix')
                ? command
                : t(`${i18nPreFix}${formattedCommand}`);

            modalStore
                .confirm({
                    description: t('confirm.message', {
                        command: displayCommandText
                    }),
                    title: t('confirm.title'),
                    confirmText: t('confirm.confirm_button'),
                    cancelText: t('confirm.cancel_button')
                })
                .then(({ ok }) => {
                    if (ok) {
                        performUserDialogCommand(command, D.id);
                    }
                })
                .catch(() => {});
        }
    }

    function handleSendFriendRequest(args) {
        const ref = cachedUsers.get(args.params.userId);
        if (typeof ref === 'undefined') {
            return;
        }
        const friendLogHistory = {
            created_at: new Date().toJSON(),
            type: 'FriendRequest',
            userId: ref.id,
            displayName: ref.displayName
        };
        friendLogTable.value.data.push(friendLogHistory);
        database.addFriendLogHistory(friendLogHistory);

        const D = userDialog.value;
        if (D.visible === false || D.id !== args.params.userId) {
            return;
        }
        if (args.json.success) {
            D.isFriend = true;
        } else {
            D.outgoingRequest = true;
        }
    }

    function handleCancelFriendRequest(args) {
        const ref = cachedUsers.get(args.params.userId);
        if (typeof ref === 'undefined') {
            return;
        }
        const friendLogHistory = {
            created_at: new Date().toJSON(),
            type: 'CancelFriendRequest',
            userId: ref.id,
            displayName: ref.displayName
        };
        friendLogTable.value.data.push(friendLogHistory);
        database.addFriendLogHistory(friendLogHistory);
        const D = userDialog.value;
        if (D.visible === false || D.id !== args.params.userId) {
            return;
        }
        D.outgoingRequest = false;
    }

    function handleSendPlayerModeration(args) {
        const ref = applyPlayerModeration(args.json);
        const D = userDialog.value;
        if (D.visible === false || (ref.targetUserId !== D.id && ref.sourceUserId !== currentUser.value.id)) {
            return;
        }
        if (ref.type === 'block') {
            D.isBlock = true;
        } else if (ref.type === 'mute') {
            D.isMute = true;
        } else if (ref.type === 'interactOff') {
            D.isInteractOff = true;
        } else if (ref.type === 'muteChat') {
            D.isMuteChat = true;
        }
        toast.success(t('message.user.moderated'));
    }

    async function performUserDialogCommand(command, userId) {
        let args;
        let key;
        switch (command) {
            case 'Trash2 Favorite':
                favoriteRequest.deleteFavorite({
                    objectId: userId
                });
                break;
            case 'Accept Friend Request':
                key = getFriendRequest(userId);
                if (key === '') {
                    const args = await friendRequest.sendFriendRequest({
                        userId
                    });
                    handleSendFriendRequest(args);
                } else {
                    notificationRequest.acceptFriendRequestNotification({
                        notificationId: key
                    });
                }
                break;
            case 'Decline Friend Request':
                key = getFriendRequest(userId);
                if (key === '') {
                    const args = await friendRequest.cancelFriendRequest({
                        userId
                    });
                    handleCancelFriendRequest(args);
                } else {
                    notificationRequest.hideNotification({
                        notificationId: key
                    });
                }
                break;
            case 'Cancel Friend Request': {
                args = await friendRequest.cancelFriendRequest({
                    userId
                });
                handleCancelFriendRequest(args);
                break;
            }
            case 'Send Friend Request': {
                args = await friendRequest.sendFriendRequest({
                    userId
                });
                handleSendFriendRequest(args);
                break;
            }
            case 'Moderation Unblock':
                args = await playerModerationRequest.deletePlayerModeration({
                    moderated: userId,
                    type: 'block'
                });
                handlePlayerModerationDelete(args);
                break;
            case 'Moderation Block': {
                args = await playerModerationRequest.sendPlayerModeration({
                    moderated: userId,
                    type: 'block'
                });
                handleSendPlayerModeration(args);
                break;
            }
            case 'Moderation Unmute':
                args = await playerModerationRequest.deletePlayerModeration({
                    moderated: userId,
                    type: 'mute'
                });
                handlePlayerModerationDelete(args);
                break;
            case 'Moderation VolumeX': {
                args = await playerModerationRequest.sendPlayerModeration({
                    moderated: userId,
                    type: 'mute'
                });
                handleSendPlayerModeration(args);
                break;
            }
            case 'Moderation Enable Avatar Interaction':
                args = await playerModerationRequest.deletePlayerModeration({
                    moderated: userId,
                    type: 'interactOff'
                });
                handlePlayerModerationDelete(args);
                break;
            case 'Moderation Disable Avatar Interaction': {
                args = await playerModerationRequest.sendPlayerModeration({
                    moderated: userId,
                    type: 'interactOff'
                });
                handleSendPlayerModeration(args);
                break;
            }
            case 'Moderation Enable Chatbox':
                args = await playerModerationRequest.deletePlayerModeration({
                    moderated: userId,
                    type: 'muteChat'
                });
                handlePlayerModerationDelete(args);
                break;
            case 'Moderation Disable Chatbox': {
                args = await playerModerationRequest.sendPlayerModeration({
                    moderated: userId,
                    type: 'muteChat'
                });
                handleSendPlayerModeration(args);
                break;
            }
            case 'Report Hacking':
                reportUserForHacking(userId);
                break;
            case 'Unfriend':
                args = await friendRequest.deleteFriend(
                    {
                        userId
                    },
                    t('dialog.user.actions.unfriend_success_msg')
                );
                handleFriendDelete(args);
                break;
        }
    }

    function reportUserForHacking(userId) {
        miscRequest.reportUser({
            userId,
            contentType: 'user',
            reason: 'behavior-hacking',
            type: 'report'
        });
    }

    async function getUserGroups(userId) {
        exitEditModeCurrentUserGroups();
        userDialog.value.isGroupsLoading = true;
        userGroups.value = {
            groups: [],
            ownGroups: [],
            mutualGroups: [],
            remainingGroups: []
        };
        const args = await groupRequest.getGroups({ userId });
        handleGroupList(args);
        if (userId !== userDialog.value.id) {
            userDialog.value.isGroupsLoading = false;
            return;
        }
        if (userId === currentUser.value.id) {
            // update current user groups
            currentUserGroups.value.clear();
            args.json.forEach((group) => {
                const ref = applyGroup(group);
                if (!currentUserGroups.value.has(group.id)) {
                    currentUserGroups.value.set(group.id, ref);
                }
            });

            saveCurrentUserGroups();
        }
        userGroups.value.groups = args.json;
        for (let i = 0; i < args.json.length; ++i) {
            const group = args.json[i];
            if (!group?.id) {
                console.error('getUserGroups, group ID is missing', group);
                continue;
            }
            if (group.ownerId === userId) {
                userGroups.value.ownGroups.unshift(group);
            }
            if (userId === currentUser.value.id) {
                // skip mutual groups for current user
                if (group.ownerId !== userId) {
                    userGroups.value.remainingGroups.unshift(group);
                }
                continue;
            }
            if (group.mutualGroup) {
                userGroups.value.mutualGroups.unshift(group);
            }
            if (!group.mutualGroup && group.ownerId !== userId) {
                userGroups.value.remainingGroups.unshift(group);
            }
        }
        if (userId === currentUser.value.id) {
            userDialog.value.groupSorting = userDialogGroupSortingOptions.inGame;
        } else if (userDialog.value.groupSorting.value === userDialogGroupSortingOptions.inGame.value) {
            userDialog.value.groupSorting = userDialogGroupSortingOptions.alphabetical;
        }
        await sortCurrentUserGroups();
        userDialog.value.isGroupsLoading = false;
    }

    async function getUserMutualFriends(userId) {
        userDialog.value.mutualFriends = [];
        if (currentUser.value.hasSharedConnectionsOptOut) {
            return;
        }
        userDialog.value.isMutualFriendsLoading = true;
        const params = {
            userId,
            n: 100,
            offset: 0
        };
        processBulk({
            fn: userRequest.getMutualFriends,
            N: -1,
            params,
            handle: (args) => {
                for (const json of args.json) {
                    if (userDialog.value.mutualFriends.some((u) => u.id === json.id)) {
                        continue;
                    }
                    const ref = cachedUsers.get(json.id);
                    if (typeof ref !== 'undefined') {
                        userDialog.value.mutualFriends.push(ref);
                    } else {
                        userDialog.value.mutualFriends.push(json);
                    }
                }
                setUserDialogMutualFriendSorting(userDialog.value.mutualFriendSorting);
            },
            done: (success) => {
                userDialog.value.isMutualFriendsLoading = false;
                if (success) {
                    const mutualIds = userDialog.value.mutualFriends.map((u) => u.id);
                    database.updateMutualsForFriend(userId, mutualIds);
                }
                mutualFriendsError.value = !success;
            }
        });
    }

    function sortGroupsByInGame(a, b) {
        const aIndex = inGameGroupOrder.value.indexOf(a?.id);
        const bIndex = inGameGroupOrder.value.indexOf(b?.id);
        if (aIndex === -1 && bIndex === -1) {
            return 0;
        }
        if (aIndex === -1) {
            return 1;
        }
        if (bIndex === -1) {
            return -1;
        }
        return aIndex - bIndex;
    }

    async function sortCurrentUserGroups() {
        const D = userDialog.value;
        let sortMethod = () => 0;

        switch (D.groupSorting.value) {
            case 'alphabetical':
                sortMethod = compareByName;
                break;
            case 'members':
                sortMethod = compareByMemberCount;
                break;
            case 'inGame':
                sortMethod = sortGroupsByInGame;
                await updateInGameGroupOrder();
                break;
        }

        userGroups.value.ownGroups.sort(sortMethod);
        userGroups.value.mutualGroups.sort(sortMethod);
        userGroups.value.remainingGroups.sort(sortMethod);
    }

    function setUserDialogAvatars(userId) {
        const avatars = new Set();
        userDialogAvatars.value.forEach((avatar) => {
            avatars.add(avatar.id);
        });
        for (const ref of cachedAvatars.values()) {
            if (ref.authorId === userId && !avatars.has(ref.id)) {
                userDialog.value.avatars.push(ref);
            }
        }
        sortUserDialogAvatars(userDialog.value.avatars);
    }

    function setUserDialogWorlds(userId) {
        const worlds = [];
        for (const ref of cachedWorlds.values()) {
            if (ref.authorId === userId) {
                worlds.push(ref);
            }
        }
        userDialog.value.worlds = worlds;
    }

    function refreshUserDialogWorlds() {
        const D = userDialog.value;
        if (D.isWorldsLoading) {
            return;
        }
        D.isWorldsLoading = true;
        const params = {
            n: 50,
            offset: 0,
            sort: userDialog.value.worldSorting.value,
            order: userDialog.value.worldOrder.value,
            // user: 'friends',
            userId: D.id,
            releaseStatus: 'public'
        };
        if (params.userId === currentUser.value.id) {
            params.user = 'me';
            params.releaseStatus = 'all';
        }
        const map = new Map();
        for (const ref of cachedWorlds.values()) {
            if (ref.authorId === D.id && (ref.authorId === currentUser.value.id || ref.releaseStatus === 'public')) {
                cachedWorlds.delete(ref.id);
            }
        }
        processBulk({
            fn: worldRequest.getWorlds,
            N: -1,
            params,
            handle: (args) => {
                for (const json of args.json) {
                    const $ref = cachedWorlds.get(json.id);
                    if (typeof $ref !== 'undefined') {
                        map.set($ref.id, $ref);
                    }
                }
            },
            done: () => {
                if (D.id === params.userId) {
                    setUserDialogWorlds(D.id);
                }
                D.isWorldsLoading = false;
            }
        });
    }

    async function getUserFavoriteWorlds(userId) {
        userDialog.value.isFavoriteWorldsLoading = true;
        favoriteWorldsTab.value = '0';
        userFavoriteWorlds.value = [];
        const worldLists = [];
        let params = {
            ownerId: userId,
            n: 100,
            offset: 0
        };
        const json = await request('favorite/groups', {
            method: 'GET',
            params
        });
        for (let i = 0; i < json.length; ++i) {
            const list = json[i];
            if (list.type !== 'world') {
                continue;
            }
            params = {
                ownerId: userId,
                n: 100,
                offset: 0,
                userId,
                tag: list.name
            };
            try {
                const args = await favoriteRequest.getFavoriteWorlds(params);
                handleFavoriteWorldList(args);
                worldLists.push([list.displayName, list.visibility, args.json]);
            } catch (err) {
                console.error('getUserFavoriteWorlds', err);
            }
        }
        userFavoriteWorlds.value = worldLists;
        userDialog.value.isFavoriteWorldsLoading = false;
    }

    function showBioDialog() {
        const D = bioDialog.value;
        D.bio = currentUser.value.bio;
        D.bioLinks = currentUser.value.bioLinks.slice();
        D.visible = true;
    }

    async function translateBio() {
        if (translateLoading.value) {
            return;
        }
        const bio = userDialog.value.ref.bio;
        if (!bio) {
            return;
        }

        const targetLang = bioLanguage.value;

        if (bioCache.value.userId !== userDialog.value.id) {
            bioCache.value.userId = userDialog.value.id;
            bioCache.value.translated = null;
        }

        if (bioCache.value.translated) {
            bioCache.value.translated = null;
            return;
        }

        translateLoading.value = true;
        try {
            const providerLabel = translationApiType.value === 'openai' ? 'OpenAI' : 'Google';
            const translated = await translateText(`${bio}\n\nTranslated by ${providerLabel}`, targetLang);
            if (!translated) {
                throw new Error('No translation returned');
            }

            bioCache.value.translated = translated;
        } catch (err) {
            console.error('Translation failed:', err);
        } finally {
            translateLoading.value = false;
        }
    }

    function showPreviousInstancesUserDialog(userRef) {
        const D = previousInstancesUserDialog.value;
        D.userRef = userRef;
        D.visible = true;
        // trigger watcher
        D.openFlg = true;
        nextTick(() => (D.openFlg = false));
    }

    function toggleAvatarCopying() {
        userRequest.saveCurrentUser({
            allowAvatarCopying: !currentUser.value.allowAvatarCopying
        });
    }

    function toggleAllowBooping() {
        userRequest.saveCurrentUser({
            isBoopingEnabled: !currentUser.value.isBoopingEnabled
        });
    }

    function resetHome() {
        modalStore
            .confirm({
                description: 'Continue? Reset Home',
                title: 'Confirm'
            })
            .then(({ ok }) => {
                if (!ok) return;
                userRequest
                    .saveCurrentUser({
                        homeLocation: ''
                    })
                    .then((args) => {
                        toast.success('Home world has been reset');
                        return args;
                    });
            })
            .catch(() => {});
    }

    function copyUserId(userId) {
        copyToClipboard(userId, 'User ID copied to clipboard');
    }

    function copyUserURL(userId) {
        copyToClipboard(`https://vrchat.com/home/user/${userId}`, 'User URL copied to clipboard');
    }

    function copyUserDisplayName(displayName) {
        copyToClipboard(displayName, 'User DisplayName copied to clipboard');
    }

    const userDialogGroupSortingKey = computed(() => {
        const current = userDialog.value.groupSorting;
        const found = Object.entries(userDialogGroupSortingOptions).find(([, option]) => {
            if (option === current) {
                return true;
            }
            return option?.value === current?.value || option?.name === current?.name;
        });
        return found ? String(found[0]) : '';
    });

    function setUserDialogGroupSortingByKey(key) {
        const option = userDialogGroupSortingOptions[key];
        if (!option) {
            return;
        }
        setUserDialogGroupSorting(option);
    }

    async function setUserDialogGroupSorting(sortOrder) {
        const D = userDialog.value;
        if (D.groupSorting.value === sortOrder.value) {
            return;
        }
        D.groupSorting = sortOrder;
        await sortCurrentUserGroups();
    }

    const userDialogMutualFriendSortingKey = computed(() => {
        const current = userDialog.value.mutualFriendSorting;
        const found = Object.entries(userDialogMutualFriendSortingOptions).find(([, option]) => {
            if (option === current) {
                return true;
            }
            return option?.value === current?.value || option?.name === current?.name;
        });
        return found ? String(found[0]) : '';
    });

    function setUserDialogMutualFriendSortingByKey(key) {
        const option = userDialogMutualFriendSortingOptions[key];
        if (!option) {
            return;
        }
        setUserDialogMutualFriendSorting(option);
    }

    async function setUserDialogMutualFriendSorting(sortOrder) {
        const D = userDialog.value;
        D.mutualFriendSorting = sortOrder;
        switch (sortOrder.value) {
            case 'alphabetical':
                D.mutualFriends.sort(compareByDisplayName);
                break;
            case 'lastActive':
                D.mutualFriends.sort(compareByLastActiveRef);
                break;
            case 'friendOrder':
                D.mutualFriends.sort(compareByFriendOrder);
                break;
        }
    }

    async function exitEditModeCurrentUserGroups() {
        userDialogGroupEditMode.value = false;
        userDialogGroupEditGroups.value = [];
        userDialogGroupEditSelectedGroupIds.value = [];
        userDialogGroupAllSelected.value = false;
        await sortCurrentUserGroups();
    }

    async function editModeCurrentUserGroups() {
        await updateInGameGroupOrder();
        userDialogGroupEditGroups.value = Array.from(currentUserGroups.value.values());
        userDialogGroupEditGroups.value.sort(sortGroupsByInGame);
        userDialogGroupEditMode.value = true;
    }

    async function saveInGameGroupOrder() {
        userDialogGroupEditGroups.value.sort(sortGroupsByInGame);
        try {
            await AppApi.SetVRChatRegistryKey(
                `VRC_GROUP_ORDER_${currentUser.value.id}`,
                JSON.stringify(inGameGroupOrder.value),
                3
            );
        } catch (err) {
            console.error(err);
            toast.error('Failed to save in-game group order');
        }
    }

    // Select all groups currently in the editable list by collecting their IDs
    function selectAllGroups() {
        const allSelected = userDialogGroupEditSelectedGroupIds.value.length === userDialogGroupEditGroups.value.length;

        // First update selection state
        userDialogGroupEditSelectedGroupIds.value = allSelected ? [] : userDialogGroupEditGroups.value.map((g) => g.id);
        userDialogGroupAllSelected.value = !allSelected;

        // Toggle editMode off and back on to force checkbox UI update
        userDialogGroupEditMode.value = false;
        nextTick(() => {
            userDialogGroupEditMode.value = true;
        });
    }

    const bulkGroupActionValue = ref('');

    function handleBulkGroupAction(value) {
        bulkGroupActionValue.value = value;

        if (value === 'leave') {
            bulkLeaveGroups();
        } else if (typeof value === 'string' && value.startsWith('visibility:')) {
            const newVisibility = value.slice('visibility:'.length);
            bulkSetVisibility(newVisibility);
        }

        nextTick(() => {
            bulkGroupActionValue.value = '';
        });
    }

    // Apply the given visibility to all selected groups
    async function bulkSetVisibility(newVisibility) {
        for (const groupId of userDialogGroupEditSelectedGroupIds.value) {
            setGroupVisibility(groupId, newVisibility);
        }
    }

    // Leave (remove user from) all selected groups
    function bulkLeaveGroups() {
        for (const groupId of userDialogGroupEditSelectedGroupIds.value) {
            leaveGroup(groupId);
        }
    }

    // Toggle individual group selection for bulk actions
    function toggleGroupSelection(groupId) {
        const index = userDialogGroupEditSelectedGroupIds.value.indexOf(groupId);
        if (index === -1) {
            userDialogGroupEditSelectedGroupIds.value.push(groupId);
        } else {
            userDialogGroupEditSelectedGroupIds.value.splice(index, 1);
        }
    }

    function moveGroupUp(groupId) {
        const index = inGameGroupOrder.value.indexOf(groupId);
        if (index > 0) {
            inGameGroupOrder.value.splice(index, 1);
            inGameGroupOrder.value.splice(index - 1, 0, groupId);
            saveInGameGroupOrder();
        }
    }

    function moveGroupDown(groupId) {
        const index = inGameGroupOrder.value.indexOf(groupId);
        if (index < inGameGroupOrder.value.length - 1) {
            inGameGroupOrder.value.splice(index, 1);
            inGameGroupOrder.value.splice(index + 1, 0, groupId);
            saveInGameGroupOrder();
        }
    }

    function moveGroupTop(groupId) {
        const index = inGameGroupOrder.value.indexOf(groupId);
        if (index > 0) {
            inGameGroupOrder.value.splice(index, 1);
            inGameGroupOrder.value.unshift(groupId);
            saveInGameGroupOrder();
        }
    }

    function moveGroupBottom(groupId) {
        const index = inGameGroupOrder.value.indexOf(groupId);
        if (index < inGameGroupOrder.value.length - 1) {
            inGameGroupOrder.value.splice(index, 1);
            inGameGroupOrder.value.push(groupId);
            saveInGameGroupOrder();
        }
    }

    async function setUserDialogWorldSorting(sortOrder) {
        const D = userDialog.value;
        if (D.worldSorting.value === sortOrder.value) {
            return;
        }
        D.worldSorting = sortOrder;
        refreshUserDialogWorlds();
    }

    const userDialogWorldSortingKey = computed(() => {
        const current = userDialog.value.worldSorting;
        const found = Object.entries(userDialogWorldSortingOptions).find(([, option]) => {
            if (option === current) {
                return true;
            }
            return option?.value === current?.value || option?.name === current?.name;
        });
        return found ? String(found[0]) : '';
    });

    function setUserDialogWorldSortingByKey(key) {
        const option = userDialogWorldSortingOptions[key];
        if (!option) {
            return;
        }
        setUserDialogWorldSorting(option);
    }

    async function setUserDialogWorldOrder(order) {
        const D = userDialog.value;
        if (D.worldOrder.value === order.value) {
            return;
        }
        D.worldOrder = order;
        refreshUserDialogWorlds();
    }

    const userDialogWorldOrderKey = computed(() => {
        const current = userDialog.value.worldOrder;
        const found = Object.entries(userDialogWorldOrderOptions).find(([, option]) => {
            if (option === current) {
                return true;
            }
            return option?.value === current?.value || option?.name === current?.name;
        });
        return found ? String(found[0]) : '';
    });

    function setUserDialogWorldOrderByKey(key) {
        const option = userDialogWorldOrderOptions[key];
        if (!option) {
            return;
        }
        setUserDialogWorldOrder(option);
    }

    function changeUserDialogAvatarSorting(sortOption) {
        const D = userDialog.value;
        D.avatarSorting = sortOption;
        sortUserDialogAvatars(D.avatars);
    }

    function closeInviteDialog() {
        clearInviteImageUpload();
    }

    function getVRChatCredits() {
        miscRequest.getVRChatCredits().then((args) => (vrchatCredit.value = args.json?.balance));
    }
</script>
