<template>
    <div class="flex-grow-1" style="display: flex; flex-direction: column; height: 100%;">
        <div style="flex-grow: 1; width: 100%; border: 1px solid #eee; border-radius: 8px; padding: 16px">
            <v-banner lines="one">
                <template v-slot:text>
                    建议您更换使用新版知识库功能。
                </template>
            </v-banner>
            <!-- knowledge card -->
            <div v-if="!installed" class="d-flex align-center justify-center flex-column"
                style="flex-grow: 1; width: 100%; height: 100%;">
                <h2>{{ tm('notInstalled.title') }}
                    <v-icon class="ml-2" size="small" color="grey"
                        @click="openUrl('https://astrbot.app/use/knowledge-base.html')">mdi-information-outline</v-icon>
                </h2>
                <v-btn style="margin-top: 16px;" variant="tonal" color="primary" @click="installPlugin"
                    :loading="installing">
                    {{ tm('notInstalled.install') }}
                </v-btn>
                <ConsoleDisplayer v-show="installing"
                    style="background-color: #fff; max-height: 300px; margin-top: 16px; max-width: 100%"
                    :show-level-btns="false"></ConsoleDisplayer>
            </div>
            <div v-else-if="kbCollections.length == 0" class="d-flex align-center justify-center flex-column"
                style="flex-grow: 1; width: 100%; height: 100%;">
                <h2>{{ tm('empty.title') }}</h2>
                <v-btn style="margin-top: 16px;" variant="tonal" color="primary" @click="showCreateDialog = true">
                    {{ tm('empty.create') }}
                </v-btn>
            </div>
            <div v-else>
                <h2 class="mb-4">{{ tm('list.title') }}
                    <v-icon class="ml-2" size="x-small" color="grey"
                        @click="openUrl('https://astrbot.app/use/knowledge-base.html')">mdi-information-outline</v-icon>
                </h2>
                <v-btn class="mb-4" prepend-icon="mdi-plus" variant="tonal" color="primary"
                    @click="showCreateDialog = true">
                    {{ tm('list.create') }}
                </v-btn>
                <v-btn class="mb-4 ml-4" prepend-icon="mdi-cog" variant="tonal" color="success"
                    @click="$router.push('/extension?open_config=astrbot_plugin_knowledge_base')">
                    {{ tm('list.config') }}
                </v-btn>
                <v-btn class="mb-4 ml-4" prepend-icon="mdi-update" variant="tonal" color="warning"
                    @click="checkPluginUpdate" :loading="checkingUpdate">
                    {{ tm('list.checkUpdate') }}
                </v-btn>
                <v-btn v-if="pluginHasUpdate" class="mb-4 ml-4" prepend-icon="mdi-download" variant="tonal"
                    color="primary" @click="updatePlugin" :loading="updatingPlugin">
                    {{ tm('list.updatePlugin', { version: pluginLatestVersion }) }}
                </v-btn>

                <div class="kb-grid">
                    <div v-for="(kb, index) in kbCollections" :key="index" class="kb-card"
                        @click="openKnowledgeBase(kb)">
                        <div class="book-spine"></div>
                        <div class="book-content">
                            <div class="emoji-container">
                                <span class="kb-emoji">{{ kb.emoji || '🙂' }}</span>
                            </div>
                            <div class="kb-name">{{ kb.collection_name }}</div>
                            <div class="kb-count">{{ kb.count || 0 }} {{ tm('list.knowledgeCount') }}</div>
                            <div class="kb-actions">
                                <v-btn icon variant="text" size="small" color="error" @click.stop="confirmDelete(kb)">
                                    <v-icon>mdi-delete</v-icon>
                                </v-btn>
                            </div>
                        </div>
                    </div>
                </div>

            </div>

        </div>

        <!-- 创建知识库对话框 -->
        <v-dialog v-model="showCreateDialog" max-width="500px">
            <v-card>
                <v-card-title class="text-h4">{{ tm('createDialog.title') }}</v-card-title>
                <v-card-text>

                    <div style="width: 100%; display: flex; align-items: center; justify-content: center;">
                        <span id="emoji-display" @click="showEmojiPicker = true">
                            {{ newKB.emoji || '🙂' }}
                        </span>
                    </div>
                    <v-form @submit.prevent="submitCreateForm">


                        <v-text-field variant="outlined" v-model="newKB.name" :label="tm('createDialog.nameLabel')"
                            required></v-text-field>

                        <v-textarea v-model="newKB.description" :label="tm('createDialog.descriptionLabel')"
                            variant="outlined" :placeholder="tm('createDialog.descriptionPlaceholder')"
                            rows="3"></v-textarea>

                        <v-select v-model="newKB.embedding_provider_id" :items="embeddingProviderConfigs"
                            :item-props="embeddingModelProps" :label="tm('createDialog.embeddingModelLabel')"
                            variant="outlined" density="comfortable">
                        </v-select>

                        <v-select v-model="newKB.rerank_provider_id" :items="rerankProviderConfigs"
                            :item-props="rerankModelProps" :label="tm('createDialog.rerankModelLabel')"
                            variant="outlined" density="comfortable">
                        </v-select>

                        <small>{{ tm('createDialog.tips') }}</small>
                    </v-form>
                </v-card-text>
                <v-card-actions>
                    <v-spacer></v-spacer>
                    <v-btn color="error" variant="text" @click="showCreateDialog = false">{{ tm('createDialog.cancel')
                        }}</v-btn>
                    <v-btn color="primary" variant="text" @click="submitCreateForm">{{ tm('createDialog.create')
                        }}</v-btn>
                </v-card-actions>
            </v-card>
        </v-dialog>

        <!-- 表情选择器对话框 -->
        <v-dialog v-model="showEmojiPicker" max-width="400px">
            <v-card>
                <v-card-title class="text-h6">{{ tm('emojiPicker.title') }}</v-card-title>
                <v-card-text>
                    <div class="emoji-picker">
                        <div v-for="(category, catIndex) in emojiCategories" :key="catIndex" class="mb-4">
                            <div class="text-subtitle-2 mb-2">{{ tm(`emojiPicker.categories.${category.key}`) }}</div>
                            <div class="emoji-grid">
                                <div v-for="(emoji, emojiIndex) in category.emojis" :key="emojiIndex" class="emoji-item"
                                    @click="selectEmoji(emoji)">
                                    {{ emoji }}
                                </div>
                            </div>
                        </div>
                    </div>
                </v-card-text>
                <v-card-actions>
                    <v-spacer></v-spacer>
                    <v-btn color="primary" variant="text" @click="showEmojiPicker = false">{{ tm('emojiPicker.close')
                        }}</v-btn>
                </v-card-actions>
            </v-card>
        </v-dialog>

        <!-- 知识库内容管理对话框 -->
        <v-dialog v-model="showContentDialog" max-width="1000px">
            <v-card>
                <v-card-title class="d-flex align-center">
                    <div class="me-2 emoji-sm">{{ currentKB.emoji || '🙂' }}</div>
                    <span>{{ currentKB.collection_name }} - {{ tm('contentDialog.title') }}</span>
                    <v-spacer></v-spacer>
                    <v-btn variant="plain" icon @click="showContentDialog = false">
                        <v-icon>mdi-close</v-icon>
                    </v-btn>
                </v-card-title>

                <div v-if="currentKB._embedding_provider_config" class="px-6 py-2">
                    <v-chip class="mr-2" color="primary" variant="tonal" size="small" rounded="sm">
                        <v-icon start size="small">mdi-database</v-icon>
                        {{ tm('contentDialog.embeddingModel') }}: {{
                            currentKB._embedding_provider_config.embedding_model }}
                    </v-chip>

                    <v-chip v-if="currentKB.rerank_provider_id" color="tertiary" variant="tonal" size="small"
                        rounded="sm">
                        <v-icon start size="small">mdi-sort-variant</v-icon>
                        重排序模型: {{rerankProviderConfigs.
                            find(provider => provider.id === currentKB.rerank_provider_id)?.rerank_model || '未设置'}}
                    </v-chip>
                    <small style="margin-left: 8px;">💡 使用方式: 在聊天页中输入 "/kb use {{ currentKB.collection_name }}"</small>
                </div>

                <v-card-text>
                    <v-tabs v-model="activeTab">
                        <v-tab value="import">导入数据</v-tab>
                        <v-tab value="search">{{ tm('contentDialog.tabs.search') }}</v-tab>
                    </v-tabs>

                    <v-window v-model="activeTab" class="mt-4">
                        <!-- 导入数据标签页 -->
                        <v-window-item value="import">
                            <div class="import-container pa-4">
                                <div class="mb-8">
                                    <h2>导入数据</h2>
                                    <p class="text-subtitle-1">选择数据源并导入内容到知识库</p>
                                </div>

                                <!-- 数据源选择下拉列表 -->
                                <v-select v-model="dataSource" :items="dataSourceOptions" :label="'数据源选择'"
                                    variant="outlined" item-title="title" item-value="value"
                                    prepend-inner-icon="mdi-database"></v-select>

                                <!-- 从文件导入 -->
                                <div v-if="dataSource === 'file'" class="mt-4">
                                    <div class="upload-zone" @dragover.prevent @drop.prevent="onFileDrop"
                                        @click="triggerFileInput">
                                        <input type="file" ref="fileInput" style="display: none"
                                            @change="onFileSelected" />
                                        <v-icon size="48" color="primary">mdi-cloud-upload</v-icon>
                                        <p class="mt-2">{{ tm('upload.dropzone') }}</p>
                                    </div>

                                    <!-- 分片长度和重叠长度设置 -->
                                    <v-card class="mt-4 chunk-settings-card" variant="outlined" color="grey-lighten-4">
                                        <v-card-title class="pa-4 pb-0 d-flex align-center">
                                            <v-icon color="primary" class="mr-2">mdi-puzzle-outline</v-icon>
                                            <span class="text-subtitle-1 font-weight-bold">{{
                                                tm('upload.chunkSettings.title') }}</span>
                                            <v-tooltip location="top">
                                                <template v-slot:activator="{ props }">
                                                    <v-icon v-bind="props" class="ml-2" size="small" color="grey">
                                                        mdi-information-outline
                                                    </v-icon>
                                                </template>
                                                <span>
                                                    {{ tm('upload.chunkSettings.tooltip') }}
                                                </span>
                                            </v-tooltip>
                                        </v-card-title>
                                        <v-card-text class="pa-4 pt-2">
                                            <div class="d-flex flex-wrap" style="gap: 8px">
                                                <v-text-field v-model="chunkSize"
                                                    :label="tm('upload.chunkSettings.chunkSizeLabel')" type="number"
                                                    :hint="tm('upload.chunkSettings.chunkSizeHint')" persistent-hint
                                                    variant="outlined" density="comfortable"
                                                    class="flex-grow-1 chunk-field"
                                                    prepend-inner-icon="mdi-text-box-outline" min="50"></v-text-field>

                                                <v-text-field v-model="overlap"
                                                    :label="tm('upload.chunkSettings.overlapLabel')" type="number"
                                                    :hint="tm('upload.chunkSettings.overlapHint')" persistent-hint
                                                    variant="outlined" density="comfortable"
                                                    class="flex-grow-1 chunk-field"
                                                    prepend-inner-icon="mdi-vector-intersection" min="0"></v-text-field>
                                            </div>
                                        </v-card-text>
                                    </v-card>

                                    <div class="selected-files mt-4" v-if="selectedFile">
                                        <div type="info" variant="tonal" class="d-flex align-center">
                                            <div>
                                                <v-icon class="me-2">{{ getFileIcon(selectedFile.name) }}</v-icon>
                                                <span style="font-weight: 1000;">{{ selectedFile.name }}</span>
                                            </div>
                                            <v-btn size="small" color="error" variant="text"
                                                @click="selectedFile = null">
                                                <v-icon>mdi-close</v-icon>
                                            </v-btn>
                                        </div>

                                        <div class="text-center mt-4">
                                            <v-btn color="primary" variant="elevated" :loading="uploading"
                                                :disabled="!selectedFile" @click="uploadFile">
                                                {{ tm('upload.upload') }}
                                            </v-btn>
                                        </div>
                                    </div>

                                    <div class="upload-progress mt-4" v-if="uploading">
                                        <v-progress-linear indeterminate color="primary"></v-progress-linear>
                                    </div>
                                </div>

                                <!-- 从URL导入 -->
                                <div v-if="dataSource === 'url'" class="from-url-container">
                                    <v-alert type="info" variant="tonal" class="mb-4" border>
                                        {{ tm('importFromUrl.preRequisite') }}
                                    </v-alert>
                                    <v-text-field v-model="importUrl" :label="tm('importFromUrl.urlLabel')"
                                        :placeholder="tm('importFromUrl.urlPlaceholder')" variant="outlined"
                                        class="mb-4" hide-details></v-text-field>

                                    <v-card class="mb-4" variant="outlined" color="grey-lighten-4">
                                        <v-card-title class="pa-4 pb-0 d-flex align-center">
                                            <v-icon color="primary" class="mr-2">mdi-cog-outline</v-icon>
                                            <span class="text-subtitle-1 font-weight-bold">{{
                                                tm('importFromUrl.optionsTitle') }}</span>
                                            <v-tooltip location="top">
                                                <template v-slot:activator="{ props }">
                                                    <v-icon v-bind="props" class="ml-2" size="small"
                                                        color="grey">mdi-information-outline</v-icon>
                                                </template>
                                                <span>{{ tm('importFromUrl.tooltip') }}</span>
                                            </v-tooltip>
                                        </v-card-title>
                                        <v-card-text class="pa-4 pt-2">
                                            <v-row>
                                                <v-col cols="12" md="6">
                                                    <v-switch hide-details v-model="importOptions.use_llm_repair"
                                                        :label="tm('importFromUrl.useLlmRepairLabel')" color="primary"
                                                        inset></v-switch>
                                                </v-col>
                                                <v-col cols="12" md="6">
                                                    <v-switch v-model="importOptions.use_clustering_summary"
                                                        hide-details
                                                        :label="tm('importFromUrl.useClusteringSummaryLabel')"
                                                        color="primary" inset></v-switch>
                                                </v-col>
                                                <v-row class="pa-4">
                                                    <!-- Optional Repair Selector -->
                                                    <v-col v-if="importOptions.use_llm_repair"
                                                        :md="optionalSelectorColWidth" cols="12">
                                                        <v-select v-model="importOptions.repair_llm_provider_id"
                                                            :items="llmProviderConfigs" item-value="id"
                                                            :item-props="llmModelProps"
                                                            :label="tm('importFromUrl.repairLlmProviderIdLabel')"
                                                            variant="outlined" clearable hide-details></v-select>
                                                    </v-col>

                                                    <!-- Optional Summary Selector -->
                                                    <v-col v-if="importOptions.use_clustering_summary"
                                                        :md="optionalSelectorColWidth" cols="12">
                                                        <v-select v-model="importOptions.summarize_llm_provider_id"
                                                            :items="llmProviderConfigs" item-value="id"
                                                            :item-props="llmModelProps"
                                                            :label="tm('importFromUrl.summarizeLlmProviderIdLabel')"
                                                            variant="outlined" clearable hide-details></v-select>
                                                    </v-col>

                                                    <v-col cols="12" md="6">
                                                        <v-select v-model="importOptions.embedding_provider_id"
                                                            :items="embeddingProviderConfigs" item-value="id"
                                                            :item-props="embeddingModelProps"
                                                            :label="tm('importFromUrl.embeddingProviderIdLabel')"
                                                            variant="outlined" clearable hide-details></v-select>
                                                    </v-col>
                                                    <v-col cols="12" md="3">
                                                        <v-text-field v-model="importOptions.chunk_size"
                                                            :label="tm('importFromUrl.chunkSizeLabel')" type="number"
                                                            variant="outlined" clearable hide-details></v-text-field>
                                                    </v-col>
                                                    <v-col cols="12" md="3">
                                                        <v-text-field v-model="importOptions.chunk_overlap"
                                                            :label="tm('importFromUrl.chunkOverlapLabel')" type="number"
                                                            variant="outlined" clearable hide-details></v-text-field>
                                                    </v-col>
                                                </v-row>
                                            </v-row>
                                        </v-card-text>
                                    </v-card>

                                    <div class="text-center">
                                        <v-btn color="primary" variant="elevated" :loading="importing"
                                            :disabled="!importUrl" @click="startImportFromUrl">
                                            {{ tm('importFromUrl.startImport') }}
                                        </v-btn>
                                    </div>
                                </div>
                            </div>
                        </v-window-item>

                        <!-- 搜索内容标签页 -->
                        <v-window-item value="search">
                            <div class="search-container pa-4">
                                <v-form @submit.prevent="searchKnowledgeBase" class="d-flex align-center">
                                    <v-text-field :model-value="searchQuery"
                                        @update:model-value="onSearchQueryInput" :label="tm('search.queryLabel')"
                                        append-icon="mdi-magnify" variant="outlined" class="flex-grow-1 me-2"
                                        @click:append="searchKnowledgeBase" @keyup.enter="searchKnowledgeBase"
                                        :placeholder="tm('search.queryPlaceholder')" hide-details clearable></v-text-field>

                                    <v-select v-model="topK" :items="[3, 5, 10, 20]"
                                        :label="tm('search.resultCountLabel')" variant="outlined"
                                        style="max-width: 120px;" hide-details></v-select>
                                </v-form>

                                <div class="search-results mt-4">
                                    <div v-if="searching">
                                        <v-progress-linear indeterminate color="primary"></v-progress-linear>
                                        <p class="text-center mt-4">{{ tm('search.searching') }}</p>
                                    </div>

                                    <div v-else-if="searchResults.length > 0">
                                        <h3 class="mb-2">{{ tm('search.resultsTitle') }}</h3>
                                        <v-card v-for="(result, index) in searchResults" :key="index"
                                            class="mb-4 search-result-card" variant="outlined">
                                            <v-card-text>
                                                <div class="d-flex align-center mb-2">
                                                    <v-icon class="me-2" size="small"
                                                        color="primary">mdi-file-document-outline</v-icon>
                                                    <span class="text-caption text-medium-emphasis">{{
                                                        result.metadata.source }}</span>
                                                    <v-spacer></v-spacer>
                                                    <v-chip v-if="result.score" size="small" color="primary"
                                                        variant="tonal">
                                                        {{ tm('search.relevance') }}: {{ Math.round(result.score * 100)
                                                        }}%
                                                    </v-chip>
                                                </div>
                                                <div class="search-content">{{ result.content }}</div>
                                            </v-card-text>
                                        </v-card>
                                    </div>

                                    <div v-else-if="searchPerformed">
                                        <v-alert type="info" variant="tonal">
                                            {{ tm('search.noResults') }}
                                        </v-alert>
                                    </div>
                                </div>
                            </div>
                        </v-window-item>
                    </v-window>
                </v-card-text>
            </v-card>
        </v-dialog>

        <!-- 删除知识库确认对话框 -->
        <v-dialog v-model="showDeleteDialog" max-width="400px">
            <v-card>
                <v-card-title class="text-h5">{{ tm('deleteDialog.title') }}</v-card-title>
                <v-card-text>
                    <p>{{ tm('deleteDialog.confirmText', { name: deleteTarget.collection_name }) }}</p>
                    <p class="text-red">{{ tm('deleteDialog.warning') }}</p>
                </v-card-text>
                <v-card-actions>
                    <v-spacer></v-spacer>
                    <v-btn color="grey-darken-1" variant="text" @click="showDeleteDialog = false">{{
                        tm('deleteDialog.cancel')
                        }}</v-btn>
                    <v-btn color="error" variant="text" @click="deleteKnowledgeBase" :loading="deleting">{{
                        tm('deleteDialog.delete') }}</v-btn>
                </v-card-actions>
            </v-card>
        </v-dialog>

        <!-- 消息提示 -->
        <v-snackbar v-model="snackbar.show" :color="snackbar.color">
            {{ snackbar.text }}
        </v-snackbar>
    </div>
</template>

<script>
import axios from 'axios';
import ConsoleDisplayer from '@/components/shared/ConsoleDisplayer.vue';
import { useModuleI18n } from '@/i18n/composables';
import { normalizeTextInput } from '@/utils/inputValue';

export default {
    name: 'KnowledgeBase',
    components: {
        ConsoleDisplayer,
    },
    setup() {
        const { tm } = useModuleI18n('features/alkaid/knowledge-base');
        return { tm };
    },
    data() {
        return {
            installed: true,
            installing: false,
            kbCollections: [],
            showCreateDialog: false,
            showEmojiPicker: false,
            newKB: {
                name: '',
                emoji: '🙂',
                description: '',
                embedding_provider_id: null,
                rerank_provider_id: null,
            },
            snackbar: {
                show: false,
                text: '',
                color: 'success'
            },
            emojiCategories: [
                {
                    key: 'emotions',
                    emojis: ['😀', '😃', '😄', '😁', '😆', '😅', '🤣', '😂', '🙂', '🙃', '😉', '😊', '😇', '🥰', '😍', '🤩', '😘']
                },
                {
                    key: 'animals',
                    emojis: ['🐶', '🐱', '🐭', '🐹', '🐰', '🦊', '🐻', '🐼', '🐨', '🐯', '🦁', '🐮', '🐷', '🐸', '🐵']
                },
                {
                    key: 'food',
                    emojis: ['🍏', '🍎', '🍐', '🍊', '🍋', '🍌', '🍉', '🍇', '🍓', '🍈', '🍒', '🍑', '🥭', '🍍', '🥥']
                },
                {
                    key: 'activities',
                    emojis: ['⚽', '🏀', '🏈', '⚾', '🥎', '🎾', '🏐', '🏉', '🎱', '🏓', '🏸', '🥅', '🏒', '🏑', '🥍']
                },
                {
                    key: 'travel',
                    emojis: ['🚗', '🚕', '🚙', '🚌', '🚎', '🏎️', '🚓', '🚑', '🚒', '🚐', '🚚', '🚛', '🚜', '🛴', '🚲']
                },
                {
                    key: 'symbols',
                    emojis: ['❤️', '🧡', '💛', '💚', '💙', '💜', '🖤', '🤍', '🤎', '💔', '❣️', '💕', '💞', '💓', '💗']
                }
            ],
            showContentDialog: false,
            currentKB: {
                collection_name: '',
                emoji: ''
            },
            activeTab: 'import',
            dataSource: 'file',
            dataSourceOptions: [
                { title: '从文件', value: 'file', icon: 'mdi-file-upload' },
                { title: '从URL', value: 'url', icon: 'mdi-web' }
            ],
            selectedFile: null,
            chunkSize: null,
            overlap: null,
            uploading: false,
            searchQuery: '',
            searchResults: [],
            searching: false,
            searchPerformed: false,
            topK: 5,
            showDeleteDialog: false,
            deleteTarget: {
                collection_name: ''
            },
            deleting: false,
            embeddingProviderConfigs: [],
            rerankProviderConfigs: [],
            llmProviderConfigs: [],
            // URL导入相关数据
            importUrl: '',
            importOptions: {
                use_llm_repair: true,
                use_clustering_summary: false,
                repair_llm_provider_id: null,
                summarize_llm_provider_id: null,
                embedding_provider_id: null,
                chunk_size: 300,
                chunk_overlap: 50,
            },
            importing: false,
            pollingInterval: null,
            // 插件更新相关
            checkingUpdate: false,
            updatingPlugin: false,
            pluginHasUpdate: false,
            pluginCurrentVersion: '',
            pluginLatestVersion: '',
        }
    },
    computed: {
        optionalSelectorColWidth() {
            const repairOn = this.importOptions.use_llm_repair;
            const summaryOn = this.importOptions.use_clustering_summary;
            if (repairOn && summaryOn) {
                return 6; // Both on, each takes half
            }
            return 12; // Only one is on, it takes full width
        }
    },
    watch: {
        llmProviderConfigs: {
            handler(newVal) {
                if (newVal && newVal.length > 0) {
                    if (!this.importOptions.repair_llm_provider_id) {
                        this.importOptions.repair_llm_provider_id = newVal[0].id;
                    }
                    if (!this.importOptions.summarize_llm_provider_id) {
                        this.importOptions.summarize_llm_provider_id = newVal[0].id;
                    }
                }
            },
            immediate: true,
            deep: true
        },
        embeddingProviderConfigs: {
            handler(newVal) {
                if (newVal && newVal.length > 0) {
                    if (!this.importOptions.embedding_provider_id) {
                        this.importOptions.embedding_provider_id = newVal[0].id;
                    }
                }
            },
            immediate: true,
            deep: true
        }
    },
    mounted() {
        this.checkPlugin();
        this.getProviderList();
    },
    methods: {
        onSearchQueryInput(value) {
            this.searchQuery = normalizeTextInput(value);
        },
        getSelectedGitHubProxy() {
            if (typeof window === "undefined" || !window.localStorage) return "";
            return localStorage.getItem("githubProxyRadioValue") === "1"
                ? localStorage.getItem("selectedGitHubProxy") || ""
                : "";
        },
        llmModelProps(providerConfig) {
            return {
                title: providerConfig.llm_model || providerConfig.id,
                subtitle: `Provider ID: ${providerConfig.id}`,
            }
        },
        embeddingModelProps(providerConfig) {
            return {
                title: providerConfig.embedding_model,
                subtitle: this.tm('createDialog.providerInfo', {
                    id: providerConfig.id,
                    dimensions: providerConfig.embedding_dimensions
                }),
            }
        },
        rerankModelProps(providerConfig) {
            return {
                title: providerConfig.rerank_model,
                subtitle: this.tm('createDialog.rerankProviderInfo', {
                    id: providerConfig.id,
                }),
            }
        },
        checkPlugin() {
            axios.get('/api/plugin/get?name=astrbot_plugin_knowledge_base')
                .then(response => {
                    if (response.data.status !== 'ok' || response.data.data.length === 0) {
                        this.showSnackbar(this.tm('messages.pluginNotAvailable'), 'error');
                        this.installed = false;
                        return
                    }
                    if (!response.data.data[0].activated) {
                        this.showSnackbar(this.tm('messages.pluginNotActivated'), 'error');
                        return
                    }
                    if (response.data.data.length > 0) {
                        this.installed = true;
                        this.pluginCurrentVersion = response.data.data[0].version || '未知';
                        this.getKBCollections();
                        // 自动检查更新
                        this.checkPluginUpdate();
                    } else {
                        this.installed = false;
                    }
                })
                .catch(error => {
                    console.error('Error checking plugin:', error);
                    this.showSnackbar(this.tm('messages.checkPluginFailed'), 'error');
                })
        },

        async checkPluginUpdate() {
            this.checkingUpdate = true;
            this.pluginHasUpdate = false;
            try {
                // 获取在线插件数据
                const onlineResponse = await axios.get('/api/plugin/market_list');
                if (onlineResponse.data.status === 'ok') {
                    const knowledgeBasePlugin = onlineResponse.data.data['astrbot_plugin_knowledge_base'];
                    if (knowledgeBasePlugin) {
                        this.pluginLatestVersion = knowledgeBasePlugin.version || '未知';

                        // 比较版本
                        if (this.pluginCurrentVersion && this.pluginLatestVersion &&
                            this.pluginCurrentVersion !== '未知' && this.pluginLatestVersion !== '未知') {
                            this.pluginHasUpdate = this.pluginCurrentVersion != this.pluginLatestVersion
                        }

                        if (this.pluginHasUpdate) {
                            this.showSnackbar(this.tm('messages.updateAvailable', {
                                current: this.pluginCurrentVersion,
                                latest: this.pluginLatestVersion
                            }), 'info');
                        } else {
                            this.showSnackbar(this.tm('messages.pluginUpToDate'), 'success');
                        }
                    } else {
                        this.showSnackbar(this.tm('messages.pluginNotFoundInMarket'), 'warning');
                    }
                } else {
                    this.showSnackbar(this.tm('messages.checkUpdateFailed'), 'error');
                }
            } catch (error) {
                console.error('Error checking plugin update:', error);
                this.showSnackbar(this.tm('messages.checkUpdateFailed'), 'error');
            } finally {
                this.checkingUpdate = false;
            }
        },

        async updatePlugin() {
            this.updatingPlugin = true;
            try {
                const response = await axios.post('/api/plugin/update', {
                    name: 'astrbot_plugin_knowledge_base',
                    proxy: this.getSelectedGitHubProxy()
                });

                if (response.data.status === 'ok') {
                    this.showSnackbar(this.tm('messages.updateSuccess'), 'success');
                    this.pluginHasUpdate = false;
                    this.pluginCurrentVersion = this.pluginLatestVersion;
                    // 刷新插件信息
                    this.checkPlugin();
                } else {
                    this.showSnackbar(response.data.message || this.tm('messages.updateFailed'), 'error');
                }
            } catch (error) {
                console.error('Error updating plugin:', error);
                this.showSnackbar(this.tm('messages.updatePluginFailed'), 'error');
            } finally {
                this.updatingPlugin = false;
            }
        },

        installPlugin() {
            this.installing = true;
            axios.post('/api/plugin/install', {
                url: "https://github.com/lxfight/astrbot_plugin_knowledge_base",
                proxy: this.getSelectedGitHubProxy()
            })
                .then(response => {
                    if (response.data.status === 'ok') {
                        this.checkPlugin();
                    } else {
                        this.showSnackbar(response.data.message || this.tm('messages.installFailed'), 'error');
                    }
                })
                .catch(error => {
                    console.error('Error installing plugin:', error);
                    this.showSnackbar(this.tm('messages.installPluginFailed'), 'error');
                }).finally(() => {
                    this.installing = false;
                });
        },

        getKBCollections() {
            axios.get('/api/plug/alkaid/kb/collections')
                .then(response => {
                    if (response.data.status !== 'ok') {
                        this.showSnackbar(response.data.message || this.tm('messages.getKnowledgeBaseListFailed'), 'error');
                        return;
                    }
                    this.kbCollections = response.data.data;
                })
                .catch(error => {
                    console.error('Error fetching knowledge base collections:', error);
                    this.showSnackbar(this.tm('messages.getKnowledgeBaseListFailed'), 'error');
                });
        },

        createCollection(name, emoji, description) {
            // 如果 this.newKB.embedding_provider_id 是 Object
            if (this.newKB.embedding_provider_id && typeof this.newKB.embedding_provider_id === 'object') {
                this.newKB.embedding_provider_id = this.newKB.embedding_provider_id.id || '';
            }
            if (this.newKB.rerank_provider_id && typeof this.newKB.rerank_provider_id === 'object') {
                this.newKB.rerank_provider_id = this.newKB.rerank_provider_id.id || '';
            }
            axios.post('/api/plug/alkaid/kb/create_collection', {
                collection_name: name,
                emoji: emoji,
                description: description,
                embedding_provider_id: this.newKB.embedding_provider_id || '',
                rerank_provider_id: this.newKB.rerank_provider_id || ''
            })
                .then(response => {
                    if (response.data.status === 'ok') {
                        this.showSnackbar(this.tm('messages.knowledgeBaseCreated'));
                        this.getKBCollections();
                        this.showCreateDialog = false;
                        this.resetNewKB();
                    } else {
                        this.showSnackbar(response.data.message || this.tm('messages.createFailed'), 'error');
                    }
                })
                .catch(error => {
                    console.error('Error creating knowledge base collection:', error);
                    this.showSnackbar(this.tm('messages.createKnowledgeBaseFailed'), 'error');
                });
        },

        submitCreateForm() {
            if (!this.newKB.name) {
                this.showSnackbar(this.tm('messages.pleaseEnterKnowledgeBaseName'), 'warning');
                return;
            }
            this.createCollection(
                this.newKB.name,
                this.newKB.emoji || '🙂',
                this.newKB.description,
                this.newKB.embedding_provider_id || ''
            );
        },

        resetNewKB() {
            this.newKB = {
                name: '',
                emoji: '🙂',
                description: '',
                embedding_provider: ''
            };
        },

        openKnowledgeBase(kb) {
            // 不再跳转路由，而是打开对话框
            this.currentKB = kb;
            this.showContentDialog = true;
            this.resetContentDialog();
        },

        resetContentDialog() {
            this.activeTab = 'import';
            this.dataSource = 'file';
            this.selectedFile = null;
            this.searchQuery = '';
            this.searchResults = [];
            this.searchPerformed = false;
            // 重置分片长度和重叠长度参数
            this.chunkSize = null;
            this.overlap = null;
            // 重置URL导入相关数据
            this.importUrl = '';
            this.importing = false;
            if (this.pollingInterval) {
                clearInterval(this.pollingInterval);
                this.pollingInterval = null;
            }
        },

        triggerFileInput() {
            this.$refs.fileInput.click();
        },

        onFileSelected(event) {
            const files = event.target.files;
            if (files.length > 0) {
                this.selectedFile = files[0];
            }
        },

        onFileDrop(event) {
            const files = event.dataTransfer.files;
            if (files.length > 0) {
                this.selectedFile = files[0];
            }
        },

        getFileIcon(filename) {
            const extension = filename.split('.').pop().toLowerCase();

            switch (extension) {
                case 'pdf':
                    return 'mdi-file-pdf-box';
                case 'doc':
                case 'docx':
                    return 'mdi-file-word-box';
                case 'xls':
                case 'xlsx':
                    return 'mdi-file-excel-box';
                case 'ppt':
                case 'pptx':
                    return 'mdi-file-powerpoint-box';
                case 'txt':
                    return 'mdi-file-document-outline';
                default:
                    return 'mdi-file-outline';
            }
        },

        uploadFile() {
            if (!this.selectedFile) {
                this.showSnackbar(this.tm('messages.pleaseSelectFile'), 'warning');
                return;
            }

            this.uploading = true;

            const formData = new FormData();
            formData.append('file', this.selectedFile);
            formData.append('collection_name', this.currentKB.collection_name);

            // 添加可选的分片长度和重叠长度参数
            if (this.chunkSize && this.chunkSize > 0) {
                formData.append('chunk_size', this.chunkSize);
            }

            if (this.overlap && this.overlap >= 0) {
                formData.append('chunk_overlap', this.overlap);
            }

            axios.post('/api/plug/alkaid/kb/collection/add_file', formData, {
                headers: {
                    'Content-Type': 'multipart/form-data'
                }
            })
                .then(response => {
                    if (response.data.status === 'ok') {
                        this.showSnackbar(this.tm('messages.operationSuccess', { message: response.data.message }));
                        this.selectedFile = null;

                        // 刷新知识库列表，获取更新的数量
                        this.getKBCollections();
                    } else {
                        this.showSnackbar(response.data.message || this.tm('messages.uploadFailed'), 'error');
                    }
                })
                .catch(error => {
                    console.error('Error uploading file:', error);
                    this.showSnackbar(this.tm('messages.fileUploadFailed'), 'error');
                })
                .finally(() => {
                    this.uploading = false;
                });
        },

        searchKnowledgeBase() {
            const query = normalizeTextInput(this.searchQuery).trim();
            if (!query) {
                this.showSnackbar(this.tm('messages.pleaseEnterSearchContent'), 'warning');
                return;
            }

            this.searching = true;
            this.searchPerformed = true;

            axios.get(`/api/plug/alkaid/kb/collection/search`, {
                params: {
                    collection_name: this.currentKB.collection_name,
                    query,
                    top_k: this.topK
                }
            })
                .then(response => {
                    if (response.data.status === 'ok') {
                        this.searchResults = response.data.data || [];

                        if (this.searchResults.length === 0) {
                            this.showSnackbar(this.tm('messages.noMatchingContent'), 'info');
                        }
                    } else {
                        this.showSnackbar(response.data.message || this.tm('messages.searchFailed'), 'error');
                        this.searchResults = [];
                    }
                })
                .catch(error => {
                    console.error('Error searching knowledge base:', error);
                    this.showSnackbar(this.tm('messages.searchKnowledgeBaseFailed'), 'error');
                    this.searchResults = [];
                })
                .finally(() => {
                    this.searching = false;
                });
        },

        showSnackbar(text, color = 'success') {
            this.snackbar.text = text;
            this.snackbar.color = color;
            this.snackbar.show = true;
        },

        selectEmoji(emoji) {
            this.newKB.emoji = emoji;
            this.showEmojiPicker = false;
        },

        confirmDelete(kb) {
            this.deleteTarget = kb;
            this.showDeleteDialog = true;
        },

        deleteKnowledgeBase() {
            if (!this.deleteTarget.collection_name) {
                this.showSnackbar(this.tm('messages.deleteTargetNotExists'), 'error');
                return;
            }

            this.deleting = true;

            axios.get('/api/plug/alkaid/kb/collection/delete', {
                params: {
                    collection_name: this.deleteTarget.collection_name
                }
            })
                .then(response => {
                    if (response.data.status === 'ok') {
                        this.showSnackbar(this.tm('messages.knowledgeBaseDeleted'));
                        this.getKBCollections(); // 刷新列表
                        this.showDeleteDialog = false;
                    } else {
                        this.showSnackbar(response.data.message || this.tm('messages.deleteFailed'), 'error');
                    }
                })
                .catch(error => {
                    console.error('Error deleting knowledge base:', error);
                    this.showSnackbar(this.tm('messages.deleteKnowledgeBaseFailed'), 'error');
                })
                .finally(() => {
                    this.deleting = false;
                });
        },

        getProviderList() {
            axios.get('/api/config/provider/list', {
                params: {
                    provider_type: 'embedding,rerank,chat_completion'
                }
            })
                .then(response => {
                    if (response.data.status === 'ok') {
                        this.embeddingProviderConfigs = response.data.data.filter(provider => provider.provider_type === 'embedding');
                        this.rerankProviderConfigs = response.data.data.filter(provider => provider.provider_type === 'rerank');
                        this.llmProviderConfigs = response.data.data.filter(provider => provider.provider_type === 'chat_completion');
                    } else {
                        this.showSnackbar(response.data.message || this.tm('messages.getEmbeddingModelListFailed'), 'error');
                        return [];
                    }
                })
                .catch(error => {
                    console.error('Error fetching embedding providers:', error);
                    this.showSnackbar(this.tm('messages.getEmbeddingModelListFailed'), 'error');
                    return [];
                });
        },

        openUrl(url) {
            window.open(url, '_blank');
        },

        // URL导入相关方法
        async startImportFromUrl() {
            if (!this.importUrl) {
                this.showSnackbar('Please enter a URL', 'warning');
                return;
            }

            this.importing = true;

            try {
                const payload = {
                    url: this.importUrl,
                    ...Object.fromEntries(Object.entries(this.importOptions).filter(([_, v]) => v !== '' && v !== null && v !== undefined))
                };

                console.log('Starting URL import with payload:', JSON.stringify(payload, null, 2));
                const addTaskResponse = await axios.post('/api/plug/url_2_kb/add', payload);

                if (!addTaskResponse.data.task_id) {
                    throw new Error(addTaskResponse.data.message || 'Failed to start import task: No task_id received.');
                }

                const taskId = addTaskResponse.data.task_id;
                this.pollTaskStatus(taskId);

            } catch (error) {
                const errorMessage = error.response?.data?.message || error.message || 'An unknown error occurred.';
                this.showSnackbar(`Error: ${errorMessage}`, 'error');
                this.importing = false;
            }
        },

        pollTaskStatus(taskId) {
            this.pollingInterval = setInterval(async () => {
                try {
                    const statusResponse = await axios.post(`/api/plug/url_2_kb/status`, { task_id: taskId });

                    const taskData = statusResponse.data;
                    const taskStatus = taskData.status;

                    if (taskStatus === 'completed') {
                        clearInterval(this.pollingInterval);
                        this.pollingInterval = null;
                        this.showSnackbar(this.tm('importFromUrl.uploadingChunks'), 'info');
                        this.handleImportResult(taskData);
                    } else if (taskStatus === 'failed') {
                        clearInterval(this.pollingInterval);
                        this.pollingInterval = null;
                        const failureReason = taskData.result || 'Unknown reason.';
                        this.showSnackbar(`${this.tm('importFromUrl.importFailed')}: ${failureReason}`, 'error');
                        this.importing = false;
                    }
                } catch (error) {
                    clearInterval(this.pollingInterval);
                    this.pollingInterval = null;
                    const errorMessage = error.response?.data?.message || error.message || 'An unknown error occurred during polling.';
                    this.showSnackbar(`Polling Error: ${errorMessage}`, 'error');
                    this.importing = false;
                }
            }, 3000);
        },

        async handleImportResult(data) {
            const chunks = [];
            const result = data.result;

            // 1. Handle overall summary
            if (result.overall_summary) {
                chunks.push({ content: result.overall_summary, filename: 'overall_summary.txt' });
            }

            // 2. Handle topic summaries
            if (result.topics && result.topics.length > 0) {
                result.topics.forEach(topic => {
                    if (topic.topic_summary) {
                        chunks.push({ content: topic.topic_summary, filename: `topic_${topic.topic_id}_summary.txt` });
                    }
                });
            }

            // 3. Handle noise points
            if (result.noise_points && result.noise_points.length > 0) {
                result.noise_points.forEach((point, index) => {
                    const content = typeof point === 'object' && point.text ? point.text : point;
                    chunks.push({ content: content, filename: `noise_${index + 1}.txt` });
                });
            }

            if (chunks.length === 0) {
                this.showSnackbar('URL processed, but no text chunks were extracted.', 'info');
                this.importing = false;
                return;
            }

            let successCount = 0;
            let failCount = 0;

            for (let i = 0; i < chunks.length; i++) {
                const chunk = chunks[i];
                try {
                    await this.uploadChunkAsFile(chunk.content, chunk.filename);
                    successCount++;
                } catch (error) {
                    failCount++;
                }
            }

            if (failCount === 0) {
                this.showSnackbar(this.tm('importFromUrl.allChunksUploaded'), 'success');
            } else if (successCount > 0) {
                this.showSnackbar('Import partially complete. See console for details.', 'warning');
            } else {
                this.showSnackbar('Import failed. No chunks were uploaded.', 'error');
            }

            this.importing = false;
            this.getKBCollections();
        },

        async uploadChunkAsFile(content, filename) {
            const blob = new Blob([content], { type: 'text/plain' });
            const file = new File([blob], filename, { type: 'text/plain' });

            const formData = new FormData();
            formData.append('file', file);
            formData.append('collection_name', this.currentKB.collection_name);

            if (this.importOptions.chunk_size && this.importOptions.chunk_size > 0) {
                formData.append('chunk_size', this.importOptions.chunk_size);
            }
            if (this.importOptions.chunk_overlap && this.importOptions.chunk_overlap >= 0) {
                formData.append('chunk_overlap', this.importOptions.chunk_overlap);
            }

            const response = await axios.post('/api/plug/alkaid/kb/collection/add_file', formData, {
                headers: {
                    'Content-Type': 'multipart/form-data'
                }
            });

            if (response.data.status !== 'ok') {
                throw new Error(response.data.message || 'Chunk upload failed');
            }
            return response.data;
        },
    },
    beforeUnmount() {
        if (this.pollingInterval) {
            clearInterval(this.pollingInterval);
        }
    },
}
</script>

<style scoped>
.kb-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    gap: 24px;
    margin-top: 16px;
}

.kb-card {
    height: 280px;
    border-radius: 8px;
    overflow: hidden;
    position: relative;
    cursor: pointer;
    display: flex;
    background-color: var(--v-theme-background);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
    transition: all 0.3s ease;
}

.kb-card:hover {
    transform: translateY(-5px);
    box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
}

.book-spine {
    width: 12px;
    background-color: #5c6bc0;
    height: 100%;
    border-radius: 2px 0 0 2px;
}

.book-content {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: linear-gradient(145deg, #f5f7fa 0%, #e4e8f0 100%);
}

.emoji-container {
    width: 80px;
    height: 80px;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--v-theme-background);
    border-radius: 50%;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
    margin-bottom: 16px;
}

.kb-emoji {
    font-size: 40px;
}

.kb-name {
    font-weight: bold;
    font-size: 18px;
    margin-bottom: 8px;
    text-align: center;
    color: #333;
}

.kb-count {
    font-size: 14px;
    color: #666;
}

.emoji-picker {
    max-height: 300px;
    overflow-y: auto;
}

.emoji-grid {
    display: grid;
    grid-template-columns: repeat(8, 1fr);
    gap: 8px;
}

.emoji-item {
    font-size: 24px;
    padding: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    border-radius: 4px;
    transition: background-color 0.2s ease;
}

.emoji-item:hover {
    background-color: rgba(0, 0, 0, 0.05);
}

#emoji-display {
    font-size: 64px;
    cursor: pointer;
    transition: transform 0.2s ease;
}

#emoji-display:hover {
    transform: scale(1.1);
}

.emoji-sm {
    font-size: 24px;
}

.upload-zone {
    border: 2px dashed #ccc;
    border-radius: 8px;
    padding: 32px;
    text-align: center;
    cursor: pointer;
    transition: all 0.3s ease;
}

.upload-zone:hover {
    border-color: #5c6bc0;
    background-color: rgba(92, 107, 192, 0.05);
}

.search-container {
    min-height: 300px;
}

.search-result-card {
    transition: all 0.2s ease;
}

.search-result-card:hover {
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}

.search-content {
    white-space: pre-line;
    max-height: 200px;
    overflow-y: auto;
    font-size: 0.95rem;
    line-height: 1.6;
    padding: 8px;
    background-color: rgba(0, 0, 0, 0.02);
    border-radius: 4px;
}

.kb-actions {
    position: absolute;
    bottom: 10px;
    right: 10px;
    display: flex;
    gap: 8px;
    opacity: 0;
    transition: opacity 0.2s ease;
}

.kb-card {
    position: relative;
}

.kb-card:hover .kb-actions {
    opacity: 1;
}

.chunk-settings-card {
    border: 1px solid rgba(92, 107, 192, 0.2) !important;
    transition: all 0.3s ease;
}

.chunk-settings-card:hover {
    border-color: rgba(92, 107, 192, 0.4) !important;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.07) !important;
}

.chunk-field :deep(.v-field__input) {
    padding-top: 8px;
    padding-bottom: 8px;
}

.chunk-field :deep(.v-field__prepend-inner) {
    padding-right: 8px;
    opacity: 0.7;
}

.chunk-field:focus-within :deep(.v-field__prepend-inner) {
    opacity: 1;
}

.import-container,
.from-url-container {
    min-height: 400px;
}

.data-source-select :deep(.v-field__prepend-inner) {
    padding-right: 12px;
}
</style>
