<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>BAI 业务审计控制台</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
    <style>
        @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap');
        
        body { font-family: 'Inter', system-ui, -apple-system, sans-serif; -webkit-font-smoothing: antialiased; background-color: #0b0f1a; }
        .font-mono { font-family: 'JetBrains Mono', monospace; font-variant-numeric: tabular-nums; }

        ::-webkit-scrollbar { width: 4px; height: 4px; }
        ::-webkit-scrollbar-thumb { background: #334155; border-radius: 10px; }
        ::-webkit-scrollbar-track { background: transparent; }

        .prose-audit { font-size: 14px; line-height: 1.7; color: #64748b !important; transition: all 0.3s ease; }
        .prose-audit p { margin-bottom: 0.85rem; }
        .prose-audit strong { color: #94a3b8 !important; font-weight: 600; }
        .prose-audit table { display: block; width: 100%; overflow-x: auto; border-collapse: collapse; margin: 1rem 0; -webkit-overflow-scrolling: touch; }
        .prose-audit th, .prose-audit td { border: 1px solid rgba(100, 116, 139, 0.2); padding: 8px 12px; min-width: 80px; }
        .prose-audit hr { border: 0; border-top: 1px solid #64748b; opacity: 0.3; margin: 1.2rem 0; }
        .prose-audit pre { background: #020617; padding: 1rem; border-radius: 0.75rem; border: 1px solid #1e293b; color: #94a3b8; margin: 1rem 0; overflow-x: auto; }

        .chat-card-container:hover .prose-audit, .chat-card-container:hover .prose-audit p { color: #94a3b8 !important; }
        .chat-card-container:hover .prose-audit strong { color: #cbd5e1 !important; }
        .chat-card-container:hover .response-box { background-color: rgba(15, 23, 42, 0.6) !important; border-color: rgba(255, 255, 255, 0.1) !important; }

        /* 去除 details 默认的黑色三角形 */
        details > summary { list-style: none; }
        details > summary::-webkit-details-marker { display: none; }

        [x-cloak] { display: none !important; }
    </style>
</head>
<body class="text-slate-400 h-screen flex flex-col overflow-hidden" x-data="aiManage()">

    <header class="h-14 bg-slate-950/50 backdrop-blur-md border-b border-white/5 flex items-center px-4 lg:px-6 justify-between shrink-0 z-20">
        <div class="flex items-center space-x-3 lg:space-x-8">
            <button x-show="activePhone && isMobile" @click="backToList()" class="lg:hidden p-2 -ml-2 text-emerald-500 hover:bg-white/5 rounded-full">
                <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M15 19l-7-7 7-7" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg>
            </button>
            <div class="flex items-center space-x-2">
                <div class="w-1.5 h-5 bg-emerald-500 rounded-full"></div>
                <span class="text-white font-bold text-sm lg:text-base tracking-tight uppercase">Audit <span class="hidden lg:inline text-emerald-500 font-normal">v2.2</span></span>
            </div>
            
            <div class="flex bg-black/40 rounded-lg p-0.5 border border-white/5 scale-90 lg:scale-100 origin-left">
                <template x-for="opt in timeOptions">
                    <button @click="setTimeFilter(opt.days)" 
                            class="px-2 lg:px-4 py-1 text-[10px] lg:text-[11px] rounded-md transition-all font-medium"
                            :class="selectedDays === opt.days ? 'bg-emerald-600 text-white shadow-lg' : 'text-slate-500'" 
                            x-text="opt.label"></button>
                </template>
            </div>
        </div>
        <div class="flex items-center space-x-2 lg:space-x-4 text-[11px] font-medium">
            <a href="/pb/manage" class="text-slate-300 hover:text-white border border-white/10 px-3 py-1 rounded transition-all">返回首页</a>
            <div class="hidden lg:flex items-center space-x-4">
                <span class="text-emerald-500 font-mono" x-text="users.length"></span>
                <a href="/pb/bai-ai-sql-lab" target="_blank" class="text-slate-500 hover:text-emerald-400 border border-white/10 px-3 py-1 rounded transition-all">TERMINAL</a>
            </div>
        </div>
    </header>

    <div class="flex-1 flex overflow-hidden relative">
        <aside class="absolute lg:relative z-10 w-full lg:w-80 h-full bg-[#06080f] border-r border-white/5 flex flex-col shrink-0 transition-transform duration-300"
               :class="isMobile && activePhone && !showSidebar ? '-translate-x-full' : 'translate-x-0'">
            <div class="p-3 bg-black/20 flex space-x-2 border-b border-white/5">
                <input type="text" x-model="searchQuery" placeholder="Quick search..." 
                       class="flex-1 bg-slate-900/50 border border-white/5 rounded-lg px-3 py-2 text-xs text-slate-200 outline-none">
                <button @click="fetchUsers()" class="p-2 hover:bg-white/5 rounded-lg text-slate-500">
                    <svg class="w-4 h-4" :class="{'animate-spin text-emerald-500': loadingUsers}" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" stroke-width="2" stroke-linecap="round"></path></svg>
                </button>
            </div>
            
            <div class="flex-1 overflow-y-auto p-2">
                <template x-for="user in filteredUsers" :key="user.phone">
                    <div @click="selectUser(user)" 
                         class="p-4 rounded-xl mb-1 cursor-pointer transition-all border border-transparent"
                         :class="activePhone === user.phone ? 'bg-emerald-500/5 border-emerald-500/20' : 'hover:bg-white/[0.03]'">
                        <div class="flex justify-between items-start">
                            <div class="flex flex-col flex-1 truncate pr-2">
                                <span class="text-[15px] truncate" :class="user.name ? 'text-slate-100 font-semibold' : 'text-slate-600 font-normal'" x-text="user.name || '-'"></span>
                                <span class="text-slate-500 text-[11px] mt-1 font-mono tracking-wider" x-text="user.phone"></span>
                            </div>
                            <div class="flex flex-col items-end shrink-0">
                                <span class="text-[14px] font-bold font-mono" :class="activePhone === user.phone ? 'text-emerald-500' : 'text-slate-400'" x-text="user.chat_count"></span>
                                <span class="text-[10px] text-slate-600 mt-0.5 font-mono" x-text="formatDateShort(user.last_active)"></span>
                            </div>
                        </div>
                    </div>
                </template>
            </div>
        </aside>

        <main class="flex-1 flex flex-col bg-[#0b0f1a] overflow-hidden relative z-0"
              :class="isMobile && (!activePhone || showSidebar) ? 'hidden' : 'flex'">
            <template x-if="activePhone">
                <div class="flex-1 flex flex-col overflow-hidden">
                    <div class="px-4 lg:px-6 py-3 lg:py-4 bg-slate-950/30 border-b border-white/5 flex justify-between items-center shrink-0">
                        <div class="flex flex-col lg:flex-row lg:items-baseline lg:space-x-3">
                            <h2 class="text-lg lg:text-xl tracking-tight truncate max-w-[150px] lg:max-w-none" :class="activeName !== '-' ? 'text-white font-bold' : 'text-slate-600'" x-text="activeName"></h2>
                            <span class="text-slate-500 text-[10px] font-mono lg:bg-slate-900 lg:px-3 lg:py-0.5 lg:rounded-full" x-text="activePhone"></span>
                        </div>
                        <div class="flex items-center space-x-2">
                            <button @click="fetchChats()" class="p-2 hover:bg-white/5 rounded-xl">
                                <svg class="w-5 h-5" :class="{'animate-spin text-emerald-500': loadingChats}" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" stroke-width="2" stroke-linecap="round"></path></svg>
                            </button>
                            <button @click="downloadCSV()" class="p-2 lg:px-4 lg:py-1.5 lg:bg-emerald-600 text-emerald-500 lg:text-white rounded-xl lg:text-xs font-bold transition-all">
                                <span class="hidden lg:inline">EXPORT</span>
                                <svg class="w-5 h-5 lg:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg>
                            </button>
                        </div>
                    </div>

                    <div class="flex-1 overflow-y-auto p-4 lg:p-6 space-y-6 lg:space-y-8 bg-[#0b0f1a]">
                        <template x-for="chat in chats" :key="chat.id">
                            <div class="flex flex-col chat-card-container transition-all duration-300">
                                <div class="flex justify-between items-center mb-3 px-1">
                                    <div class="flex items-center space-x-3">
                                        <span class="text-slate-600 font-mono text-[10px]">#<span x-text="chat.id"></span></span>
                                        <span class="text-blue-400 font-bold text-[12px]" x-text="chat.workspace_name"></span>
                                    </div>
                                    <div class="flex items-center space-x-2 text-right">
                                        <span class="text-emerald-700 font-bold text-[10px] font-mono" x-text="formatTime(chat.createdAt)"></span>
                                        <button @click="copySingleChat(chat)" class="p-1.5 text-slate-700 hover:text-emerald-500">
                                            <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z" stroke-width="2"></path></svg>
                                        </button>
                                    </div>
                                </div>
                                
                                <div class="space-y-4">
                                    <div class="flex flex-col items-end">
                                        <div class="max-w-[90%] lg:max-w-[80%] bg-[#1a233b] border border-blue-500/10 text-slate-200 px-4 lg:px-5 py-3 rounded-2xl rounded-tr-none text-sm leading-relaxed" x-text="chat.prompt"></div>
                                    </div>
                                    
                                    <div class="flex flex-col items-start" x-data="{ parsedData: parseResponse(chat.response) }">
                                        <div class="max-w-full lg:max-w-[95%] w-full bg-slate-950/40 border border-white/5 px-4 lg:px-6 py-4 rounded-3xl rounded-tl-none response-box transition-all">
                                            
                                            <template x-if="parsedData.think">
                                                <details class="mb-4 group">
                                                    <summary class="cursor-pointer text-xs font-mono text-slate-500 hover:text-emerald-500 transition-colors flex items-center select-none outline-none">
                                                        <svg class="w-4 h-4 mr-1 transform transition-transform group-open:rotate-90" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M9 5l7 7-7 7" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg>
                                                        AI 思考逻辑 (Think Process)
                                                    </summary>
                                                    <div class="mt-3 pl-4 border-l-2 border-slate-800 text-slate-400 prose-audit opacity-80" x-html="parsedData.think"></div>
                                                </details>
                                            </template>

                                            <div class="prose-audit max-w-none w-full" x-html="parsedData.text"></div>
                                            
                                            <template x-if="parsedData.sources && parsedData.sources.length > 0">
                                                <details class="mt-4 group border-t border-white/5 pt-3">
                                                    <summary class="cursor-pointer text-xs font-mono text-slate-500 hover:text-blue-400 transition-colors flex items-center select-none outline-none">
                                                        <svg class="w-4 h-4 mr-1 transform transition-transform group-open:rotate-90" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M9 5l7 7-7 7" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg>
                                                        <svg class="w-3.5 h-3.5 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 002-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"></path></svg>
                                                        知识库召回溯源 (<span x-text="parsedData.sources.length"></span>)
                                                    </summary>
                                                    
                                                    <div class="mt-3 grid grid-cols-2 lg:grid-cols-3 gap-2.5">
                                                        <template x-for="(src, index) in parsedData.sources" :key="index">
                                                            <div @click="openSourceModal(chat, src)" 
                                                                 class="bg-[#0f172a]/80 border border-white/5 hover:border-blue-500/40 hover:bg-blue-500/10 rounded-lg p-2.5 cursor-pointer transition-all flex flex-col justify-center h-16 shadow-sm group/card">
                                                                <div class="flex items-center space-x-1.5 w-full mb-1">
                                                                    <svg class="w-3.5 h-3.5 text-blue-400/70 shrink-0 group-hover/card:text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg>
                                                                    <span class="text-[11px] font-medium text-slate-300 truncate" x-text="src.title || 'Unknown Source'" :title="src.title"></span>
                                                                </div>
                                                                <div class="text-[9px] font-mono text-emerald-500/70 pl-5">
                                                                    Match: <span x-text="src.score ? src.score.toFixed(4) : 'N/A'"></span>
                                                                </div>
                                                            </div>
                                                        </template>
                                                    </div>
                                                </details>
                                            </template>

                                            <template x-if="parsedData.metrics && parsedData.metrics.model">
                                                <div class="mt-5 pt-3 border-t border-white/5 flex flex-wrap gap-2 lg:gap-3 text-[9px] lg:text-[10px] font-mono text-slate-500 uppercase tracking-wider">
                                                    <div class="flex items-center space-x-1.5 bg-black/40 px-2.5 py-1 rounded-md border border-white/5" title="Model Used">
                                                        <svg class="w-3 h-3 text-slate-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M19.428 15.428a2 2 0 00-1.022-.547l-2.387-.477a6 6 0 00-3.86.517l-.318.158a6 6 0 01-3.86.517L6.05 15.21a2 2 0 00-1.806.547M8 4h8l-1 1v5.172a2 2 0 00.586 1.414l5 5c1.26 1.26.367 3.414-1.415 3.414H4.828c-1.782 0-2.674-2.154-1.414-3.414l5-5A2 2 0 009 10.172V5L8 4z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg>
                                                        <span class="text-slate-400" x-text="parsedData.metrics.model"></span>
                                                    </div>
                                                    
                                                    <template x-if="parsedData.metrics.total_tokens">
                                                        <div class="flex items-center space-x-1.5 bg-black/40 px-2.5 py-1 rounded-md border border-white/5" title="Tokens (Prompt + Completion)">
                                                            <svg class="w-3 h-3 text-emerald-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"></path></svg>
                                                            <span class="text-slate-600">IN:</span> <span class="text-emerald-500/80 mr-1" x-text="parsedData.metrics.prompt_tokens"></span>
                                                            <span class="text-slate-600">OUT:</span> <span class="text-blue-400/80 mr-1" x-text="parsedData.metrics.completion_tokens"></span>
                                                            <span class="text-slate-600">TOT:</span> <span class="text-purple-400/80" x-text="parsedData.metrics.total_tokens"></span>
                                                        </div>
                                                    </template>

                                                    <template x-if="parsedData.metrics.duration">
                                                        <div class="flex items-center space-x-1.5 bg-black/40 px-2.5 py-1 rounded-md border border-white/5" title="Time taken">
                                                            <svg class="w-3 h-3 text-amber-600" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
                                                            <span class="text-amber-500/80" x-text="(parsedData.metrics.duration).toFixed(2) + 's'"></span>
                                                        </div>
                                                    </template>
                                                </div>
                                            </template>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </template>
                    </div>

                    <div class="p-3 lg:p-4 bg-slate-950 border-t border-white/5 flex justify-center items-center space-x-6 lg:space-x-12 shrink-0">
                        <button @click="changePage(navPage - 1)" :disabled="navPage <= 1" class="text-[10px] px-4 py-2 bg-slate-900 border border-white/5 rounded-xl disabled:opacity-20 font-bold uppercase">Prev</button>
                        <span class="text-emerald-500 font-bold font-mono text-base" x-text="navPage"></span>
                        <button @click="changePage(navPage + 1)" :disabled="chats.length < 50" class="text-[10px] px-4 py-2 bg-slate-900 border border-white/5 rounded-xl disabled:opacity-20 font-bold uppercase">Next</button>
                    </div>
                </div>
            </template>
            <div x-show="!activePhone" class="flex-1 items-center justify-center opacity-10 flex flex-col">
                <svg class="w-16 h-16 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg>
                <p class="text-xs uppercase tracking-widest">Select to begin</p>
            </div>
        </main>

        <div x-show="showSourceModal" style="display: none;" class="fixed inset-0 z-50 flex items-center justify-center p-4 sm:p-6" x-cloak>
            <div x-show="showSourceModal" x-transition.opacity class="absolute inset-0 bg-black/80 backdrop-blur-sm" @click="closeSourceModal()"></div>
            
            <div x-show="showSourceModal" 
                 x-transition:enter="transition ease-out duration-300"
                 x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
                 x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
                 x-transition:leave="transition ease-in duration-200"
                 x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
                 x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
                 class="relative bg-[#0b0f1a] border border-slate-700 rounded-2xl shadow-2xl w-full max-w-4xl max-h-[90vh] flex flex-col overflow-hidden">
                
                <div class="px-5 py-4 border-b border-slate-800 bg-slate-900/80 flex justify-between items-center shrink-0">
                    <div class="flex items-center space-x-3 truncate pr-4">
                        <div class="p-2 bg-blue-500/10 rounded-lg">
                            <svg class="w-5 h-5 text-blue-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"></path></svg>
                        </div>
                        <div class="flex flex-col truncate">
                            <h3 class="text-sm font-bold text-slate-200 truncate" x-text="activeSource?.title"></h3>
                            <span class="text-[10px] text-emerald-500 font-mono mt-0.5" x-text="'匹配度 (Score): ' + (activeSource?.score ? activeSource.score.toFixed(4) : 'N/A')"></span>
                        </div>
                    </div>
                    <button @click="closeSourceModal()" class="text-slate-500 hover:text-white transition-colors p-1 bg-white/5 hover:bg-white/10 rounded-lg">
                        <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
                    </button>
                </div>
                
                <div class="flex-1 overflow-y-auto p-5 lg:p-6 bg-[#06080f]">
                    <h4 class="text-xs font-mono text-slate-500 mb-3 uppercase tracking-wider flex items-center">
                        <svg class="w-3.5 h-3.5 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"></path></svg>
                        召回的文档切片内容
                    </h4>
                    <div class="text-sm text-slate-300 leading-relaxed font-mono bg-[#0b0f1a] p-4 lg:p-5 rounded-xl border border-white/5 whitespace-pre-wrap selection:bg-blue-500/30" x-text="activeSource?.text"></div>
                </div>
                
                <div class="px-5 py-4 border-t border-slate-800 bg-slate-900/80 flex justify-between items-center shrink-0">
                    <span class="text-xs text-slate-500 font-mono hidden sm:inline-block">
                        Workspace: <span class="text-slate-400 font-bold" x-text="activeChatForSource?.workspace_name"></span>
                    </span>
                    
                    <div class="flex space-x-3 w-full sm:w-auto">
                        <button @click="closeSourceModal()" class="flex-1 sm:flex-none px-4 py-2 bg-slate-800 text-slate-300 hover:bg-slate-700 rounded-lg transition-all font-medium text-sm">
                            关闭
                        </button>
                        <button @click="markIssueFromModal()" class="flex-1 sm:flex-none px-4 py-2 bg-rose-500/10 text-rose-400 hover:bg-rose-500 hover:text-white border border-rose-500/20 rounded-lg transition-all flex items-center justify-center shadow-sm font-medium text-sm">
                            <svg class="w-4 h-4 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 21v-4m0 0V5a2 2 0 012-2h6.5l1 1H21l-3 6 3 6h-8.5l-1-1H5a2 2 0 00-2 2zm9-13.5V9"></path></svg>
                            标记纠错工单
                        </button>
                    </div>
                </div>
            </div>
        </div>

    </div>

    <script>
        document.addEventListener('alpine:init', () => {
            Alpine.data('aiManage', () => ({
                users: [], chats: [], loadingUsers: false, loadingChats: false,
                searchQuery: '', activePhone: '', activeName: '',
                navPage: 1, selectedDays: 0, 
                showSidebar: true,
                isMobile: window.innerWidth < 1024,
                timeOptions: [{ label: '1D', days: 1 }, { label: '3D', days: 3 }, { label: '7D', days: 7 }, { label: 'ALL', days: 0 }],
                
                // ================= 新增：溯源弹窗相关状态 =================
                activeSource: null,
                activeChatForSource: null,
                showSourceModal: false,

                openSourceModal(chat, src) {
                    this.activeChatForSource = chat;
                    this.activeSource = src;
                    this.showSourceModal = true;
                },

                closeSourceModal() {
                    this.showSourceModal = false;
                    // 延迟清空数据，等待退出动画执行完毕
                    setTimeout(() => {
                        this.activeSource = null;
                        this.activeChatForSource = null;
                    }, 300);
                },

                markIssueFromModal() {
                    if (!this.activeSource || !this.activeChatForSource) return;
                    
                    const suggestion = prompt(`🚩 标记知识库错误\n\n来源文档：${this.activeSource.title}\n相似度得分：${this.activeSource.score ? this.activeSource.score.toFixed(4) : 'N/A'}\n\n请输入修改建议或错误说明：`);
                    
                    if (suggestion) {
                        // 【占位】后续替换为向 tbl_kb_tickets 写入记录的 API 请求
                        console.log("【生成工单】", {
                            chat_id: this.activeChatForSource.id,
                            workspace: this.activeChatForSource.workspace_name,
                            doc_title: this.activeSource.title,
                            original_text: this.activeSource.text,
                            suggestion: suggestion
                        });
                        
                        alert("✅ 纠错工单已生成！\n\n您可以在工单中心直接打开该 MD 文件进行编辑。");
                        this.closeSourceModal(); // 提交后自动关闭弹窗
                    }
                },
                // =======================================================
                
                get apiBase() { return (window.location.pathname.startsWith('/pb') ? '/pb' : '') + '/pb-api-v1'; },
                get filteredUsers() {
                    const q = this.searchQuery.toLowerCase();
                    return this.users.filter(u => u.phone.includes(q) || (u.name && u.name.toLowerCase().includes(q)));
                },
                init() { 
                    this.fetchUsers(); 
                    window.addEventListener('resize', () => {
                        this.isMobile = window.innerWidth < 1024;
                    });
                },
                setTimeFilter(d) { this.selectedDays = d; this.navPage = 1; this.fetchUsers(); if (this.activePhone) this.fetchChats(); },
                
                async fetchUsers() {
                    this.loadingUsers = true;
                    try {
                        const res = await fetch(`${this.apiBase}/audit/users?days=${this.selectedDays}`);
                        const json = await res.json();
                        if (json.code === 200) this.users = json.data;
                    } finally { this.loadingUsers = false; }
                },

                selectUser(user) {
                    this.activePhone = user.phone; 
                    this.activeName = user.name || '-';
                    this.navPage = 1;
                    this.showSidebar = false;
                    this.fetchChats();
                },

                backToList() {
                    this.showSidebar = true;
                },

                changePage(p) { this.navPage = p; this.fetchChats(); },

                async fetchChats() {
                    if (!this.activePhone) return;
                    this.loadingChats = true;
                    try {
                        const res = await fetch(`${this.apiBase}/audit/chat-list?phone=${this.activePhone}&page=${this.navPage}&days=${this.selectedDays}`);
                        const json = await res.json();
                        if (json.code === 200) this.chats = json.data;
                    } finally { this.loadingChats = false; }
                },

                parseResponse(raw) {
                    let result = { text: '', think: '', metrics: {}, sources: [] };
                    try {
                        const parsed = JSON.parse(raw);
                        let rawText = parsed.text || raw;
                        
                        result.metrics = parsed.metrics || {};
                        result.sources = parsed.sources || []; 
                
                        const thinkMatch = rawText.match(/<think>([\s\S]*?)<\/think>/i);
                        if (thinkMatch) {
                            result.think = marked.parse(thinkMatch[1].trim());
                            rawText = rawText.replace(/<think>[\s\S]*?<\/think>/i, '').trim();
                        }
                        result.text = marked.parse(rawText);
                    } catch (e) {
                        const thinkMatch = raw.match(/<think>([\s\S]*?)<\/think>/i);
                        let rawText = raw;
                        if (thinkMatch) {
                            result.think = marked.parse(thinkMatch[1].trim());
                            rawText = raw.replace(/<think>[\s\S]*?<\/think>/i, '').trim();
                        }
                        result.text = marked.parse(rawText);
                    }
                    return result;
                },

                formatTime(ms) {
                    const d = new Date(parseInt(ms));
                    const pad = (n, l=2) => String(n).padStart(l, '0');
                    return `${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`;
                },

                formatDateShort(ms) {
                    const d = new Date(parseInt(ms));
                    return `${d.getMonth()+1}/${d.getDate()} ${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`;
                },

                copySingleChat(chat) {
                    let answer = "";
                    try { 
                        const parsed = JSON.parse(chat.response).text;
                        answer = parsed.replace(/<think>[\s\S]*?<\/think>/gi, '').trim(); 
                    } catch(e) { 
                        answer = chat.response.replace(/<think>[\s\S]*?<\/think>/gi, '').trim();
                    }
                    const content = `[USER]: ${chat.prompt}\n\n[AI]: ${answer}`;
                    navigator.clipboard.writeText(content).then(() => alert("Entry copied."));
                },

                copyAsMarkdown() {
                    let md = "| ID | TIMESTAMP | WORKSPACE | PROMPT | RESPONSE |\n| --- | --- | --- | --- | --- |\n";
                    this.chats.forEach(c => {
                        let ans = ""; 
                        try { 
                            ans = JSON.parse(c.response).text.replace(/<think>[\s\S]*?<\/think>/gi, '').trim(); 
                        } catch(e) { 
                            ans = c.response.replace(/<think>[\s\S]*?<\/think>/gi, '').trim();
                        }
                        const cleanPrompt = c.prompt.replace(/\n/g, '<br>').replace(/\|/g, '\\|');
                        const cleanResponse = ans.replace(/\n/g, '<br>').replace(/\|/g, '\\|');
                        md += `| ${c.id} | ${this.formatTime(c.createdAt)} | ${c.workspace_name} | ${cleanPrompt} | ${cleanResponse} |\n`;
                    });
                    navigator.clipboard.writeText(md).then(() => alert("Copied."));
                },

                async downloadCSV() {
                    const res = await fetch(`${this.apiBase}/audit/export-all?phone=${this.activePhone}`);
                    const json = await res.json();
                    if (!json.data || json.data.length === 0) return alert("No data.");
                    const headers = Object.keys(json.data[0]);
                    let csv = "\uFEFF" + headers.join(",") + "\n";
                    json.data.forEach(row => {
                        const line = headers.map(h => {
                            let val = row[h];
                            if (val === null) return '""';
                            return '"' + String(val).replace(/"/g, '""') + '"';
                        });
                        csv += line.join(",") + "\n";
                    });
                    const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
                    const link = document.createElement("a");
                    link.href = URL.createObjectURL(blob);
                    link.download = `audit_${this.activePhone}.csv`;
                    link.click();
                }
            }));
        });
    </script>
</body>
</html>