<template>
    <v-card class="chat-page-card">
        <v-card-text class="chat-page-container">
            <div class="chat-layout">
                <div class="sidebar-panel" :class="{ 'sidebar-collapsed': sidebarCollapsed }"
                    :style="{ 'background-color': isDark ? sidebarCollapsed ? '#1e1e1e' : '#2d2d2d' : sidebarCollapsed ? '#ffffff' : '#f5f5f5' }"
                    @mouseenter="handleSidebarMouseEnter" @mouseleave="handleSidebarMouseLeave">

                    <div style="display: flex; align-items: center; justify-content: center; padding: 16px; padding-bottom: 0px;"
                        v-if="chatboxMode">
                        <img width="50" src="@/assets/images/astrbot_logo_mini.webp" alt="AstrBot Logo">
                        <span v-if="!sidebarCollapsed"
                            style="font-weight: 1000; font-size: 26px; margin-left: 8px;">AstrBot</span>
                    </div>


                    <div class="sidebar-collapse-btn-container">
                        <v-btn icon class="sidebar-collapse-btn" @click="toggleSidebar" variant="text"
                            color="deep-purple">
                            <v-icon>{{ (sidebarCollapsed || (!sidebarCollapsed && sidebarHoverExpanded)) ?
                                'mdi-chevron-right' : 'mdi-chevron-left' }}</v-icon>
                        </v-btn>
                    </div>

                    <div style="padding: 16px; padding-top: 8px;">
                        <v-btn block variant="text" class="new-chat-btn" @click="newC" :disabled="!currCid"
                            v-if="!sidebarCollapsed" prepend-icon="mdi-plus"
                            style="background-color: transparent !important; border-radius: 4px;">{{
                                tm('actions.newChat') }}</v-btn>
                        <v-btn icon="mdi-plus" rounded="lg" @click="newC" :disabled="!currCid" v-if="sidebarCollapsed"
                            elevation="0"></v-btn>
                    </div>
                    <div v-if="!sidebarCollapsed">
                        <v-divider class="mx-4"></v-divider>
                    </div>


                    <div style="overflow-y: auto; flex-grow: 1;" :class="{ 'fade-in': sidebarHoverExpanded }"
                        v-if="!sidebarCollapsed">
                        <v-card v-if="conversations.length > 0" flat style="background-color: transparent;">
                            <v-list density="compact" nav class="conversation-list"
                                style="background-color: transparent;" v-model:selected="selectedConversations"
                                @update:selected="getConversationMessages">
                                <v-list-item v-for="(item, i) in conversations" :key="item.cid" :value="item.cid"
                                    rounded="lg" class="conversation-item" active-color="secondary">
                                    <v-list-item-title v-if="!sidebarCollapsed" class="conversation-title">{{ item.title
                                        || tm('conversation.newConversation') }}</v-list-item-title>
                                    <v-list-item-subtitle v-if="!sidebarCollapsed" class="timestamp">{{
                                        formatDate(item.updated_at)
                                    }}</v-list-item-subtitle>

                                    <template v-if="!sidebarCollapsed" v-slot:append>
                                        <div class="conversation-actions">
                                            <v-btn icon="mdi-pencil" size="x-small" variant="text"
                                                class="edit-title-btn"
                                                @click.stop="showEditTitleDialog(item.cid, item.title)" />
                                            <v-btn icon="mdi-delete" size="x-small" variant="text"
                                                class="delete-conversation-btn" color="error"
                                                @click.stop="deleteConversation(item.cid)" />
                                        </div>
                                    </template>
                                </v-list-item>
                            </v-list>
                        </v-card>

                        <v-fade-transition>
                            <div class="no-conversations" v-if="conversations.length === 0">
                                <v-icon icon="mdi-message-text-outline" size="large" color="grey-lighten-1"></v-icon>
                                <div class="no-conversations-text" v-if="!sidebarCollapsed || sidebarHoverExpanded">
                                    {{ tm('conversation.noHistory') }}</div>
                            </div>
                        </v-fade-transition>
                    </div>

                </div>

                <!-- 右侧聊天内容区域 -->
                <div class="chat-content-panel">

                    <div class="conversation-header fade-in">
                        <div v-if="currCid && getCurrentConversation">
                            <h3
                                style="max-width: 300px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
                                {{ getCurrentConversation.title || tm('conversation.newConversation') }}</h3>
                            <span style="font-size: 12px;">{{ formatDate(getCurrentConversation.updated_at) }}</span>
                        </div>
                        <div class="conversation-header-actions">
                            <!-- router 推送到 /chatbox -->
                            <v-tooltip :text="tm('actions.fullscreen')" v-if="!chatboxMode">
                                <template v-slot:activator="{ props }">
                                    <v-icon v-bind="props"
                                        @click="router.push(currCid ? `/chatbox/${currCid}` : '/chatbox')"
                                        class="fullscreen-icon">mdi-fullscreen</v-icon>
                                </template>
                            </v-tooltip>
                            <!-- 语言切换按钮 -->
                            <v-tooltip :text="t('core.common.language')" v-if="chatboxMode">
                                <template v-slot:activator="{ props }">
                                    <LanguageSwitcher variant="chatbox" />
                                </template>
                            </v-tooltip>
                            <!-- 主题切换按钮 -->
                            <v-tooltip :text="isDark ? tm('modes.lightMode') : tm('modes.darkMode')" v-if="chatboxMode">
                                <template v-slot:activator="{ props }">
                                    <v-btn v-bind="props" icon @click="toggleTheme" class="theme-toggle-icon"
                                        size="small" rounded="sm" style="margin-right: 8px;" variant="text">
                                        <v-icon>{{ isDark ? 'mdi-weather-night' : 'mdi-white-balance-sunny' }}</v-icon>
                                    </v-btn>
                                </template>
                            </v-tooltip>
                            <!-- router 推送到 /chat -->
                            <v-tooltip :text="tm('actions.exitFullscreen')" v-if="chatboxMode">
                                <template v-slot:activator="{ props }">
                                    <v-icon v-bind="props" @click="router.push(currCid ? `/chat/${currCid}` : '/chat')"
                                        class="fullscreen-icon">mdi-fullscreen-exit</v-icon>
                                </template>
                            </v-tooltip>
                        </div>
                    </div>
                    <v-divider v-if="currCid && getCurrentConversation" class="conversation-divider"></v-divider>

                    <MessageList v-if="messages && messages.length > 0" :messages="messages" :isDark="isDark"
                        :isStreaming="isStreaming || isConvRunning" @openImagePreview="openImagePreview"
                        ref="messageList" />
                    <div class="welcome-container fade-in" v-else>
                        <div class="welcome-title">
                            <span>Hello, I'm</span>
                            <span class="bot-name">AstrBot ⭐</span>
                        </div>
                        <div class="welcome-hint markdown-content">
                            <span>{{ t('core.common.type') }}</span>
                            <code>help</code>
                            <span>{{ tm('shortcuts.help') }} 😊</span>
                        </div>
                        <div class="welcome-hint markdown-content">
                            <span>{{ t('core.common.longPress') }}</span>
                            <code>Ctrl + B</code>
                            <span>{{ tm('shortcuts.voiceRecord') }} 🎤</span>
                        </div>
                        <div class="welcome-hint markdown-content">
                            <span>{{ t('core.common.press') }}</span>
                            <code>Ctrl + V</code>
                            <span>{{ tm('shortcuts.pasteImage') }} 🏞️</span>
                        </div>
                    </div>

                    <!-- 输入区域 -->
                    <div class="input-area fade-in">
                        <div
                            style="width: 85%; max-width: 900px; margin: 0 auto; border: 1px solid #e0e0e0; border-radius: 24px;">
                            <textarea id="input-field" v-model="prompt" @keydown="handleInputKeyDown"
                                :disabled="isStreaming || isConvRunning" @click:clear="clearMessage"
                                placeholder="Ask AstrBot..."
                                style="width: 100%; resize: none; outline: none; border: 1px solid var(--v-theme-border); border-radius: 12px; padding: 8px 16px; min-height: 40px; font-family: inherit; font-size: 16px; background-color: var(--v-theme-surface);"></textarea>
                            <div
                                style="display: flex; justify-content: space-between; align-items: center; padding: 0px 8px;">
                                <div style="display: flex; justify-content: flex-start; margin-top: 4px;">
                                    <!-- 选择提供商和模型 -->
                                    <ProviderModelSelector ref="providerModelSelector" />
                                </div>
                                <div
                                    style="display: flex; justify-content: flex-end; margin-top: 8px; align-items: center;">
                                    <input type="file" ref="imageInput" @change="handleFileSelect" accept="image/*"
                                        style="display: none" multiple />
                                    <v-progress-circular v-if="isStreaming || isConvRunning" indeterminate size="16"
                                        class="mr-1" width="1.5" />
                                    <v-btn @click="triggerImageInput" icon="mdi-plus" variant="text" color="deep-purple"
                                        class="add-btn" size="small" />
                                    <v-btn @click="isRecording ? stopRecording() : startRecording()"
                                        :icon="isRecording ? 'mdi-stop-circle' : 'mdi-microphone'" variant="text"
                                        :color="isRecording ? 'error' : 'deep-purple'" class="record-btn"
                                        size="small" />
                                    <v-btn @click="sendMessage" icon="mdi-send" variant="text" color="deep-purple"
                                        :disabled="!prompt && stagedImagesName.length === 0 && !stagedAudioUrl"
                                        class="send-btn" size="small" />
                                </div>
                            </div>

                        </div>

                        <!-- 附件预览区 -->
                        <div class="attachments-preview" v-if="stagedImagesUrl.length > 0 || stagedAudioUrl">
                            <div v-for="(img, index) in stagedImagesUrl" :key="index" class="image-preview">
                                <img :src="img" class="preview-image" />
                                <v-btn @click="removeImage(index)" class="remove-attachment-btn" icon="mdi-close"
                                    size="small" color="error" variant="text" />
                            </div>

                            <div v-if="stagedAudioUrl" class="audio-preview">
                                <v-chip color="deep-purple-lighten-4" class="audio-chip">
                                    <v-icon start icon="mdi-microphone" size="small"></v-icon>
                                    {{ tm('voice.recording') }}
                                </v-chip>
                                <v-btn @click="removeAudio" class="remove-attachment-btn" icon="mdi-close" size="small"
                                    color="error" variant="text" />
                            </div>
                        </div>
                    </div>
                </div>

            </div>
        </v-card-text>
    </v-card>
    <!-- 编辑对话标题对话框 -->
    <v-dialog v-model="editTitleDialog" max-width="400">
        <v-card>
            <v-card-title class="dialog-title">{{ tm('actions.editTitle') }}</v-card-title>
            <v-card-text>
                <v-text-field v-model="editingTitle" :label="tm('conversation.newConversation')" variant="outlined"
                    hide-details class="mt-2" @keyup.enter="saveTitle" autofocus />
            </v-card-text>
            <v-card-actions>
                <v-spacer></v-spacer>
                <v-btn text @click="editTitleDialog = false" color="grey-darken-1">{{ t('core.common.cancel') }}</v-btn>
                <v-btn text @click="saveTitle" color="primary">{{ t('core.common.save') }}</v-btn>
            </v-card-actions>
        </v-card>
    </v-dialog>

    <!-- 图片预览对话框 -->
    <v-dialog v-model="imagePreviewDialog" max-width="90vw" max-height="90vh">
        <v-card class="image-preview-card" elevation="8">
            <v-card-title class="d-flex justify-space-between align-center pa-4">
                <span>{{ t('core.common.imagePreview') }}</span>
                <v-btn icon="mdi-close" variant="text" @click="imagePreviewDialog = false" />
            </v-card-title>
            <v-card-text class="text-center pa-4">
                <img :src="previewImageUrl" class="preview-image-large" />
            </v-card-text>
        </v-card>
    </v-dialog>
</template>

<script>
import { router } from '@/router';
import axios from 'axios';
import { ref } from 'vue';
import { useCustomizerStore } from '@/stores/customizer';
import { useI18n, useModuleI18n } from '@/i18n/composables';
import LanguageSwitcher from '@/components/shared/LanguageSwitcher.vue';
import ProviderModelSelector from '@/components/chat/ProviderModelSelector.vue';
import MessageList from '@/components/chat/MessageList.vue';
import 'highlight.js/styles/github.css';
import { useToast } from '@/utils/toast';

export default {
    name: 'ChatPage',
    components: {
        LanguageSwitcher,
        ProviderModelSelector,
        MessageList
    },
    props: {
        chatboxMode: {
            type: Boolean,
            default: false
        }
    }, setup() {
        const { t } = useI18n();
        const { tm } = useModuleI18n('features/chat');

        return {
            t,
            tm,
            router,
            ref
        };
    },
    data() {
        return {
            prompt: '',
            messages: [],
            conversations: [],
            selectedConversations: [], // 用于控制左侧列表的选中状态
            currCid: '',
            stagedImagesName: [], // 用于存储图片文件名的数组
            stagedImagesUrl: [], // 用于存储图片的blob URL数组
            loadingChat: false,

            inputFieldLabel: '',

            isRecording: false,
            audioChunks: [],
            stagedAudioUrl: "",
            mediaRecorder: null,

            // Ctrl键长按相关变量
            ctrlKeyDown: false,
            ctrlKeyTimer: null,
            ctrlKeyLongPressThreshold: 300, // 长按阈值，单位毫秒

            mediaCache: {}, // Add a cache to store media blobs

            // 添加对话标题编辑相关变量
            editTitleDialog: false,
            editingTitle: '',
            editingCid: '',

            // 侧边栏折叠状态
            sidebarCollapsed: true,
            sidebarHovered: false,
            sidebarHoverTimer: null,
            sidebarHoverExpanded: false,
            sidebarHoverDelay: 100, // 悬停延迟，单位毫秒            
            pendingCid: null, // Store pending conversation ID for route handling

            // 图片预览相关变量
            imagePreviewDialog: false,
            previewImageUrl: '',

            isStreaming: false,
            isConvRunning: false, // Track if the current conversation is running

            isToastedRunningInfo: false, // To avoid multiple toasts
        }
    },

    computed: {
        isDark() {
            return useCustomizerStore().uiTheme === 'PurpleThemeDark';
        },
        // Get the current conversation from the conversations array
        getCurrentConversation() {
            if (!this.currCid) return null;
            return this.conversations.find(c => c.cid === this.currCid);
        }
    },

    watch: {
        // Watch for route changes to handle direct navigation to /chat/<cid>
        '$route': {
            immediate: true,
            handler(to, from) {
                console.log('Route changed:', to.path, 'from:', from?.path);
                if (from &&
                    ((from.path.startsWith('/chat') && to.path.startsWith('/chatbox')) ||
                        (from.path.startsWith('/chatbox') && to.path.startsWith('/chat')))) {
                }

                // Check if the route matches /chat/<cid> or /chatbox/<cid> pattern
                if (to.path.startsWith('/chat/') || to.path.startsWith('/chatbox/')) {
                    const pathCid = to.path.split('/')[2];
                    console.log('Path CID:', pathCid);
                    if (pathCid && pathCid !== this.currCid) {
                        // If conversations are already loaded
                        if (this.conversations.length > 0) {
                            const conversation = this.conversations.find(c => c.cid === pathCid);
                            if (conversation) {
                                this.getConversationMessages([pathCid]);
                            }
                        } else {
                            // Store the cid to be used after conversations are loaded
                            this.pendingCid = pathCid;
                        }
                    }
                }
            }
        },

        // Watch for conversations loaded to handle pending cid
        conversations: {
            handler(newConversations) {
                if (this.pendingCid && newConversations.length > 0) {
                    const conversation = newConversations.find(c => c.cid === this.pendingCid);
                    if (conversation) {
                        // 先设置选中状态，然后加载对话消息
                        this.selectedConversations = [this.pendingCid];
                        this.getConversationMessages([this.pendingCid]);
                        this.pendingCid = null;
                    }
                } else {
                    // 如果没有URL参数指定的对话，且当前没有选中对话，则默认打开第一个对话
                    if (!this.currCid && newConversations.length > 0) {
                        const firstConversation = newConversations[0];
                        this.selectedConversations = [firstConversation.cid];
                        this.getConversationMessages([firstConversation.cid]);
                    }
                }
            }
        }
    },

    mounted() {
        // Theme is now handled globally by the customizer store.
        // 从 localStorage 读取侧边栏折叠状态，默认为 true（折叠）
        const savedCollapsedState = localStorage.getItem('sidebarCollapsed');
        if (savedCollapsedState !== null) {
            this.sidebarCollapsed = JSON.parse(savedCollapsedState);
        } else {
            this.sidebarCollapsed = true; // 默认折叠状态
        }

        // 设置输入框标签
        this.inputFieldLabel = this.tm('input.chatPrompt');
        this.getConversations();
        let inputField = document.getElementById('input-field');
        inputField.addEventListener('paste', this.handlePaste);
        inputField.addEventListener('keydown', function (e) {
            if (e.keyCode == 13 && !e.shiftKey) {
                e.preventDefault();
                // 检查是否有内容可发送
                if (this.canSendMessage()) {
                    this.sendMessage();
                }
            }
        }.bind(this));

        // 添加keyup事件监听
        document.addEventListener('keyup', this.handleInputKeyUp);
    },

    beforeUnmount() {
        // 移除keyup事件监听
        document.removeEventListener('keyup', this.handleInputKeyUp);

        // 清除悬停定时器
        if (this.sidebarHoverTimer) {
            clearTimeout(this.sidebarHoverTimer);
        }

        // Cleanup blob URLs
        this.cleanupMediaCache();
    },
    methods: {
        toggleTheme() {
            const customizer = useCustomizerStore();
            const newTheme = customizer.uiTheme === 'PurpleTheme' ? 'PurpleThemeDark' : 'PurpleTheme';
            customizer.SET_UI_THEME(newTheme);
        },
        // 切换侧边栏折叠状态
        toggleSidebar() {
            if (this.sidebarHoverExpanded) {
                this.sidebarHoverExpanded = false;
                return
            }
            this.sidebarCollapsed = !this.sidebarCollapsed;
            // 保存折叠状态到 localStorage
            localStorage.setItem('sidebarCollapsed', JSON.stringify(this.sidebarCollapsed));
        },

        // 侧边栏鼠标悬停处理
        handleSidebarMouseEnter() {
            if (!this.sidebarCollapsed) return;

            this.sidebarHovered = true;

            // 设置延迟定时器
            this.sidebarHoverTimer = setTimeout(() => {
                if (this.sidebarHovered) {
                    this.sidebarHoverExpanded = true;
                    this.sidebarCollapsed = false;
                }
            }, this.sidebarHoverDelay);
        },

        handleSidebarMouseLeave() {
            this.sidebarHovered = false;

            // 清除定时器
            if (this.sidebarHoverTimer) {
                clearTimeout(this.sidebarHoverTimer);
                this.sidebarHoverTimer = null;
            }

            if (this.sidebarHoverExpanded) {
                this.sidebarCollapsed = true;
            }
            this.sidebarHoverExpanded = false;
        },

        // 显示编辑对话标题对话框
        showEditTitleDialog(cid, title) {
            this.editingCid = cid;
            this.editingTitle = title || ''; // 如果标题为空，则设置为空字符串
            this.editTitleDialog = true;
        },

        // 保存对话标题
        saveTitle() {
            if (!this.editingCid) return;

            const trimmedTitle = this.editingTitle.trim();
            axios.post('/api/chat/rename_conversation', {
                conversation_id: this.editingCid,
                title: trimmedTitle
            })
                .then(response => {
                    // 更新本地对话列表中的标题
                    const conversation = this.conversations.find(c => c.cid === this.editingCid);
                    if (conversation) {
                        conversation.title = trimmedTitle;
                    }
                    this.editTitleDialog = false;
                })
                .catch(err => {
                    console.error('重命名对话失败:', err);
                });
        },

        async getMediaFile(filename) {
            if (this.mediaCache[filename]) {
                return this.mediaCache[filename];
            }

            try {
                const response = await axios.get('/api/chat/get_file', {
                    params: { filename },
                    responseType: 'blob'
                });

                const blobUrl = URL.createObjectURL(response.data);
                this.mediaCache[filename] = blobUrl;
                return blobUrl;
            } catch (error) {
                console.error('Error fetching media file:', error);
                return '';
            }
        },

        removeAudio() {
            this.stagedAudioUrl = null;
        },

        openImagePreview(imageUrl) {
            this.previewImageUrl = imageUrl;
            this.imagePreviewDialog = true;
        },

        async startRecording() {
            const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
            this.mediaRecorder = new MediaRecorder(stream);
            this.mediaRecorder.ondataavailable = (event) => {
                this.audioChunks.push(event.data);
            };
            this.mediaRecorder.start();
            this.isRecording = true;
            this.inputFieldLabel = this.tm('input.recordingPrompt');
        },

        async stopRecording() {
            this.isRecording = false;
            this.inputFieldLabel = this.tm('input.chatPrompt');
            this.mediaRecorder.stop();
            this.mediaRecorder.onstop = async () => {
                const audioBlob = new Blob(this.audioChunks, { type: 'audio/wav' });
                this.audioChunks = [];

                this.mediaRecorder.stream.getTracks().forEach(track => track.stop());

                const formData = new FormData();
                formData.append('file', audioBlob);

                try {
                    const response = await axios.post('/api/chat/post_file', formData, {
                        headers: {
                            'Content-Type': 'multipart/form-data'
                        }
                    });

                    const audio = response.data.data.filename;
                    console.log('Audio uploaded:', audio);

                    this.stagedAudioUrl = audio; // Store just the filename
                } catch (err) {
                    console.error('Error uploading audio:', err);
                }
            };
        },

        async processAndUploadImage(file) {
            const formData = new FormData();
            formData.append('file', file);

            try {
                const response = await axios.post('/api/chat/post_image', formData, {
                    headers: {
                        'Content-Type': 'multipart/form-data'
                    }
                });

                const img = response.data.data.filename;
                this.stagedImagesName.push(img); // Store just the filename
                this.stagedImagesUrl.push(URL.createObjectURL(file)); // Create a blob URL for immediate display

            } catch (err) {
                console.error('Error uploading image:', err);
            }
        },

        async handlePaste(event) {
            console.log('Pasting image...');
            const items = event.clipboardData.items;
            for (let i = 0; i < items.length; i++) {
                if (items[i].type.indexOf('image') !== -1) {
                    const file = items[i].getAsFile();
                    this.processAndUploadImage(file);
                }
            }
        },

        removeImage(index) {
            // Revoke the blob URL to prevent memory leaks
            const urlToRevoke = this.stagedImagesUrl[index];
            if (urlToRevoke && urlToRevoke.startsWith('blob:')) {
                URL.revokeObjectURL(urlToRevoke);
            }

            this.stagedImagesName.splice(index, 1);
            this.stagedImagesUrl.splice(index, 1);
        },

        clearMessage() {
            this.prompt = '';
        },

        triggerImageInput() {
            this.$refs.imageInput.click();
        },

        handleFileSelect(event) {
            const files = event.target.files;
            if (files) {
                for (const file of files) {
                    this.processAndUploadImage(file);
                }
            }
            // Reset the input value to allow selecting the same file again
            event.target.value = '';
        },
        getConversations() {
            axios.get('/api/chat/conversations').then(response => {
                this.conversations = response.data.data;

                // If there's a pending conversation ID from the route
                if (this.pendingCid) {
                    const conversation = this.conversations.find(c => c.cid === this.pendingCid);
                    if (conversation) {
                        this.getConversationMessages([this.pendingCid]);
                        this.pendingCid = null;
                    }
                } else {
                    // 如果没有URL参数指定的对话，且当前没有选中对话，则默认打开第一个对话
                    if (!this.currCid && this.conversations.length > 0) {
                        const firstConversation = this.conversations[0];
                        this.selectedConversations = [firstConversation.cid];
                        this.getConversationMessages([firstConversation.cid]);
                    }
                }
            }).catch(err => {
                if (err.response.status === 401) {
                    this.$router.push('/auth/login?redirect=/chatbox');
                }
                console.error(err);
            });
        },
        getConversationMessages(cid) {
            if (!cid[0])
                return;

            // Update the URL to reflect the selected conversation
            if (this.$route.path !== `/chat/${cid[0]}` && this.$route.path !== `/chatbox/${cid[0]}`) {
                if (this.$route.path.startsWith('/chatbox')) {
                    this.$router.push(`/chatbox/${cid[0]}`);
                } else {
                    this.$router.push(`/chat/${cid[0]}`);
                }
                return
            }

            axios.get('/api/chat/get_conversation?conversation_id=' + cid[0]).then(async response => {
                this.currCid = cid[0];
                // Update the selected conversation in the sidebar
                this.selectedConversations = [cid[0]];
                let history = response.data.data.history;
                this.isConvRunning = response.data.data.is_running || false;

                if (this.isConvRunning) {
                    if (!this.isToastedRunningInfo) {
                        useToast().info("该对话正在运行中。", { timeout: 5000 });
                        this.isToastedRunningInfo = true;
                    }

                    // 如果对话还在运行，3秒后重新获取消息
                    setTimeout(() => {
                        this.getConversationMessages([this.currCid]);
                    }, 3000);
                }

                // 滚动到底部
                this.$nextTick(() => {
                    this.$refs.messageList.scrollToBottom();
                });

                for (let i = 0; i < history.length; i++) {
                    let content = history[i].content;
                    if (content.message.startsWith('[IMAGE]')) {
                        let img = content.message.replace('[IMAGE]', '');
                        const imageUrl = await this.getMediaFile(img);
                        if (!content.embedded_images) {
                            content.embedded_images = [];
                        }
                        content.embedded_images.push(imageUrl);
                        content.message = ''; // 清空message，避免显示标记文本
                    }

                    if (content.message.startsWith('[RECORD]')) {
                        let audio = content.message.replace('[RECORD]', '');
                        const audioUrl = await this.getMediaFile(audio);
                        content.embedded_audio = audioUrl;
                        content.message = ''; // 清空message，避免显示标记文本
                    }

                    if (content.image_url && content.image_url.length > 0) {
                        for (let j = 0; j < content.image_url.length; j++) {
                            content.image_url[j] = await this.getMediaFile(content.image_url[j]);
                        }
                    }

                    if (content.audio_url) {
                        content.audio_url = await this.getMediaFile(content.audio_url);
                    }
                }
                this.messages = history;
            }).catch(err => {
                console.error(err);
            });
        },
        async newConversation() {
            return axios.get('/api/chat/new_conversation').then(response => {
                const cid = response.data.data.conversation_id;
                this.currCid = cid;
                // Update the URL to reflect the new conversation
                if (this.$route.path.startsWith('/chatbox')) {
                    this.$router.push(`/chatbox/${cid}`);
                } else {
                    this.$router.push(`/chat/${cid}`);
                }
                this.getConversations();
                return cid;
            }).catch(err => {
                console.error(err);
                throw err;
            });
        },

        newC() {
            this.currCid = '';
            this.selectedConversations = []; // 清除选中状态
            this.messages = [];
            if (this.$route.path.startsWith('/chatbox')) {
                this.$router.push('/chatbox');
            } else {
                this.$router.push('/chat');
            }
        },

        formatDate(timestamp) {
            const date = new Date(timestamp * 1000); // 假设时间戳是以秒为单位
            const options = {
                year: 'numeric',
                month: '2-digit',
                day: '2-digit',
                hour: '2-digit',
                minute: '2-digit',
                second: '2-digit',
                hour12: false
            };
            // 使用当前语言环境的locale
            const locale = this.t('core.common.locale') || 'zh-CN';
            return date.toLocaleString(locale, options).replace(/\//g, '-').replace(/, /g, ' ');
        },

        deleteConversation(cid) {
            axios.get('/api/chat/delete_conversation?conversation_id=' + cid).then(response => {
                this.getConversations();
                this.currCid = '';
                this.selectedConversations = []; // 清除选中状态
                this.messages = [];
            }).catch(err => {
                console.error(err);
            });
        },

        // 检查是否可以发送消息
        canSendMessage() {
            return (this.prompt && this.prompt.trim()) ||
                this.stagedImagesName.length > 0 ||
                this.stagedAudioUrl;
        },

        async sendMessage() {
            // 检查是否有内容可发送
            if (!this.canSendMessage()) {
                console.log('没有内容可发送');
                return;
            }

            if (this.currCid == '') {
                const cid = await this.newConversation();
                // URL is already updated in newConversation method
            }

            // 保存当前要发送的数据到临时变量
            const promptToSend = this.prompt.trim();
            const imageNamesToSend = [...this.stagedImagesName];
            const audioNameToSend = this.stagedAudioUrl;

            // 立即清空输入和附件预览
            this.prompt = '';
            this.stagedImagesName = [];
            this.stagedImagesUrl = [];
            this.stagedAudioUrl = "";

            // Create a message object with actual URLs for display
            const userMessage = {
                type: 'user',
                message: promptToSend,
                image_url: [],
                audio_url: null
            };

            // Convert image filenames to blob URLs for display
            if (imageNamesToSend.length > 0) {
                const imagePromises = imageNamesToSend.map(name => {
                    if (!name.startsWith('blob:')) {
                        return this.getMediaFile(name);
                    }
                    return Promise.resolve(name);
                });
                userMessage.image_url = await Promise.all(imagePromises);
            }

            // Convert audio filename to blob URL for display
            if (audioNameToSend) {
                if (!audioNameToSend.startsWith('blob:')) {
                    userMessage.audio_url = await this.getMediaFile(audioNameToSend);
                } else {
                    userMessage.audio_url = audioNameToSend;
                }
            }

            this.messages.push({
                "content": userMessage,
            });
            this.loadingChat = true

            // 从ProviderModelSelector组件获取当前选择
            const selection = this.$refs.providerModelSelector?.getCurrentSelection();
            const selectedProviderId = selection?.providerId || '';
            const selectedModelName = selection?.modelName || '';

            try {
                const response = await fetch('/api/chat/send', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': 'Bearer ' + localStorage.getItem('token')
                    },
                    body: JSON.stringify({
                        message: promptToSend,
                        conversation_id: this.currCid,
                        image_url: imageNamesToSend,
                        audio_url: audioNameToSend ? [audioNameToSend] : [],
                        selected_provider: selectedProviderId,
                        selected_model: selectedModelName
                    })
                });

                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }

                const reader = response.body.getReader();
                const decoder = new TextDecoder();
                let in_streaming = false;
                let message_obj = null;

                this.isStreaming = true

                while (true) {
                    try {
                        const { done, value } = await reader.read();
                        if (done) {
                            console.log('SSE stream completed');
                            break;
                        }

                        const chunk = decoder.decode(value, { stream: true });
                        const lines = chunk.split('\n\n');

                        for (let i = 0; i < lines.length; i++) {
                            let line = lines[i].trim();

                            if (!line) {
                                continue;
                            }

                            // Parse SSE data
                            let chunk_json;
                            try {
                                chunk_json = JSON.parse(line.replace('data: ', ''));
                            } catch (parseError) {
                                console.warn('JSON解析失败:', line, parseError);
                                continue;
                            }

                            // 检查解析后的数据是否有效
                            if (!chunk_json || typeof chunk_json !== 'object' || !chunk_json.hasOwnProperty('type')) {
                                console.warn('无效的数据对象:', chunk_json);
                                continue;
                            }

                            if (chunk_json.type === 'error') {
                                console.error('Error received:', chunk_json.data);
                                continue;
                            }

                            if (chunk_json.type === 'image') {
                                let img = chunk_json.data.replace('[IMAGE]', '');
                                const imageUrl = await this.getMediaFile(img);
                                let bot_resp = {
                                    type: 'bot',
                                    message: '',
                                    embedded_images: [imageUrl]
                                }
                                this.messages.push({
                                    "content": bot_resp
                                });
                            } else if (chunk_json.type === 'record') {
                                let audio = chunk_json.data.replace('[RECORD]', '');
                                const audioUrl = await this.getMediaFile(audio);
                                let bot_resp = {
                                    type: 'bot',
                                    message: '',
                                    embedded_audio: audioUrl
                                }
                                this.messages.push({
                                    "content": bot_resp
                                });
                            } else if (chunk_json.type === 'plain') {
                                if (!in_streaming) {
                                    message_obj = {
                                        type: 'bot',
                                        message: this.ref(chunk_json.data),
                                    }
                                    this.messages.push({
                                        "content": message_obj
                                    });
                                    in_streaming = true;
                                } else {
                                    message_obj.message.value += chunk_json.data;
                                }
                            } else if (chunk_json.type === 'update_title') {
                                // 更新对话标题
                                const conversation = this.conversations.find(c => c.cid === chunk_json.cid);
                                if (conversation) {
                                    conversation.title = chunk_json.data;
                                }
                            }
                            if ((chunk_json.type === 'break' && chunk_json.streaming) || !chunk_json.streaming) {
                                // break means a segment end
                                in_streaming = false;
                            }
                        }
                    } catch (readError) {
                        console.error('SSE读取错误:', readError);
                        break;
                    }
                }

                // Input and attachments are already cleared
                this.loadingChat = false;

                // get the latest conversations
                this.getConversations();

            } catch (err) {
                console.error('发送消息失败:', err);
                this.loadingChat = false;
            } finally {
                this.isStreaming = false;
            }
        },

        handleInputKeyDown(e) {
            if (e.ctrlKey && e.keyCode === 66) { // Ctrl+B组合键
                e.preventDefault(); // 防止默认行为

                // 防止重复触发
                if (this.ctrlKeyDown) return;

                this.ctrlKeyDown = true;

                // 设置定时器识别长按
                this.ctrlKeyTimer = setTimeout(() => {
                    if (this.ctrlKeyDown && !this.isRecording) {
                        this.startRecording();
                    }
                }, this.ctrlKeyLongPressThreshold);
            }
        },
        handleInputKeyUp(e) {
            if (e.keyCode === 66) { // B键释放
                this.ctrlKeyDown = false;

                // 清除定时器
                if (this.ctrlKeyTimer) {
                    clearTimeout(this.ctrlKeyTimer);
                    this.ctrlKeyTimer = null;
                }

                // 如果正在录音，停止录音
                if (this.isRecording) {
                    this.stopRecording();
                }
            }
        },

        cleanupMediaCache() {
            Object.values(this.mediaCache).forEach(url => {
                if (url.startsWith('blob:')) {
                    URL.revokeObjectURL(url);
                }
            });
            this.mediaCache = {};
        },
    },
}
</script>

<style>
/* 基础动画 */
@keyframes fadeIn {
    from {
        opacity: 0;
        transform: translateY(10px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

@keyframes pulse {
    0% {
        transform: scale(1);
    }

    50% {
        transform: scale(1.05);
    }

    100% {
        transform: scale(1);
    }
}

@keyframes slideIn {
    from {
        transform: translateX(20px);
        opacity: 0;
    }

    to {
        transform: translateX(0);
        opacity: 1;
    }
}

/* 添加淡入动画 */
@keyframes fadeInContent {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

/* 欢迎页样式 */
.welcome-container {
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
}

.welcome-title {
    font-size: 28px;
    margin-bottom: 16px;
}

.bot-name {
    font-weight: 700;
    margin-left: 8px;
    color: var(--v-theme-secondary);
}

.welcome-hint {
    margin-top: 8px;
    color: rgb(var(--v-theme-secondaryText));
    font-size: 14px;
}

.welcome-hint code {
    background-color: rgb(var(--v-theme-codeBg));
    padding: 2px 6px;
    margin: 0 4px;
    border-radius: 4px;
    font-family: 'Fira Code', monospace;
    font-size: 13px;
}

.fade-enter-active,
.fade-leave-active {
    transition: opacity 0.3s ease;
}

.fade-enter-from,
.fade-leave-to {
    opacity: 0;
}

.chat-page-card {
    width: 100%;
    height: 100%;
    max-height: 100%;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05) !important;
    overflow: hidden;
}

.chat-page-container {
    width: 100%;
    height: 100%;
    max-height: 100%;
    padding: 0;
    overflow: hidden;
}

.chat-layout {
    height: 100%;
    max-height: 100%;
    display: flex;
    overflow: hidden;
}

.sidebar-panel {
    max-width: 270px;
    min-width: 240px;
    display: flex;
    flex-direction: column;
    padding: 0;
    border-right: 1px solid rgba(0, 0, 0, 0.05);
    height: 100%;
    max-height: 100%;
    position: relative;
    transition: all 0.3s ease;
    overflow: hidden;
}

/* 侧边栏折叠状态 */
.sidebar-collapsed {
    max-width: 75px;
    min-width: 75px;
    transition: all 0.3s ease;
}

/* 当悬停展开时 */
.sidebar-collapsed.sidebar-hovered {
    max-width: 270px;
    min-width: 240px;
    transition: all 0.3s ease;
}

/* 侧边栏折叠按钮 */
.sidebar-collapse-btn-container {
    margin: 16px;
    margin-bottom: 0px;
    z-index: 10;
}

.sidebar-collapse-btn {
    opacity: 0.6;
    max-height: none;
    overflow-y: visible;
    padding: 0;
}

.conversation-item {
    margin-bottom: 4px;
    border-radius: 8px !important;
    transition: all 0.2s ease;
    height: auto !important;
    min-height: 56px;
    padding: 8px 16px !important;
    position: relative;
}

.conversation-item:hover {
    background-color: rgba(103, 58, 183, 0.05);
}

.conversation-item:hover .conversation-actions {
    opacity: 1;
    visibility: visible;
}

.conversation-actions {
    display: flex;
    gap: 4px;
    opacity: 0;
    visibility: hidden;
    transition: all 0.2s ease;
}

.edit-title-btn,
.delete-conversation-btn {
    opacity: 0.7;
    transition: opacity 0.2s ease;
}

.edit-title-btn:hover,
.delete-conversation-btn:hover {
    opacity: 1;
}

.conversation-title {
    font-weight: 500;
    font-size: 14px;
    line-height: 1.3;
    margin-bottom: 2px;
    transition: opacity 0.25s ease;
}

.timestamp {
    font-size: 11px;
    color: var(--v-theme-secondaryText);
    line-height: 1;
    transition: opacity 0.25s ease;
}

.sidebar-section-title {
    font-size: 12px;
    font-weight: 500;
    color: var(--v-theme-secondaryText);
    text-transform: uppercase;
    letter-spacing: 0.5px;
    margin-bottom: 12px;
    padding-left: 4px;
    transition: opacity 0.25s ease;
    white-space: nowrap;
}

.status-chips {
    display: flex;
    flex-wrap: nowrap;
    gap: 8px;
    margin-bottom: 8px;
    transition: opacity 0.25s ease;
}

.status-chips .v-chip {
    flex: 1 1 0;
    justify-content: center;
    opacity: 0.7;
}

.status-chip {
    font-size: 12px;
    height: 24px !important;
}

.no-conversations {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 150px;
    opacity: 0.6;
    gap: 12px;
}

.no-conversations-text {
    font-size: 14px;
    color: var(--v-theme-secondaryText);
    transition: opacity 0.25s ease;
}

.chat-content-panel {
    height: 100%;
    max-height: 100%;
    width: 100%;
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

/* 输入区域样式 */
.input-area {
    padding: 16px;
    background-color: var(--v-theme-surface);
    position: relative;
    border-top: 1px solid var(--v-theme-border);
    flex-shrink: 0;
    /* 防止输入区域被压缩 */
}

/* 附件预览区 */
.attachments-preview {
    display: flex;
    gap: 8px;
    margin-top: 8px;
    max-width: 900px;
    margin: 8px auto 0;
    flex-wrap: wrap;
}

.image-preview,
.audio-preview {
    position: relative;
    display: inline-flex;
}

.preview-image {
    width: 60px;
    height: 60px;
    object-fit: cover;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.audio-chip {
    height: 36px;
    border-radius: 18px;
}

.remove-attachment-btn {
    position: absolute;
    top: -8px;
    right: -8px;
    opacity: 0.8;
    transition: opacity 0.2s;
}

.remove-attachment-btn:hover {
    opacity: 1;
}

/* 动画类 */
.fade-in {
    animation: fadeIn 0.3s ease-in-out;
}

/* 对话框标题样式 */
.dialog-title {
    font-size: 18px;
    font-weight: 500;
    padding-bottom: 8px;
}

/* 对话标题和时间样式 */
.conversation-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 8px;
    padding-left: 16px;
    border-bottom: 1px solid var(--v-theme-border);
    width: 100%;
    padding-right: 32px;
    flex-shrink: 0;
}
</style>