<template>
    <el-dialog
        :z-index="inviteGroupDialogIndex"
        v-model="inviteGroupDialog.visible"
        :title="t('dialog.invite_to_group.header')"
        width="450px"
        append-to-body>
        <div v-if="inviteGroupDialog.visible" v-loading="inviteGroupDialog.loading">
            <span>{{ t('dialog.invite_to_group.description') }}</span>
            <br />

            <div style="margin-top: 15px; width: 100%">
                <VirtualCombobox
                    v-model="inviteGroupDialog.groupId"
                    :groups="groupPickerGroups"
                    :disabled="inviteGroupDialog.loading"
                    :placeholder="t('dialog.invite_to_group.choose_group_placeholder')"
                    :search-placeholder="t('dialog.invite_to_group.choose_group_placeholder')"
                    :clearable="true"
                    :close-on-select="true"
                    :deselect-on-reselect="true">
                    <template #item="{ item, selected }">
                        <div class="x-friend-item flex w-full items-center">
                            <div class="avatar">
                                <img :src="item.iconUrl" loading="lazy" />
                            </div>
                            <div class="detail">
                                <span class="name" v-text="item.label"></span>
                            </div>
                            <CheckIcon :class="['ml-auto size-4', selected ? 'opacity-100' : 'opacity-0']" />
                        </div>
                    </template>
                </VirtualCombobox>
            </div>

            <div style="width: 100%; margin-top: 15px">
                <VirtualCombobox
                    v-model="inviteGroupDialog.userIds"
                    :groups="friendPickerGroups"
                    multiple
                    :disabled="inviteGroupDialog.loading"
                    :placeholder="t('dialog.invite_to_group.choose_friends_placeholder')"
                    :search-placeholder="t('dialog.invite_to_group.choose_friends_placeholder')"
                    :clearable="true">
                    <template #item="{ item, selected }">
                        <div class="x-friend-item flex w-full items-center">
                            <template v-if="item.user">
                                <div class="avatar" :class="userStatusClass(item.user)">
                                    <img :src="userImage(item.user)" loading="lazy" />
                                </div>
                                <div class="detail">
                                    <span
                                        class="name"
                                        :style="{ color: item.user.$userColour }"
                                        v-text="item.user.displayName"></span>
                                </div>
                            </template>
                            <template v-else>
                                <span v-text="item.label"></span>
                            </template>

                            <CheckIcon :class="['ml-auto size-4', selected ? 'opacity-100' : 'opacity-0']" />
                        </div>
                    </template>
                </VirtualCombobox>
            </div>
        </div>
        <template #footer>
            <el-button
                type="primary"
                :disabled="inviteGroupDialog.loading || !inviteGroupDialog.userIds.length || !inviteGroupDialog.groupId"
                @click="sendGroupInvite">
                {{ t('dialog.invite_to_group.invite') }}
            </el-button>
        </template>
    </el-dialog>
</template>

<script setup>
    import { computed, nextTick, ref, watch } from 'vue';
    import { Check as CheckIcon } from 'lucide-vue-next';
    import { ElMessageBox } from 'element-plus';
    import { storeToRefs } from 'pinia';
    import { toast } from 'vue-sonner';
    import { useI18n } from 'vue-i18n';

    import { hasGroupPermission, userImage, userStatusClass } from '../../shared/utils';
    import { groupRequest, userRequest } from '../../api';
    import { useFriendStore, useGroupStore } from '../../stores';
    import { VirtualCombobox } from '../ui/virtual-combobox';
    import { getNextDialogIndex } from '../../shared/utils/base/ui';

    import configRepository from '../../service/config';

    const { vipFriends, onlineFriends, activeFriends, offlineFriends } = storeToRefs(useFriendStore());
    const { currentUserGroups, inviteGroupDialog } = storeToRefs(useGroupStore());
    const { applyGroup } = useGroupStore();
    const { t } = useI18n();

    watch(
        () => inviteGroupDialog.value.visible,
        async (value) => {
            if (value) {
                inviteGroupDialog.value.groupId = await configRepository.getString('inviteGroupLastGroup', '');
                initDialog();
            } else {
                await configRepository.setString('inviteGroupLastGroup', inviteGroupDialog.value.groupId);
            }
        }
    );

    const inviteGroupDialogIndex = ref(2000);

    const groupsWithInvitePermission = computed(() => {
        return Array.from(currentUserGroups.value.values()).filter((group) =>
            hasGroupPermission(group, 'group-invites-manage')
        );
    });

    const groupPickerGroups = computed(() => [
        {
            key: 'groupsWithInvitePermission',
            label: t('dialog.invite_to_group.groups_with_invite_permission'),
            items: groupsWithInvitePermission.value.map((group) => ({
                value: String(group.id),
                label: group.name,
                search: group.name,
                iconUrl: group.iconUrl
            }))
        }
    ]);

    const friendById = computed(() => {
        const map = new Map();
        for (const friend of vipFriends.value) map.set(friend.id, friend);
        for (const friend of onlineFriends.value) map.set(friend.id, friend);
        for (const friend of activeFriends.value) map.set(friend.id, friend);
        for (const friend of offlineFriends.value) map.set(friend.id, friend);
        return map;
    });

    function resolveUserDisplayName(userId) {
        const D = inviteGroupDialog.value;
        if (D?.userObject?.id && D.userObject.id === userId) {
            return D.userObject.displayName;
        }
        const friend = friendById.value.get(userId);
        return friend?.ref?.displayName ?? friend?.name ?? String(userId);
    }

    const friendPickerGroups = computed(() => {
        const D = inviteGroupDialog.value;

        const groups = [];

        if (D?.userId) {
            const selectedUser = D.userObject?.id
                ? {
                      value: String(D.userObject.id),
                      label: D.userObject.displayName,
                      search: D.userObject.displayName,
                      user: D.userObject
                  }
                : {
                      value: String(D.userId),
                      label: String(D.userId),
                      search: String(D.userId)
                  };

            groups.push({
                key: 'selectedUsers',
                label: t('dialog.invite_to_group.selected_users'),
                items: [selectedUser]
            });
        }

        const addFriendGroup = (key, label, friends) => {
            if (!friends?.length) return;
            groups.push({
                key,
                label,
                items: friends.map((friend) => {
                    const user = friend?.ref ?? null;
                    const displayName = resolveUserDisplayName(friend.id);
                    return {
                        value: String(friend.id),
                        label: displayName,
                        search: displayName,
                        user
                    };
                })
            });
        };

        addFriendGroup('vip', t('side_panel.favorite'), vipFriends.value);
        addFriendGroup('online', t('side_panel.online'), onlineFriends.value);
        addFriendGroup('active', t('side_panel.active'), activeFriends.value);
        addFriendGroup('offline', t('side_panel.offline'), offlineFriends.value);

        return groups;
    });

    watch(
        () => inviteGroupDialog.value.groupId,
        (groupId) => {
            if (!inviteGroupDialog.value.visible) {
                return;
            }
            if (!groupId) {
                inviteGroupDialog.value.groupName = '';
                return;
            }
            groupRequest
                .getCachedGroup({ groupId })
                .then((args) => {
                    inviteGroupDialog.value.groupName = args.ref.name;
                })
                .catch(() => {
                    inviteGroupDialog.value.groupId = '';
                });
            isAllowedToInviteToGroup();
        }
    );

    function initDialog() {
        nextTick(() => {
            inviteGroupDialogIndex.value = getNextDialogIndex();
        });
        const D = inviteGroupDialog.value;
        if (D.groupId) {
            groupRequest
                .getCachedGroup({
                    groupId: D.groupId
                })
                .then((args) => {
                    D.groupName = args.ref.name;
                })
                .catch(() => {
                    D.groupId = '';
                });
            isAllowedToInviteToGroup();
        }

        if (D.userId) {
            userRequest.getCachedUser({ userId: D.userId }).then((args) => {
                D.userObject = args.ref;
                D.userIds = [D.userId];
            });
        }
    }
    function isAllowedToInviteToGroup() {
        const D = inviteGroupDialog.value;
        const groupId = D.groupId;
        if (!groupId) {
            return;
        }
        inviteGroupDialog.value.loading = true;
        groupRequest
            .getGroup({ groupId })
            .then((args) => {
                const ref = applyGroup(args.json);
                if (hasGroupPermission(ref, 'group-invites-manage')) {
                    return args;
                }
                // not allowed to invite
                inviteGroupDialog.value.groupId = '';
                toast.error('You are not allowed to invite to this group');
                return args;
            })
            .finally(() => {
                inviteGroupDialog.value.loading = false;
            });
    }
    function sendGroupInvite() {
        ElMessageBox.confirm('Continue? Invite User(s) To Group', 'Confirm', {
            confirmButtonText: 'Confirm',
            cancelButtonText: 'Cancel',
            type: 'info'
        })
            .then((action) => {
                const D = inviteGroupDialog.value;
                if (action !== 'confirm' || D.loading === true) {
                    return;
                }
                D.loading = true;
                const inviteLoop = () => {
                    if (D.userIds.length === 0) {
                        D.loading = false;
                        return;
                    }
                    const receiverUserId = D.userIds.shift();
                    groupRequest
                        .sendGroupInvite({
                            groupId: D.groupId,
                            userId: receiverUserId
                        })
                        .then(inviteLoop)
                        .catch(() => {
                            D.loading = false;
                        });
                };
                inviteLoop();
            })
            .catch(() => {});
    }
</script>
