<template>
    <div class="x-container">
        <div style="margin: 0 0 10px; display: flex; align-items: center">
            <el-input
                :model-value="searchText"
                :placeholder="t('view.search.search_placeholder')"
                style="flex: 1"
                clearable
                @input="updateSearchText"
                @keyup.enter="search"></el-input>
            <TooltipWrapper side="bottom" :content="t('view.search.clear_results_tooltip')">
                <Button class="rounded-full mr-2" size="icon-sm" variant="ghost" @click="handleClearSearch"
                    ><Trash2
                /></Button>
            </TooltipWrapper>
        </div>
        <el-tabs ref="searchTabRef" style="margin-top: 15px" @tab-click="searchText = ''">
            <el-tab-pane v-loading="isSearchUserLoading" :label="t('view.search.user.header')" style="min-height: 60px">
                <label class="inline-flex items-center gap-2" style="margin-left: 10px">
                    <Checkbox v-model="searchUserByBio" />
                    <span>{{ t('view.search.user.search_by_bio') }}</span>
                </label>
                <label class="inline-flex items-center gap-2" style="margin-left: 10px">
                    <Checkbox v-model="searchUserSortByLastLoggedIn" />
                    <span>{{ t('view.search.user.sort_by_last_logged_in') }}</span>
                </label>
                <div class="x-friend-list" style="min-height: 500px">
                    <div
                        v-for="user in searchUserResults"
                        :key="user.id"
                        class="x-friend-item"
                        @click="showUserDialog(user.id)">
                        <div class="avatar">
                            <img :src="userImage(user, true)" loading="lazy" />
                        </div>
                        <div class="detail">
                            <span class="name" v-text="user.displayName"></span>
                            <span
                                v-if="randomUserColours"
                                class="extra"
                                :class="user.$trustClass"
                                v-text="user.$trustLevel"></span>
                            <span
                                v-else
                                class="extra"
                                :style="{ color: user.$userColour }"
                                v-text="user.$trustLevel"></span>
                        </div>
                    </div>
                </div>
                <ButtonGroup v-if="searchUserResults.length" style="margin-top: 15px">
                    <Button
                        variant="outline"
                        size="sm"
                        :disabled="!searchUserParams.offset"
                        @click="handleMoreSearchUser(-1)">
                        <Back />
                        {{ t('view.search.prev_page') }}
                    </Button>
                    <Button
                        variant="outline"
                        size="sm"
                        :disabled="searchUserResults.length < 10"
                        @click="handleMoreSearchUser(1)">
                        <Right />
                        {{ t('view.search.next_page') }}
                    </Button>
                </ButtonGroup>
            </el-tab-pane>
            <el-tab-pane
                v-loading="isSearchWorldLoading"
                :label="t('view.search.world.header')"
                style="min-height: 60px">
                <div class="inline-flex justify-between mb-4 w-full">
                    <Select
                        :model-value="searchWorldCategoryIndex"
                        @update:modelValue="handleSearchWorldCategorySelect"
                        style="margin-bottom: 15px">
                        <SelectTrigger size="sm">
                            <SelectValue :placeholder="t('view.search.world.category')" />
                        </SelectTrigger>
                        <SelectContent>
                            <SelectGroup>
                                <SelectItem
                                    v-for="row in cachedConfig.dynamicWorldRows"
                                    :key="row.index"
                                    :value="row.index">
                                    {{ row.name }}
                                </SelectItem>
                            </SelectGroup>
                        </SelectContent>
                    </Select>
                    <label class="inline-flex items-center gap-2" style="margin-left: 10px">
                        <Checkbox v-model="searchWorldLabs" />
                        <span>{{ t('view.search.world.community_lab') }}</span>
                    </label>
                </div>
                <div class="x-friend-list" style="min-height: 500px">
                    <div
                        v-for="world in searchWorldResults"
                        :key="world.id"
                        class="x-friend-item"
                        @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.authorName }} ({{ world.occupants }})</span
                            >
                            <span v-else class="extra" v-text="world.authorName"></span>
                        </div>
                    </div>
                </div>
                <ButtonGroup v-if="searchWorldResults.length" style="margin-top: 15px">
                    <Button
                        variant="outline"
                        size="sm"
                        :disabled="!searchWorldParams.offset"
                        @click="moreSearchWorld(-1)">
                        <Back />
                        {{ t('view.search.prev_page') }}
                    </Button>
                    <Button
                        variant="outline"
                        size="sm"
                        :disabled="searchWorldResults.length < 10"
                        @click="moreSearchWorld(1)">
                        <Right />
                        {{ t('view.search.next_page') }}
                    </Button>
                </ButtonGroup>
            </el-tab-pane>
            <el-tab-pane
                v-loading="isSearchAvatarLoading"
                :label="t('view.search.avatar.header')"
                style="min-height: 60px">
                <div style="display: flex; align-items: center; justify-content: space-between">
                    <div style="display: flex; align-items: center">
                        <Select
                            v-if="avatarRemoteDatabaseProviderList.length > 1"
                            :model-value="avatarRemoteDatabaseProvider"
                            @update:modelValue="setAvatarProvider"
                            style="margin-right: 5px">
                            <SelectTrigger size="sm">
                                <SelectValue :placeholder="t('view.search.avatar.search_provider')" />
                            </SelectTrigger>
                            <SelectContent>
                                <SelectGroup>
                                    <SelectItem
                                        v-for="provider in avatarRemoteDatabaseProviderList"
                                        :key="provider"
                                        :value="provider">
                                        {{ provider }}
                                    </SelectItem>
                                </SelectGroup>
                            </SelectContent>
                        </Select>
                        <TooltipWrapper side="bottom" :content="t('view.search.avatar.refresh_tooltip')">
                            <el-button
                                type="default"
                                :loading="userDialog.isAvatarsLoading"
                                size="small"
                                :icon="Refresh"
                                class="ml-1"
                                circle
                                @click="refreshUserDialogAvatars"></el-button>
                        </TooltipWrapper>
                        <span style="font-size: 14px; margin-left: 5px; margin-right: 5px">{{
                            t('view.search.avatar.result_count', {
                                count: searchAvatarResults.length
                            })
                        }}</span>
                    </div>
                    <div style="display: flex; align-items: center">
                        <RadioGroup
                            :model-value="searchAvatarFilter"
                            class="flex items-center gap-4"
                            style="margin: 5px"
                            @update:modelValue="handleSearchAvatarFilterChange">
                            <div class="flex items-center space-x-2">
                                <RadioGroupItem id="searchAvatarFilter-all" value="all" />
                                <label for="searchAvatarFilter-all">{{ t('view.search.avatar.all') }}</label>
                            </div>
                            <div class="flex items-center space-x-2">
                                <RadioGroupItem id="searchAvatarFilter-public" value="public" />
                                <label for="searchAvatarFilter-public">{{ t('view.search.avatar.public') }}</label>
                            </div>
                            <div class="flex items-center space-x-2">
                                <RadioGroupItem id="searchAvatarFilter-private" value="private" />
                                <label for="searchAvatarFilter-private">{{ t('view.search.avatar.private') }}</label>
                            </div>
                        </RadioGroup>
                        <el-divider direction="vertical"></el-divider>
                        <RadioGroup
                            :model-value="searchAvatarFilterRemote"
                            class="flex items-center gap-4"
                            style="margin: 5px"
                            @update:modelValue="handleSearchAvatarFilterRemoteChange">
                            <div class="flex items-center space-x-2">
                                <RadioGroupItem id="searchAvatarFilterRemote-all" value="all" />
                                <label for="searchAvatarFilterRemote-all">{{ t('view.search.avatar.all') }}</label>
                            </div>
                            <div class="flex items-center space-x-2">
                                <RadioGroupItem id="searchAvatarFilterRemote-local" value="local" />
                                <label for="searchAvatarFilterRemote-local">{{ t('view.search.avatar.local') }}</label>
                            </div>
                            <div class="flex items-center space-x-2">
                                <RadioGroupItem
                                    id="searchAvatarFilterRemote-remote"
                                    value="remote"
                                    :disabled="!avatarRemoteDatabase" />
                                <label for="searchAvatarFilterRemote-remote">{{
                                    t('view.search.avatar.remote')
                                }}</label>
                            </div>
                        </RadioGroup>
                    </div>
                </div>
                <div style="display: flex; justify-content: end">
                    <RadioGroup
                        :model-value="searchAvatarSort"
                        :disabled="searchAvatarFilterRemote !== 'local'"
                        class="flex items-center gap-4"
                        style="margin: 5px"
                        @update:modelValue="handleSearchAvatarSortChange">
                        <div class="flex items-center space-x-2">
                            <RadioGroupItem id="searchAvatarSort-name" value="name" />
                            <label for="searchAvatarSort-name">{{ t('view.search.avatar.sort_name') }}</label>
                        </div>
                        <div class="flex items-center space-x-2">
                            <RadioGroupItem id="searchAvatarSort-update" value="update" />
                            <label for="searchAvatarSort-update">{{ t('view.search.avatar.sort_update') }}</label>
                        </div>
                        <div class="flex items-center space-x-2">
                            <RadioGroupItem id="searchAvatarSort-created" value="created" />
                            <label for="searchAvatarSort-created">{{ t('view.search.avatar.sort_created') }}</label>
                        </div>
                    </RadioGroup>
                </div>
                <div class="x-friend-list" style="margin-top: 20px; min-height: 500px">
                    <div
                        v-for="avatar in searchAvatarPage"
                        :key="avatar.id"
                        class="x-friend-item"
                        @click="showAvatarDialog(avatar.id)">
                        <div class="avatar">
                            <img v-if="avatar.thumbnailImageUrl" :src="avatar.thumbnailImageUrl" loading="lazy" />
                            <img v-else-if="avatar.imageUrl" :src="avatar.imageUrl" 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>
                            <span class="extra" v-text="avatar.authorName"></span>
                        </div>
                    </div>
                </div>
                <ButtonGroup v-if="searchAvatarPage.length" style="margin-top: 15px">
                    <Button variant="outline" size="sm" :disabled="!searchAvatarPageNum" @click="moreSearchAvatar(-1)">
                        <Back />
                        {{ t('view.search.prev_page') }}
                    </Button>
                    <Button
                        variant="outline"
                        size="sm"
                        :disabled="
                            searchAvatarResults.length < 10 ||
                            (searchAvatarPageNum + 1) * 10 >= searchAvatarResults.length
                        "
                        @click="moreSearchAvatar(1)">
                        <Right />
                        {{ t('view.search.next_page') }}
                    </Button>
                </ButtonGroup>
            </el-tab-pane>
            <el-tab-pane
                v-loading="isSearchGroupLoading"
                :label="t('view.search.group.header')"
                style="min-height: 60px">
                <div class="x-friend-list" style="min-height: 500px">
                    <div
                        v-for="group in searchGroupResults"
                        :key="group.id"
                        class="x-friend-item"
                        @click="showGroupDialog(group.id)">
                        <div class="avatar">
                            <img :src="getSmallThumbnailUrl(group.iconUrl)" loading="lazy" />
                        </div>
                        <div class="detail">
                            <span class="name">
                                <span v-text="group.name"></span>
                                <span style="margin-left: 5px; font-weight: normal">({{ group.memberCount }})</span>
                                <span
                                    style="
                                        margin-left: 5px;
                                        color: #909399;
                                        font-weight: normal;
                                        font-family: monospace;
                                        font-size: 12px;
                                    "
                                    >{{ group.shortCode }}.{{ group.discriminator }}</span
                                >
                            </span>
                            <span class="extra" v-text="group.description"></span>
                        </div>
                    </div>
                </div>
                <ButtonGroup v-if="searchGroupResults.length" style="margin-top: 15px">
                    <Button
                        variant="outline"
                        size="sm"
                        :disabled="!searchGroupParams.offset"
                        @click="moreSearchGroup(-1)">
                        <Back />
                        {{ t('view.search.prev_page') }}
                    </Button>
                    <Button
                        variant="outline"
                        size="sm"
                        :disabled="searchGroupResults.length < 10"
                        @click="moreSearchGroup(1)">
                        <Right />
                        {{ t('view.search.next_page') }}
                    </Button>
                </ButtonGroup>
            </el-tab-pane>
        </el-tabs>
    </div>
</template>

<script setup>
    import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
    import { Back, Refresh, Right } from '@element-plus/icons-vue';
    import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
    import { Button } from '@/components/ui/button';
    import { ButtonGroup } from '@/components/ui/button-group';
    import { Checkbox } from '@/components/ui/checkbox';
    import { Trash2 } from 'lucide-vue-next';
    import { ref } from 'vue';
    import { storeToRefs } from 'pinia';
    import { useI18n } from 'vue-i18n';

    import {
        useAdvancedSettingsStore,
        useAppearanceSettingsStore,
        useAuthStore,
        useAvatarProviderStore,
        useAvatarStore,
        useGroupStore,
        useSearchStore,
        useUserStore,
        useWorldStore
    } from '../../stores';
    import {
        compareByCreatedAt,
        compareByName,
        compareByUpdatedAt,
        convertFileUrlToImageUrl,
        replaceBioSymbols,
        userImage
    } from '../../shared/utils';
    import { groupRequest, worldRequest } from '../../api';

    const { randomUserColours } = storeToRefs(useAppearanceSettingsStore());
    const { avatarRemoteDatabase } = storeToRefs(useAdvancedSettingsStore());
    const { avatarRemoteDatabaseProviderList, avatarRemoteDatabaseProvider } = storeToRefs(useAvatarProviderStore());
    const { setAvatarProvider } = useAvatarProviderStore();
    const { userDialog } = storeToRefs(useUserStore());
    const { showUserDialog, refreshUserDialogAvatars } = useUserStore();
    const { showAvatarDialog, lookupAvatars, cachedAvatars } = useAvatarStore();
    const { cachedWorlds, showWorldDialog } = useWorldStore();
    const { showGroupDialog } = useGroupStore();
    const { searchText, searchUserResults } = storeToRefs(useSearchStore());
    const { clearSearch, moreSearchUser } = useSearchStore();
    const { cachedConfig } = storeToRefs(useAuthStore());

    const { t } = useI18n();

    const searchTabRef = ref(null);

    const searchUserParams = ref({});
    const searchUserByBio = ref(false);
    const searchUserSortByLastLoggedIn = ref(false);

    const isSearchUserLoading = ref(false);
    const isSearchWorldLoading = ref(false);
    const isSearchAvatarLoading = ref(false);
    const isSearchGroupLoading = ref(false);

    const searchWorldOption = ref('');
    const searchWorldLabs = ref(false);
    const searchWorldParams = ref({});

    const searchWorldCategoryIndex = ref(null);
    const searchWorldResults = ref([]);

    function handleSearchAvatarFilterChange(value) {
        searchAvatarFilter.value = value;
        searchAvatar();
    }

    function handleSearchAvatarFilterRemoteChange(value) {
        searchAvatarFilterRemote.value = value;
        searchAvatar();
    }

    function handleSearchAvatarSortChange(value) {
        searchAvatarSort.value = value;
        searchAvatar();
    }

    const searchAvatarFilter = ref('');
    const searchAvatarSort = ref('');
    const searchAvatarFilterRemote = ref('');
    const searchAvatarPageNum = ref(0);
    const searchAvatarResults = ref([]);
    const searchAvatarPage = ref([]);

    const searchGroupParams = ref({});
    const searchGroupResults = ref([]);

    function getSmallThumbnailUrl(url) {
        return convertFileUrlToImageUrl(url);
    }

    function handleClearSearch() {
        searchUserParams.value = {};
        searchWorldParams.value = {};
        searchWorldResults.value = [];
        searchAvatarResults.value = [];
        searchAvatarPage.value = [];
        searchAvatarPageNum.value = 0;
        searchGroupParams.value = {};
        searchGroupResults.value = [];
        clearSearch();
    }

    function updateSearchText(text) {
        searchText.value = text;
    }

    function search() {
        switch (searchTabRef.value.currentName) {
            case '0':
                searchUser();
                break;
            case '1':
                searchWorld({});
                break;
            case '2':
                searchAvatar();
                break;
            case '3':
                searchGroup();
                break;
        }
    }

    async function searchUser() {
        searchUserParams.value = {
            n: 10,
            offset: 0,
            search: searchText.value,
            customFields: searchUserByBio.value ? 'bio' : 'displayName',
            sort: searchUserSortByLastLoggedIn.value ? 'last_login' : 'relevance'
        };
        await handleMoreSearchUser();
    }

    async function handleMoreSearchUser(go = null) {
        isSearchUserLoading.value = true;
        await moreSearchUser(go, searchUserParams.value);
        isSearchUserLoading.value = false;
    }

    function searchWorld(ref) {
        searchWorldOption.value = '';
        searchWorldCategoryIndex.value = ref?.index ?? null;
        const params = {
            n: 10,
            offset: 0
        };
        switch (ref.sortHeading) {
            case 'featured':
                params.sort = 'order';
                params.featured = 'true';
                break;
            case 'trending':
                params.sort = 'popularity';
                params.featured = 'false';
                break;
            case 'updated':
                params.sort = 'updated';
                break;
            case 'created':
                params.sort = 'created';
                break;
            case 'publication':
                params.sort = 'publicationDate';
                break;
            case 'shuffle':
                params.sort = 'shuffle';
                break;
            case 'active':
                searchWorldOption.value = 'active';
                break;
            case 'recent':
                searchWorldOption.value = 'recent';
                break;
            case 'favorite':
                searchWorldOption.value = 'favorites';
                break;
            case 'labs':
                params.sort = 'labsPublicationDate';
                break;
            case 'heat':
                params.sort = 'heat';
                params.featured = 'false';
                break;
            default:
                params.sort = 'relevance';
                params.search = replaceBioSymbols(searchText.value);
                break;
        }
        params.order = ref.sortOrder || 'descending';
        if (ref.sortOwnership === 'mine') {
            params.user = 'me';
            params.releaseStatus = 'all';
        }
        if (ref.tag) {
            params.tag = ref.tag;
        }
        if (!searchWorldLabs.value) {
            if (params.tag) {
                params.tag += ',system_approved';
            } else {
                params.tag = 'system_approved';
            }
        }
        // TODO: option.platform
        searchWorldParams.value = params;
        moreSearchWorld();
    }

    function handleSearchWorldCategorySelect(index) {
        searchWorldCategoryIndex.value = index;
        const row = cachedConfig.value?.dynamicWorldRows?.find((r) => r.index === index);
        searchWorld(row || {});
    }

    function moreSearchWorld(go) {
        const params = searchWorldParams.value;
        if (go) {
            params.offset += params.n * go;
            if (params.offset < 0) {
                params.offset = 0;
            }
        }
        isSearchWorldLoading.value = true;
        worldRequest
            .getWorlds(params, searchWorldOption.value)
            .finally(() => {
                isSearchWorldLoading.value = false;
            })
            .then((args) => {
                const map = new Map();
                for (const json of args.json) {
                    const ref = cachedWorlds.get(json.id);
                    if (typeof ref !== 'undefined') {
                        map.set(ref.id, ref);
                    }
                }
                searchWorldResults.value = Array.from(map.values());
                return args;
            });
    }

    async function searchAvatar() {
        let ref;
        isSearchAvatarLoading.value = true;
        if (!searchAvatarFilter.value) {
            searchAvatarFilter.value = 'all';
        }
        if (!searchAvatarSort.value) {
            searchAvatarSort.value = 'name';
        }
        if (!searchAvatarFilterRemote.value) {
            searchAvatarFilterRemote.value = 'all';
        }
        if (searchAvatarFilterRemote.value !== 'local') {
            searchAvatarSort.value = 'name';
        }
        const avatars = new Map();
        const query = searchText.value;
        const queryUpper = query.toUpperCase();
        if (!query) {
            for (ref of cachedAvatars.values()) {
                switch (searchAvatarFilter.value) {
                    case 'all':
                        avatars.set(ref.id, ref);
                        break;
                    case 'public':
                        if (ref.releaseStatus === 'public') {
                            avatars.set(ref.id, ref);
                        }
                        break;
                    case 'private':
                        if (ref.releaseStatus === 'private') {
                            avatars.set(ref.id, ref);
                        }
                        break;
                }
            }
            isSearchAvatarLoading.value = false;
        } else {
            if (searchAvatarFilterRemote.value === 'all' || searchAvatarFilterRemote.value === 'local') {
                for (ref of cachedAvatars.values()) {
                    let match = ref.name.toUpperCase().includes(queryUpper);
                    if (!match && ref.description) {
                        match = ref.description.toUpperCase().includes(queryUpper);
                    }
                    if (!match && ref.authorName) {
                        match = ref.authorName.toUpperCase().includes(queryUpper);
                    }
                    if (match) {
                        switch (searchAvatarFilter.value) {
                            case 'all':
                                avatars.set(ref.id, ref);
                                break;
                            case 'public':
                                if (ref.releaseStatus === 'public') {
                                    avatars.set(ref.id, ref);
                                }
                                break;
                            case 'private':
                                if (ref.releaseStatus === 'private') {
                                    avatars.set(ref.id, ref);
                                }
                                break;
                        }
                    }
                }
            }
            if (
                (searchAvatarFilterRemote.value === 'all' || searchAvatarFilterRemote.value === 'remote') &&
                avatarRemoteDatabase.value &&
                query.length >= 3
            ) {
                const data = await lookupAvatars('search', query);
                if (data && typeof data === 'object') {
                    data.forEach((avatar) => {
                        avatars.set(avatar.id, avatar);
                    });
                }
            }
            isSearchAvatarLoading.value = false;
        }
        const avatarsArray = Array.from(avatars.values());
        if (searchAvatarFilterRemote.value === 'local') {
            switch (searchAvatarSort.value) {
                case 'updated':
                    avatarsArray.sort(compareByUpdatedAt);
                    break;
                case 'created':
                    avatarsArray.sort(compareByCreatedAt);
                    break;
                case 'name':
                    avatarsArray.sort(compareByName);
                    break;
            }
        }
        searchAvatarPageNum.value = 0;
        searchAvatarResults.value = avatarsArray;
        searchAvatarPage.value = avatarsArray.slice(0, 10);
    }
    function moreSearchAvatar(n) {
        let offset;
        if (n === -1) {
            searchAvatarPageNum.value--;
            offset = searchAvatarPageNum.value * 10;
        }
        if (n === 1) {
            searchAvatarPageNum.value++;
            offset = searchAvatarPageNum.value * 10;
        }
        searchAvatarPage.value = searchAvatarResults.value.slice(offset, offset + 10);
    }
    async function searchGroup() {
        searchGroupParams.value = {
            n: 10,
            offset: 0,
            query: replaceBioSymbols(searchText.value)
        };
        await moreSearchGroup();
    }
    async function moreSearchGroup(go) {
        const params = searchGroupParams.value;
        if (go) {
            params.offset += params.n * go;
            if (params.offset < 0) {
                params.offset = 0;
            }
        }
        isSearchGroupLoading.value = true;
        await groupRequest
            .groupSearch(params)
            .finally(() => {
                isSearchGroupLoading.value = false;
            })
            .then((args) => {
                const map = new Map();
                for (const json of args.json) {
                    map.set(json.id, json);
                }
                searchGroupResults.value = Array.from(map.values());
                return args;
            });
    }
</script>
