
import React, { useState, useEffect, useRef } from 'react';
import { Plus, Search, Play, Copy, Trash2, Mic, MessageSquare, Save, Video, PhoneOff, Camera, ArrowLeftRight, Send, Phone, Rocket, AlertTriangle, PhoneCall, CameraOff, Image, Images, CloudSun, Calendar, TrendingUp, Coins, Wrench, Globe, Terminal, X, ClipboardCheck, Sparkles, Volume2, Timer, ChevronDown, Database, Server, Zap, ExternalLink, Key, BrainCircuit, Ear, Book, Filter } from 'lucide-react';
import { Button, Input, Badge, Drawer, Dialog } from '../components/UI';
import { ASRModel, Assistant, KnowledgeBase, LLMModel, TabValue, Tool, Voice } from '../types';
import { createAssistant, deleteAssistant, fetchASRModels, fetchAssistants, fetchKnowledgeBases, fetchLLMModels, fetchTools, fetchVoices, updateAssistant as updateAssistantApi } from '../services/backendApi';

const isSiliconflowVendor = (vendor?: string) => {
  const normalized = String(vendor || '').trim().toLowerCase();
  return normalized === 'siliconflow' || normalized === '硅基流动';
};

const SILICONFLOW_DEFAULT_MODEL = 'FunAudioLLM/CosyVoice2-0.5B';

const buildSiliconflowVoiceKey = (voiceId: string, model?: string) => {
  const id = String(voiceId || '').trim();
  if (!id) return '';
  if (id.includes(':')) return id;
  return `${model || SILICONFLOW_DEFAULT_MODEL}:${id}`;
};

const resolveRuntimeTtsVoice = (selectedVoiceId: string, voice: Voice) => {
  const explicitKey = String(voice.voiceKey || '').trim();
  if (!isSiliconflowVendor(voice.vendor)) {
    return explicitKey || selectedVoiceId;
  }
  if (voice.isSystem) {
    const canonical = buildSiliconflowVoiceKey(selectedVoiceId, voice.model);
    if (!explicitKey) return canonical;
    const explicitSuffix = explicitKey.includes(':') ? explicitKey.split(':').pop() : explicitKey;
    if (explicitSuffix && explicitSuffix !== selectedVoiceId) return canonical;
  }
  return explicitKey || buildSiliconflowVoiceKey(selectedVoiceId, voice.model);
};

const renderToolIcon = (icon: string) => {
  const className = 'w-4 h-4';
  const map: Record<string, React.ReactNode> = {
    Camera: <Camera className={className} />,
    CameraOff: <CameraOff className={className} />,
    Image: <Image className={className} />,
    Images: <Images className={className} />,
    CloudSun: <CloudSun className={className} />,
    Calendar: <Calendar className={className} />,
    TrendingUp: <TrendingUp className={className} />,
    Coins: <Coins className={className} />,
    Terminal: <Terminal className={className} />,
    Globe: <Globe className={className} />,
    Wrench: <Wrench className={className} />,
  };
  return map[icon] || <Wrench className={className} />;
};

export const AssistantsPage: React.FC = () => {
  const [assistants, setAssistants] = useState<Assistant[]>([]);
  const [voices, setVoices] = useState<Voice[]>([]);
  const [knowledgeBases, setKnowledgeBases] = useState<KnowledgeBase[]>([]);
  const [llmModels, setLlmModels] = useState<LLMModel[]>([]);
  const [asrModels, setAsrModels] = useState<ASRModel[]>([]);
  const [tools, setTools] = useState<Tool[]>([]);
  const [searchTerm, setSearchTerm] = useState('');
  const [selectedId, setSelectedId] = useState<string | null>(null);
  const [activeTab, setActiveTab] = useState<TabValue>(TabValue.GLOBAL);
  const [debugOpen, setDebugOpen] = useState(false);
  const [hotwordInput, setHotwordInput] = useState('');
  
  // Publish Modal State
  const [isPublishModalOpen, setIsPublishModalOpen] = useState(false);
  const [publishTab, setPublishTab] = useState<'web' | 'api'>('web');
  
  const [deleteId, setDeleteId] = useState<string | null>(null);
  const [copySuccess, setCopySuccess] = useState(false);
  const [saveLoading, setSaveLoading] = useState(false);
  const [isLoading, setIsLoading] = useState(true);

  const selectedAssistant = assistants.find(a => a.id === selectedId) || null;

  const filteredAssistants = assistants.filter(a => 
    a.name.toLowerCase().includes(searchTerm.toLowerCase())
  );

  useEffect(() => {
    const loadInitialData = async () => {
      setIsLoading(true);
      try {
        const [assistantList, voiceList, kbList, llmList, asrList, toolList] = await Promise.all([
          fetchAssistants(),
          fetchVoices(),
          fetchKnowledgeBases(),
          fetchLLMModels(),
          fetchASRModels(),
          fetchTools(),
        ]);
        setAssistants(assistantList);
        setVoices(voiceList);
        setKnowledgeBases(kbList);
        setLlmModels(llmList);
        setAsrModels(asrList);
        setTools(toolList);
        if (assistantList.length > 0) {
          setSelectedId(assistantList[0].id);
        }
      } catch (error) {
        console.error(error);
        alert('加载助手数据失败，请检查后端服务是否启动。');
      } finally {
        setIsLoading(false);
      }
    };

    loadInitialData();
  }, []);

  const handleCreate = async () => {
    const newAssistantPayload: Partial<Assistant> = {
      name: 'New Assistant',
      opener: '',
      prompt: '',
      knowledgeBaseId: '',
      language: 'zh',
      voice: voices[0]?.id || '',
      speed: 1,
      hotwords: [],
      tools: [],
      interruptionSensitivity: 500,
      configMode: 'platform',
    };
    try {
      const created = await createAssistant(newAssistantPayload);
      setAssistants((prev) => [created, ...prev]);
      setSelectedId(created.id);
      setActiveTab(TabValue.GLOBAL);
    } catch (error) {
      console.error(error);
      alert('创建助手失败。');
    }
  };

  const handleSave = async () => {
    if (!selectedAssistant) return;
    setSaveLoading(true);
    try {
      const updated = await updateAssistantApi(selectedAssistant.id, selectedAssistant);
      setAssistants((prev) => prev.map((item) => (item.id === updated.id ? { ...item, ...updated } : item)));
    } catch (error) {
      console.error(error);
      alert('保存失败，请稍后重试。');
    } finally {
      setSaveLoading(false);
    }
  };

  const handleCopyId = (id: string, text?: string) => {
    navigator.clipboard.writeText(text || id);
    setCopySuccess(true);
    setTimeout(() => setCopySuccess(false), 2000);
  };

  const handleCopy = async (e: React.MouseEvent, assistant: Assistant) => {
    e.stopPropagation();
    try {
      const copied = await createAssistant({
        ...assistant,
        name: `${assistant.name} (Copy)`,
      });
      setAssistants((prev) => [copied, ...prev]);
    } catch (error) {
      console.error(error);
      alert('复制助手失败。');
    }
  };

  const handleDeleteClick = (e: React.MouseEvent, id: string) => {
    e.stopPropagation();
    setDeleteId(id);
  };

  const confirmDelete = async () => {
    if (deleteId) {
        try {
          await deleteAssistant(deleteId);
          setAssistants(prev => prev.filter(a => a.id !== deleteId));
          if (selectedId === deleteId) setSelectedId(null);
          setDeleteId(null);
        } catch (error) {
          console.error(error);
          alert('删除失败，请稍后重试。');
        }
    }
  };

  const updateAssistant = (field: keyof Assistant, value: any) => {
    if (!selectedId) return;
    setAssistants(prev => prev.map(a => a.id === selectedId ? { ...a, [field]: value } : a));
    
    if (field === 'configMode') {
      if (value === 'platform') {
        setActiveTab(TabValue.GLOBAL);
      } else if (value === 'dify' || value === 'fastgpt') {
        setActiveTab(TabValue.LINK);
      }
    }
  };

  const toggleTool = (toolId: string) => {
    if (!selectedAssistant) return;
    const currentTools = selectedAssistant.tools || [];
    const newTools = currentTools.includes(toolId) 
        ? currentTools.filter(id => id !== toolId)
        : [...currentTools, toolId];
    updateAssistant('tools', newTools);
  };

  const removeImportedTool = (e: React.MouseEvent, tool: Tool) => {
    e.stopPropagation();
    if (!selectedAssistant) return;
    updateAssistant('tools', (selectedAssistant.tools || []).filter((id) => id !== tool.id));
  };

  const addHotword = () => {
    if (hotwordInput.trim() && selectedAssistant) {
      updateAssistant('hotwords', [...selectedAssistant.hotwords, hotwordInput.trim()]);
      setHotwordInput('');
    }
  };

  const removeHotword = (word: string) => {
    if (selectedAssistant) {
      updateAssistant('hotwords', selectedAssistant.hotwords.filter(w => w !== word));
    }
  };

  const systemTools = tools.filter((t) => t.enabled !== false && t.category === 'system');
  const queryTools = tools.filter((t) => t.enabled !== false && t.category === 'query');

  const isExternalConfig = selectedAssistant?.configMode === 'dify' || selectedAssistant?.configMode === 'fastgpt';
  const isNoneConfig = selectedAssistant?.configMode === 'none' || !selectedAssistant?.configMode;

  return (
    <div className="flex h-[calc(100vh-8rem)] gap-6 animate-in fade-in py-4">
      {/* LEFT COLUMN: List */}
      <div className="w-80 flex flex-col gap-4 shrink-0">
        <div className="flex items-center justify-between px-1 text-white">
            <h2 className="text-xl font-bold tracking-tight text-white">小助手列表</h2>
        </div>
        
        <div className="flex gap-2">
            <div className="relative flex-1">
                <Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
                <Input 
                  placeholder="搜索..." 
                  className="pl-9 bg-card/50 border-white/5"
                  value={searchTerm}
                  onChange={(e) => setSearchTerm(e.target.value)}
                />
            </div>
            <Button size="icon" onClick={handleCreate} title="新建小助手">
                <Plus className="h-5 w-5" />
            </Button>
        </div>

        <div className="flex-1 overflow-y-auto space-y-2 pr-1 custom-scrollbar">
            {!isLoading && filteredAssistants.map(assistant => (
                <div 
                    key={assistant.id}
                    onClick={() => setSelectedId(assistant.id)}
                    className={`group relative flex flex-col p-4 rounded-xl border transition-all cursor-pointer ${
                        selectedId === assistant.id 
                        ? 'bg-primary/10 border-primary/40 shadow-[0_0_15px_rgba(6,182,212,0.15)]' 
                        : 'bg-card/30 border-white/5 hover:bg-white/5 hover:border-white/10'
                    }`}
                >
                    <div className="flex flex-col gap-1.5 mb-2 pr-16 overflow-hidden">
                        <span className={`font-semibold truncate ${selectedId === assistant.id ? 'text-primary' : 'text-foreground'}`}>
                            {assistant.name}
                        </span>
                        {assistant.configMode && assistant.configMode !== 'none' && (
                          <div className="flex">
                            <Badge 
                              variant="outline" 
                              className={`text-[9px] uppercase tracking-tighter shrink-0 opacity-70 border-white/10 ${
                                assistant.configMode === 'platform' ? 'text-cyan-400 bg-cyan-400/5' : 
                                assistant.configMode === 'dify' ? 'text-blue-400 bg-blue-400/5' : 
                                'text-purple-400 bg-purple-400/5'
                              }`}
                            >
                              {assistant.configMode === 'platform' ? '内置' : assistant.configMode}
                            </Badge>
                          </div>
                        )}
                    </div>
                    
                    <div className="flex items-center text-xs text-muted-foreground">
                        <Phone className="h-3 w-3 mr-1.5 opacity-70" />
                        <span>{assistant.callCount} 次通话</span>
                    </div>

                    <div className="absolute right-2 top-2 flex space-x-1 opacity-0 group-hover:opacity-100 transition-opacity bg-background/50 backdrop-blur-sm rounded-lg p-0.5 shadow-lg border border-white/5">
                        <Button variant="ghost" size="icon" className="h-7 w-7" onClick={(e) => handleCopy(e, assistant)} title="复制">
                            <Copy className="h-3.5 w-3.5" />
                        </Button>
                        <Button variant="ghost" size="icon" className="h-7 w-7 text-destructive hover:text-destructive" onClick={(e) => handleDeleteClick(e, assistant.id)} title="删除">
                            <Trash2 className="h-3.5 w-3.5" />
                        </Button>
                    </div>
                </div>
            ))}
            {!isLoading && filteredAssistants.length === 0 && (
                <div className="text-center py-10 text-muted-foreground text-sm">
                    未找到小助手
                </div>
            )}
            {isLoading && (
                <div className="text-center py-10 text-muted-foreground text-sm">
                    加载中...
                </div>
            )}
        </div>
      </div>

      {/* RIGHT COLUMN: Config Panel */}
      <div className="flex-1 bg-card/20 backdrop-blur-sm border border-white/5 rounded-2xl overflow-hidden flex flex-col relative shadow-xl">
        {selectedAssistant ? (
            <>
                {/* Header Area */}
                <div className="p-6 border-b border-white/5 bg-white/[0.02] space-y-5">
                    <div className="flex items-start justify-between gap-4">
                        <div className="flex-1">
                            <div className="flex items-center gap-2 mb-2">
                                <label className="text-[10px] text-muted-foreground font-black tracking-widest uppercase ml-1">小助手名称</label>
                                <div className="flex items-center gap-1.5 px-2 py-0.5 rounded-md bg-white/5 border border-white/10 group/id transition-all hover:bg-white/10">
                                    <span className="text-[10px] font-mono text-muted-foreground/60 select-all tracking-tight">UUID: {selectedAssistant.id}</span>
                                    <button 
                                        onClick={() => handleCopyId(selectedAssistant.id)}
                                        className="text-muted-foreground hover:text-primary transition-colors flex items-center"
                                        title="复制ID"
                                    >
                                        {copySuccess ? <ClipboardCheck className="h-3 w-3 text-green-400" /> : <Copy className="h-3 w-3" />}
                                    </button>
                                </div>
                            </div>
                            <Input 
                                value={selectedAssistant.name}
                                onChange={(e) => updateAssistant('name', e.target.value)}
                                className="font-bold bg-white/5 border-white/10 focus:border-primary/50 text-base"
                            />
                        </div>
                        <div className="flex items-center space-x-2 pt-6">
                             <Button 
                                variant="secondary" 
                                onClick={() => setDebugOpen(true)} 
                                className="border border-primary/20 hover:border-primary/50 text-primary hover:text-primary hover:bg-primary/10 shadow-[0_0_15px_rgba(6,182,212,0.1)]"
                             >
                                <Play className="mr-2 h-4 w-4" /> 调试
                            </Button>
                            <Button 
                                variant="outline" 
                                onClick={handleSave} 
                                disabled={saveLoading}
                                className="border border-white/10 hover:border-primary/50 text-foreground"
                             >
                                <Save className={`mr-2 h-4 w-4 ${saveLoading ? 'animate-pulse' : ''}`} /> {saveLoading ? '正在保存...' : '保存'}
                            </Button>
                            <Button 
                                onClick={() => setIsPublishModalOpen(true)} 
                                className="shadow-[0_0_20px_rgba(6,182,212,0.3)]"
                            >
                                <Rocket className="mr-2 h-4 w-4" /> 发布
                            </Button>
                        </div>
                    </div>

                    <div className="space-y-2">
                        <label className="text-[10px] text-muted-foreground font-black tracking-widest uppercase ml-1">配置方式</label>
                        <div className="relative group w-full">
                          <select 
                            className="flex h-10 w-full rounded-md border border-white/10 bg-white/5 px-3 py-1 text-sm shadow-sm transition-all focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/50 text-foreground appearance-none cursor-pointer [&>option]:bg-card"
                            value={selectedAssistant.configMode || 'none'}
                            onChange={(e) => updateAssistant('configMode', e.target.value as any)}
                          >
                            <option value="none">无</option>
                            <option value="platform">平台配置</option>
                            <option value="dify">Dify 接入</option>
                            <option value="fastgpt">FastGPT 接入</option>
                          </select>
                          <ChevronDown className="absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none group-hover:text-primary transition-colors" />
                        </div>
                    </div>

                    {!isNoneConfig && (
                      <div className="flex bg-white/5 p-1 rounded-lg w-fit border border-white/5 animate-in fade-in slide-in-from-top-1">
                          {selectedAssistant.configMode === 'platform' ? (
                            <>
                              <button
                                  onClick={() => setActiveTab(TabValue.GLOBAL)}
                                  className={`px-6 py-1.5 text-sm font-medium rounded-md transition-all ${activeTab === TabValue.GLOBAL ? 'bg-primary text-primary-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground hover:bg-white/5'}`}
                              >
                                  全局配置
                              </button>
                              <button
                                  onClick={() => setActiveTab(TabValue.VOICE)}
                                  className={`px-6 py-1.5 text-sm font-medium rounded-md transition-all ${activeTab === TabValue.VOICE ? 'bg-primary text-primary-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground hover:bg-white/5'}`}
                              >
                                  语音配置
                              </button>
                              <button
                                  onClick={() => setActiveTab(TabValue.TOOLS)}
                                  className={`px-6 py-1.5 text-sm font-medium rounded-md transition-all ${activeTab === TabValue.TOOLS ? 'bg-primary text-primary-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground hover:bg-white/5'}`}
                              >
                                  工具配置
                              </button>
                              <button
                                  onClick={() => setActiveTab(TabValue.KNOWLEDGE)}
                                  className={`px-6 py-1.5 text-sm font-medium rounded-md transition-all ${activeTab === TabValue.KNOWLEDGE ? 'bg-primary text-primary-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground hover:bg-white/5'}`}
                              >
                                  知识库配置
                              </button>
                            </>
                          ) : (
                            <>
                              <button
                                  onClick={() => setActiveTab(TabValue.LINK)}
                                  className={`px-6 py-1.5 text-sm font-medium rounded-md transition-all ${activeTab === TabValue.LINK ? 'bg-primary text-primary-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground hover:bg-white/5'}`}
                              >
                                  链接设置
                              </button>
                              <button
                                  onClick={() => setActiveTab(TabValue.VOICE)}
                                  className={`px-6 py-1.5 text-sm font-medium rounded-md transition-all ${activeTab === TabValue.VOICE ? 'bg-primary text-primary-foreground shadow-sm' : 'text-muted-foreground hover:text-foreground hover:bg-white/5'}`}
                              >
                                  语音设置
                              </button>
                            </>
                          )}
                      </div>
                    )}
                </div>

                <div className="flex-1 overflow-y-auto p-8 custom-scrollbar">
                    {isNoneConfig ? (
                      <div className="h-full flex flex-col items-center justify-center text-muted-foreground opacity-40 animate-in fade-in">
                          <AlertTriangle className="w-12 h-12 mb-4" />
                          <p className="text-sm font-medium">请先选择配置方式以展开详细设置</p>
                      </div>
                    ) : (
                      <div className="max-w-4xl mx-auto space-y-8 animate-in slide-in-from-bottom-2 duration-300">
                        {activeTab === TabValue.LINK && isExternalConfig && (
                           <div className="space-y-6 animate-in fade-in">
                              <div className="p-4 rounded-xl bg-primary/5 border border-primary/20 flex items-start gap-3 mb-4">
                                <Database className="w-5 h-5 text-primary shrink-0 mt-0.5" />
                                <div>
                                  <h4 className="text-sm font-bold text-white mb-1">
                                    接入 {selectedAssistant.configMode === 'dify' ? 'Dify' : 'FastGPT'} 引擎
                                  </h4>
                                  <p className="text-xs text-muted-foreground leading-relaxed">
                                    配置后，视频通话过程中的对话逻辑、知识库检索以及工作流将由外部引擎托管。
                                  </p>
                                </div>
                              </div>

                              <div className="space-y-2">
                                  <label className="text-sm font-medium text-white flex items-center">
                                      <Globe className="w-4 h-4 mr-2 text-primary"/> 接口地址 (API URL)
                                  </label>
                                  <Input 
                                      value={selectedAssistant.apiUrl || ''}
                                      onChange={(e) => updateAssistant('apiUrl', e.target.value)}
                                      placeholder={selectedAssistant.configMode === 'dify' ? "https://api.dify.ai/v1" : "https://api.fastgpt.in/api/v1"}
                                      className="bg-white/5 border-white/10 focus:border-primary/50 font-mono text-xs"
                                  />
                              </div>

                              <div className="space-y-2">
                                  <label className="text-sm font-medium text-white flex items-center">
                                      <Terminal className="w-4 h-4 mr-2 text-primary"/> 密钥 (API KEY)
                                  </label>
                                  <Input 
                                      type="password"
                                      value={selectedAssistant.apiKey || ''}
                                      onChange={(e) => updateAssistant('apiKey', e.target.value)}
                                      placeholder="请输入应用 API 密钥..."
                                      className="bg-white/5 border-white/10 focus:border-primary/50 font-mono text-xs"
                                  />
                              </div>
                           </div>
                        )}

                        {activeTab === TabValue.GLOBAL && selectedAssistant.configMode === 'platform' && (
                            <div className="space-y-6">
                                <div className="space-y-2">
                                    <label className="text-sm font-medium text-white flex items-center">
                                        <BrainCircuit className="w-4 h-4 mr-2 text-primary"/> 大模型 (LLM Model)
                                    </label>
                                    <div className="relative group">
                                        <select
                                            className="flex h-10 w-full rounded-md border border-white/10 bg-white/5 px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/50 [&>option]:bg-card text-foreground appearance-none cursor-pointer"
                                            value={selectedAssistant.llmModelId || ''}
                                            onChange={(e) => updateAssistant('llmModelId', e.target.value)}
                                        >
                                            <option value="">使用系统默认模型</option>
                                            {llmModels.filter(m => m.type === 'text').map(model => (
                                                <option key={model.id} value={model.id}>{model.name} ({model.vendor})</option>
                                            ))}
                                        </select>
                                        <ChevronDown className="absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none group-hover:text-primary transition-colors" />
                                    </div>
                                    <p className="text-xs text-muted-foreground">选择用于驱动该助手对话的大语言模型。</p>
                                </div>

                                <div className="space-y-2">
                                    <label className="text-sm font-medium text-white flex items-center">
                                        <MessageSquare className="w-4 h-4 mr-2 text-primary"/> 开场白 (Opener)
                                    </label>
                                    <Input 
                                        value={selectedAssistant.opener}
                                        onChange={(e) => updateAssistant('opener', e.target.value)}
                                        placeholder="例如：您好，我是您的专属AI助手..."
                                        className="bg-white/5 border-white/10 focus:border-primary/50"
                                    />
                                    <p className="text-xs text-muted-foreground">接通通话后的第一句话。</p>
                                </div>

                                <div className="space-y-2">
                                    <label className="text-sm font-medium text-white flex items-center">
                                        <BotIcon className="w-4 h-4 mr-2 text-primary"/> 提示词 (Prompt)
                                    </label>
                                    <textarea 
                                        className="flex min-h-[200px] w-full rounded-xl border border-white/10 bg-white/5 px-4 py-3 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/50 resize-y text-white"
                                        value={selectedAssistant.prompt}
                                        onChange={(e) => updateAssistant('prompt', e.target.value)}
                                        placeholder="设定小助手的人设、语气、行为规范以及业务逻辑..."
                                    />
                                </div>
                            </div>
                        )}

                        {activeTab === TabValue.KNOWLEDGE && selectedAssistant.configMode === 'platform' && (
                            <div className="space-y-8 animate-in fade-in">
                                <div className="space-y-2">
                                    <label className="text-sm font-medium text-white flex items-center">
                                        <BrainCircuit className="w-4 h-4 mr-2 text-primary"/> 嵌入模型 (Embedding Model)
                                    </label>
                                    <div className="relative group">
                                        <select
                                            className="flex h-10 w-full rounded-md border border-white/10 bg-white/5 px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/50 [&>option]:bg-card text-foreground appearance-none cursor-pointer"
                                            value={selectedAssistant.embeddingModelId || ''}
                                            onChange={(e) => updateAssistant('embeddingModelId', e.target.value)}
                                        >
                                            <option value="">使用系统默认</option>
                                            {llmModels.filter(m => m.type === 'embedding').map(model => (
                                                <option key={model.id} value={model.id}>{model.name} ({model.vendor})</option>
                                            ))}
                                        </select>
                                        <ChevronDown className="absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none group-hover:text-primary transition-colors" />
                                    </div>
                                    <p className="text-xs text-muted-foreground">用于将文本转换为向量的嵌入模型。</p>
                                </div>

                                <div className="space-y-2">
                                    <label className="text-sm font-medium text-white flex items-center">
                                        <Filter className="w-4 h-4 mr-2 text-primary"/> 重排模型 (Rerank Model)
                                    </label>
                                    <div className="relative group">
                                        <select
                                            className="flex h-10 w-full rounded-md border border-white/10 bg-white/5 px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/50 [&>option]:bg-card text-foreground appearance-none cursor-pointer"
                                            value={selectedAssistant.rerankModelId || ''}
                                            onChange={(e) => updateAssistant('rerankModelId', e.target.value)}
                                        >
                                            <option value="">不使用重排</option>
                                            {llmModels.filter(m => m.type === 'rerank').map(model => (
                                                <option key={model.id} value={model.id}>{model.name} ({model.vendor})</option>
                                            ))}
                                        </select>
                                        <ChevronDown className="absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none group-hover:text-primary transition-colors" />
                                    </div>
                                    <p className="text-xs text-muted-foreground">可选配置。重排模型可优化检索结果的相关性排序。</p>
                                </div>

                                <div className="space-y-2">
                                    <label className="text-sm font-medium text-white flex items-center">
                                        <Book className="w-4 h-4 mr-2 text-primary"/> 知识库挂载 (Knowledge Base)
                                    </label>
                                    <div className="relative group">
                                        <select 
                                            className="flex h-10 w-full rounded-md border border-white/10 bg-white/5 px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/50 [&>option]:bg-card text-foreground appearance-none cursor-pointer"
                                            value={selectedAssistant.knowledgeBaseId}
                                            onChange={(e) => updateAssistant('knowledgeBaseId', e.target.value)}
                                        >
                                            <option value="">如果不选择，则使用通用大模型知识</option>
                                            {knowledgeBases.map(kb => (
                                                <option key={kb.id} value={kb.id}>{kb.name}</option>
                                            ))}
                                        </select>
                                        <ChevronDown className="absolute right-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none group-hover:text-primary transition-colors" />
                                    </div>
                                    <p className="text-xs text-muted-foreground">选择助手回答问题时参考的私有知识库。</p>
                                </div>
                            </div>
                        )}

                        {activeTab === TabValue.VOICE && !isNoneConfig && (
                            <div className="space-y-8">
                                <div className="space-y-2">
                                    <label className="text-sm font-medium text-white flex items-center">
                                        <Ear className="w-4 h-4 mr-2 text-primary"/> 语音识别 (ASR Model)
                                    </label>
                                    <div className="relative group">
                                        <select 
                                            className="flex h-12 w-full rounded-xl border border-white/10 bg-white/5 px-4 py-1 text-sm shadow-sm transition-all focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/50 [&>option]:bg-card text-foreground appearance-none cursor-pointer"
                                            value={selectedAssistant.asrModelId || ''}
                                            onChange={(e) => updateAssistant('asrModelId', e.target.value)}
                                        >
                                            <option value="">使用系统默认模型</option>
                                            {asrModels.map(model => (
                                                <option key={model.id} value={model.id}>
                                                    {model.name} ({model.vendor})
                                                </option>
                                            ))}
                                        </select>
                                        <ChevronDown className="absolute right-4 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none group-hover:text-primary transition-colors" />
                                    </div>
                                    <p className="text-xs text-muted-foreground flex items-center mt-1">
                                        选择用于识别用户语音输入的模型。
                                    </p>
                                </div>

                                <div className="space-y-2">
                                    <label className="text-sm font-medium text-white flex items-center">
                                        <Volume2 className="w-4 h-4 mr-2 text-primary"/> 选择音色 (From Voice Library)
                                    </label>
                                    <div className="relative group">
                                        <select 
                                            className="flex h-12 w-full rounded-xl border border-white/10 bg-white/5 px-4 py-1 text-sm shadow-sm transition-all focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/50 [&>option]:bg-card text-foreground appearance-none cursor-pointer"
                                            value={selectedAssistant.voice}
                                            onChange={(e) => updateAssistant('voice', e.target.value)}
                                        >
                                            <option value="" disabled>请选择声音库中的声音...</option>
                                            {voices.map(voice => (
                                                <option key={voice.id} value={voice.id}>
                                                    {voice.name} ({voice.vendor} - {voice.gender === 'Male' ? '男' : '女'})
                                                </option>
                                            ))}
                                        </select>
                                        <ChevronDown className="absolute right-4 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground pointer-events-none group-hover:text-primary transition-colors" />
                                    </div>
                                    <p className="text-xs text-muted-foreground flex items-center mt-1">
                                        <Sparkles className="w-3 h-3 mr-1 text-primary opacity-70" />
                                        音色配置同步自声音库。如需添加更多音色，请前往“声音库”模块。
                                    </p>
                                </div>

                                <div className="space-y-4 pt-2">
                                    <div className="flex justify-between items-center mb-1">
                                        <label className="text-sm font-medium text-white flex items-center">
                                            <Timer className="w-4 h-4 mr-2 text-primary"/> 打断灵敏度 (Interruption Sensitivity)
                                        </label>
                                        <div className="flex items-center gap-2">
                                            <div className="relative">
                                                <Input 
                                                    type="number"
                                                    value={selectedAssistant.interruptionSensitivity || 500}
                                                    onChange={(e) => updateAssistant('interruptionSensitivity', parseInt(e.target.value) || 0)}
                                                    className="w-20 h-8 text-right pr-7 text-xs font-mono bg-black/40 border-white/5"
                                                />
                                                <span className="absolute right-2 top-1/2 -translate-y-1/2 text-[10px] text-muted-foreground font-mono">ms</span>
                                            </div>
                                        </div>
                                    </div>
                                    <div className="flex items-center gap-6">
                                        <input 
                                            type="range" 
                                            min="0" 
                                            max="2000" 
                                            step="50" 
                                            value={selectedAssistant.interruptionSensitivity || 500}
                                            onChange={(e) => updateAssistant('interruptionSensitivity', parseInt(e.target.value))}
                                            className="flex-1 h-1.5 bg-secondary rounded-lg appearance-none cursor-pointer accent-primary"
                                        />
                                    </div>
                                    <div className="flex justify-between text-[10px] text-muted-foreground font-mono uppercase tracking-widest px-0.5 opacity-50">
                                        <span>0ms (Extreme)</span>
                                        <span>1000ms</span>
                                        <span>2000ms (Lazy)</span>
                                    </div>
                                    <p className="text-xs text-muted-foreground pt-1 italic opacity-60">
                                        * 定义用户说话多长时间后 AI 应当停止当前的发言并响应。数值越小响应越快，但也更容易被噪音误导打断。
                                    </p>
                                </div>

                                <div className="space-y-3">
                                    <label className="text-sm font-medium text-white flex items-center">
                                        <Mic className="w-4 h-4 mr-2 text-primary"/> ASR 热词优化 (Hotwords)
                                    </label>
                                    <div className="flex space-x-2">
                                        <Input 
                                            value={hotwordInput}
                                            onChange={(e) => setHotwordInput(e.target.value)}
                                            placeholder="输入专有名词或高频词汇..."
                                            onKeyDown={(e) => e.key === 'Enter' && addHotword()}
                                            className="bg-white/5 border-white/10"
                                        />
                                        <Button variant="secondary" onClick={addHotword} className="px-10 whitespace-nowrap">添加</Button>
                                    </div>
                                    <div className="flex flex-wrap gap-2 min-h-[40px] p-2 rounded-lg border border-dashed border-white/10">
                                        {selectedAssistant.hotwords.length === 0 && (
                                            <span className="text-xs text-muted-foreground py-1 px-1">暂无热词</span>
                                        )}
                                        {selectedAssistant.hotwords.map((word, idx) => (
                                            <Badge key={idx} variant="outline" className="py-1">
                                                {word}
                                                <button onClick={() => removeHotword(word)} className="ml-2 hover:text-destructive transition-colors text-lg leading-none">×</button>
                                            </Badge>
                                        ))}
                                    </div>
                                    <p className="text-xs text-muted-foreground">添加热词可以提高语音识别特定词汇的准确率。</p>
                                </div>
                            </div>
                        )}

                        {activeTab === TabValue.TOOLS && selectedAssistant.configMode === 'platform' && (
                            <div className="space-y-8 animate-in fade-in">
                                <div className="space-y-4">
                                    <div className="flex items-center justify-between">
                                        <h3 className="text-[10px] font-black flex items-center text-primary uppercase tracking-[0.2em]">
                                            <Wrench className="w-3.5 h-3.5 mr-2" /> 系统指令
                                        </h3>
                                    </div>
                                    <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
                                        {systemTools.map(tool => (
                                            <div 
                                                key={tool.id}
                                                onClick={() => toggleTool(tool.id)}
                                                className={`p-4 rounded-xl border transition-all cursor-pointer group relative flex items-start space-x-3 ${selectedAssistant.tools?.includes(tool.id) ? 'bg-primary/10 border-primary/40 shadow-[0_0_15px_rgba(6,182,212,0.1)]' : 'bg-card/30 border-white/5 hover:bg-white/5 hover:border-white/10'}`}
                                            >
                                                <div className={`p-2 rounded-lg shrink-0 transition-colors ${selectedAssistant.tools?.includes(tool.id) ? 'bg-primary text-primary-foreground' : 'bg-white/5 text-muted-foreground'}`}>
                                                    {renderToolIcon(tool.icon)}
                                                </div>
                                                <div className="flex-1 min-w-0">
                                                    <div className="flex items-center justify-between mb-0.5">
                                                        <span className="text-sm font-bold text-white">{tool.name}</span>
                                                        <div className={`w-4 h-4 rounded-full border-2 flex items-center justify-center transition-all ${selectedAssistant.tools?.includes(tool.id) ? 'border-primary bg-primary' : 'border-white/10'}`}>
                                                            {selectedAssistant.tools?.includes(tool.id) && <div className="w-1.5 h-1.5 bg-white rounded-full"></div>}
                                                        </div>
                                                    </div>
                                                    <p className="text-[10px] text-muted-foreground line-clamp-1 opacity-70">{tool.description}</p>
                                                </div>
                                                {selectedAssistant.tools?.includes(tool.id) && (
                                                  <button 
                                                      onClick={(e) => removeImportedTool(e, tool)}
                                                      className="absolute -top-1 -right-1 p-0.5 rounded-full bg-destructive text-white opacity-0 group-hover:opacity-100 transition-opacity hover:scale-110 shadow-lg z-10"
                                                      title="从当前小助手移除"
                                                  >
                                                      <X className="w-3 h-3" />
                                                  </button>
                                                )}
                                            </div>
                                        ))}
                                    </div>
                                </div>

                                <div className="space-y-4">
                                    <div className="flex items-center justify-between">
                                        <h3 className="text-[10px] font-black flex items-center text-blue-400 uppercase tracking-[0.2em]">
                                            <TrendingUp className="w-3.5 h-3.5 mr-2" /> 信息查询
                                        </h3>
                                    </div>
                                    <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
                                        {queryTools.map(tool => (
                                            <div 
                                                key={tool.id}
                                                onClick={() => toggleTool(tool.id)}
                                                className={`p-4 rounded-xl border transition-all cursor-pointer group relative flex items-start space-x-3 ${selectedAssistant.tools?.includes(tool.id) ? 'bg-blue-500/10 border-blue-500/40 shadow-[0_0_15px_rgba(59,130,246,0.1)]' : 'bg-card/30 border-white/5 hover:bg-white/5 hover:border-white/10'}`}
                                            >
                                                <div className={`p-2 rounded-lg shrink-0 transition-colors ${selectedAssistant.tools?.includes(tool.id) ? 'bg-blue-500 text-white' : 'bg-white/5 text-muted-foreground'}`}>
                                                    {renderToolIcon(tool.icon)}
                                                </div>
                                                <div className="flex-1 min-w-0">
                                                    <div className="flex items-center justify-between mb-0.5">
                                                        <span className="text-sm font-bold text-white">{tool.name}</span>
                                                        <div className={`w-4 h-4 rounded-full border-2 flex items-center justify-center transition-all ${selectedAssistant.tools?.includes(tool.id) ? 'border-blue-500 bg-blue-500' : 'border-white/10'}`}>
                                                            {selectedAssistant.tools?.includes(tool.id) && <div className="w-1.5 h-1.5 bg-white rounded-full"></div>}
                                                        </div>
                                                    </div>
                                                    <p className="text-[10px] text-muted-foreground line-clamp-1 opacity-70">{tool.description}</p>
                                                </div>
                                                {selectedAssistant.tools?.includes(tool.id) && (
                                                  <button 
                                                      onClick={(e) => removeImportedTool(e, tool)}
                                                      className="absolute -top-1 -right-1 p-0.5 rounded-full bg-destructive text-white opacity-0 group-hover:opacity-100 transition-opacity hover:scale-110 shadow-lg z-10"
                                                      title="从当前小助手移除"
                                                  >
                                                      <X className="w-3 h-3" />
                                                  </button>
                                                )}
                                            </div>
                                        ))}
                                    </div>
                                </div>
                                <div className="p-4 bg-white/5 border border-white/5 rounded-xl text-[10px] text-muted-foreground flex items-center gap-3">
                                    <Rocket className="w-4 h-4 text-primary shrink-0" />
                                    <span>提示：此处仅导入工具库中的已有工具。移除仅对当前小助手生效，不会删除工具库。</span>
                                </div>
                            </div>
                        )}
                      </div>
                    )}
                </div>
            </>
        ) : (
            <div className="flex-1 flex flex-col items-center justify-center text-muted-foreground">
                <div className="w-16 h-16 rounded-2xl bg-white/5 flex items-center justify-center mb-4">
                    <BotIcon className="h-8 w-8 opacity-50" />
                </div>
                <p className="text-lg font-medium">请选择一个小助手</p>
                <p className="text-sm opacity-60">从左侧列表选择或创建一个新的小助手以开始配置</p>
            </div>
        )}
      </div>

      {/* Publish Modal */}
      <Dialog
        isOpen={isPublishModalOpen}
        onClose={() => setIsPublishModalOpen(false)}
        title="发布小助手"
        footer={
            <Button onClick={() => setIsPublishModalOpen(false)}>确认</Button>
        }
      >
        <div className="space-y-6">
            <div className="flex bg-white/5 p-1 rounded-lg border border-white/10">
                <button 
                    onClick={() => setPublishTab('web')}
                    className={`flex-1 flex items-center justify-center py-2 text-xs font-bold rounded-md transition-all ${publishTab === 'web' ? 'bg-primary text-primary-foreground shadow-lg' : 'text-muted-foreground hover:text-foreground'}`}
                >
                    <ExternalLink className="w-3.5 h-3.5 mr-2" /> 网页分享
                </button>
                <button 
                    onClick={() => setPublishTab('api')}
                    className={`flex-1 flex items-center justify-center py-2 text-xs font-bold rounded-md transition-all ${publishTab === 'api' ? 'bg-primary text-primary-foreground shadow-lg' : 'text-muted-foreground hover:text-foreground'}`}
                >
                    <Server className="w-3.5 h-3.5 mr-2" /> API 接入
                </button>
            </div>

            {publishTab === 'web' ? (
                <div className="space-y-4 animate-in fade-in slide-in-from-top-1">
                    <div className="p-4 rounded-xl bg-primary/5 border border-primary/20 space-y-3">
                        <div className="flex items-center gap-2 text-primary">
                            <Zap className="w-4 h-4" />
                            <h4 className="text-sm font-bold">交互体验站</h4>
                        </div>
                        <p className="text-xs text-muted-foreground leading-relaxed">
                            该链接允许用户通过独立的浏览器页面与您的智能体进行交互。支持：文本对话、实时音频通话以及双向视频通话。
                        </p>
                    </div>
                    <div className="space-y-2">
                        <label className="text-[10px] text-muted-foreground font-black tracking-widest uppercase">公开访问链接</label>
                        <div className="flex gap-2">
                            <Input 
                                readOnly 
                                value={`https://ai-video.com/share/${selectedAssistant?.id}`} 
                                className="bg-white/5 border-white/10 font-mono text-[11px] text-primary"
                            />
                            <Button variant="secondary" size="icon" onClick={() => handleCopyId('', `https://ai-video.com/share/${selectedAssistant?.id}`)}>
                                {copySuccess ? <ClipboardCheck className="h-4 w-4 text-green-400" /> : <Copy className="h-4 w-4" />}
                            </Button>
                        </div>
                    </div>
                </div>
            ) : (
                <div className="space-y-5 animate-in fade-in slide-in-from-top-1">
                    <div className="space-y-2">
                        <label className="text-[10px] text-muted-foreground font-black tracking-widest uppercase">API Endpoint (v1)</label>
                        <div className="flex gap-2">
                            <Input 
                                readOnly 
                                value={`https://api.ai-video.com/v1/call/${selectedAssistant?.id}`} 
                                className="bg-white/5 border-white/10 font-mono text-[11px]"
                            />
                            <Button variant="ghost" size="icon" onClick={() => handleCopyId('', `https://api.ai-video.com/v1/call/${selectedAssistant?.id}`)}>
                                <Copy className="h-4 w-4" />
                            </Button>
                        </div>
                    </div>
                    <div className="space-y-2">
                        <div className="flex items-center justify-between">
                            <label className="text-[10px] text-muted-foreground font-black tracking-widest uppercase">Secret API KEY</label>
                            <Badge variant="outline" className="text-[8px] opacity-50">PRIVATE</Badge>
                        </div>
                        <div className="flex gap-2">
                            <Input 
                                readOnly 
                                type="password"
                                value="sk-ai-video-78x29jKkL1M90vX..." 
                                className="bg-white/5 border-white/10 font-mono text-[11px]"
                            />
                            <Button variant="ghost" size="icon" onClick={() => handleCopyId('', "sk-ai-video-78x29jKkL1M90vX...")}>
                                <Copy className="h-4 w-4" />
                            </Button>
                        </div>
                        <p className="text-[10px] text-muted-foreground italic flex items-center gap-1.5 px-1 mt-2">
                            <Key className="w-3 h-3" /> API Key 仅用于身份鉴权，请务必妥善保存。
                        </p>
                    </div>
                </div>
            )}
        </div>
      </Dialog>

      {selectedAssistant && (
         <DebugDrawer 
           isOpen={debugOpen} 
           onClose={() => setDebugOpen(false)} 
           assistant={selectedAssistant}
           voices={voices}
           llmModels={llmModels}
           asrModels={asrModels}
         />
      )}

      {/* Delete Confirmation Dialog */}
      <Dialog 
        isOpen={!!deleteId} 
        onClose={() => setDeleteId(null)} 
        title="确认删除"
        footer={
            <>
                <Button variant="ghost" onClick={() => setDeleteId(null)}>取消</Button>
                <Button variant="destructive" onClick={confirmDelete}>确认删除</Button>
            </>
        }
      >
        <div className="flex items-center space-x-4">
            <div className="p-3 bg-destructive/10 rounded-full">
                <AlertTriangle className="h-6 w-6 text-destructive" />
            </div>
            <div>
                <p className="text-sm text-foreground text-white">
                    您确定要删除此小助手吗？此操作无法撤销。
                </p>
                {deleteId && (
                    <p className="text-xs text-muted-foreground mt-1">
                        将要删除: {assistants.find(a => a.id === deleteId)?.name}
                    </p>
                )}
            </div>
        </div>
      </Dialog>
    </div>
  );
};

// Icon helper
const BotIcon = ({className}: {className?: string}) => (
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" className={className}>
        <path d="M12 8V4H8" />
        <rect width="16" height="12" x="4" y="8" rx="2" />
        <path d="M2 14h2" />
        <path d="M20 14h2" />
        <path d="M15 13v2" />
        <path d="M9 13v2" />
    </svg>
);

// Stable transcription log so the scroll container is not recreated on every render (avoids scroll jumping)
const TranscriptionLog: React.FC<{
  scrollRef: React.RefObject<HTMLDivElement | null>;
  messages: { role: 'user' | 'model'; text: string }[];
  isLoading: boolean;
  className?: string;
}> = ({ scrollRef, messages, isLoading, className = '' }) => (
  <div ref={scrollRef} className={`overflow-y-auto overflow-x-hidden space-y-4 p-2 border border-white/5 rounded-md bg-black/20 min-h-0 custom-scrollbar ${className}`}>
    {messages.length === 0 && <div className="text-center text-muted-foreground text-xs py-4">暂无转写记录</div>}
    {messages.map((m, i) => (
      <div key={i} className={`flex ${m.role === 'user' ? 'justify-end' : 'justify-start'}`}>
        <div className={`max-w-[85%] rounded-lg px-3 py-2 text-sm ${m.role === 'user' ? 'bg-primary text-primary-foreground' : 'bg-card border border-white/10 shadow-sm text-foreground'}`}>
          <span className="text-[10px] opacity-70 block mb-0.5 uppercase tracking-wider">{m.role === 'user' ? 'Me' : 'AI'}</span>
          {m.text}
        </div>
      </div>
    ))}
    {isLoading && <div className="text-xs text-muted-foreground ml-2 animate-pulse">Thinking...</div>}
  </div>
);

// --- Debug Drawer Component ---
export const DebugDrawer: React.FC<{
  isOpen: boolean;
  onClose: () => void;
  assistant: Assistant;
  voices: Voice[];
  llmModels: LLMModel[];
  asrModels: ASRModel[];
}> = ({ isOpen, onClose, assistant, voices, llmModels, asrModels }) => {
  const [mode, setMode] = useState<'text' | 'voice' | 'video'>('text');
  const [messages, setMessages] = useState<{role: 'user' | 'model', text: string}[]>([]);
  const [inputText, setInputText] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const [callStatus, setCallStatus] = useState<'idle' | 'calling' | 'active'>('idle');
  const [textSessionStarted, setTextSessionStarted] = useState(false);
  const [wsStatus, setWsStatus] = useState<'disconnected' | 'connecting' | 'ready' | 'error'>('disconnected');
  const [wsError, setWsError] = useState('');
  const [resolvedConfigOpen, setResolvedConfigOpen] = useState(false);
  const [resolvedConfigView, setResolvedConfigView] = useState<string>('');
  const [settingsDrawerOpen, setSettingsDrawerOpen] = useState(false);
  const [wsUrl, setWsUrl] = useState<string>(() => {
    const fromStorage = localStorage.getItem('debug_ws_url');
    if (fromStorage) return fromStorage;
    const defaultHost = window.location.hostname || 'localhost';
    return `ws://${defaultHost}:8000/ws`;
  });
  
  // Media State
  const videoRef = useRef<HTMLVideoElement>(null);
  const streamRef = useRef<MediaStream | null>(null);
  const scrollRef = useRef<HTMLDivElement>(null);
  const wsRef = useRef<WebSocket | null>(null);
  const wsReadyRef = useRef(false);
  const pendingResolveRef = useRef<(() => void) | null>(null);
  const pendingRejectRef = useRef<((e: Error) => void) | null>(null);
  const assistantDraftIndexRef = useRef<number | null>(null);
  const audioCtxRef = useRef<AudioContext | null>(null);
  const playbackTimeRef = useRef<number>(0);
  const activeAudioSourcesRef = useRef<Set<AudioBufferSourceNode>>(new Set());
  const queuedAudioBuffersRef = useRef<AudioBuffer[]>([]);
  const queuedAudioDurationRef = useRef<number>(0);

  const PLAYBACK_INITIAL_BUFFER_SECONDS = 0.25;
  const PLAYBACK_MAX_AHEAD_SECONDS = 0.8;
  const PLAYBACK_SCHEDULE_LEAD_SECONDS = 0.04;

  const [devices, setDevices] = useState<MediaDeviceInfo[]>([]);
  const [selectedCamera, setSelectedCamera] = useState<string>('');
  const [selectedMic, setSelectedMic] = useState<string>('');
  const [isSwapped, setIsSwapped] = useState(false); 
  const [textTtsEnabled, setTextTtsEnabled] = useState(true);

  // Initialize
  useEffect(() => {
    if (isOpen) {
      if (mode === 'text') {
        setMessages([]);
        setTextSessionStarted(false);
      } else {
        setMessages([]);
        setCallStatus('idle');
      }
    } else {
        setMode('text');
        stopMedia();
        closeWs();
        if (audioCtxRef.current) {
          void audioCtxRef.current.close();
          audioCtxRef.current = null;
        }
        setSettingsDrawerOpen(false);
        setIsSwapped(false);
        setCallStatus('idle');
    }
  }, [isOpen, assistant, mode]);

  useEffect(() => {
    localStorage.setItem('debug_ws_url', wsUrl);
  }, [wsUrl]);

  // Auto-scroll logic
  useEffect(() => {
      if (scrollRef.current) {
          scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
      }
  }, [messages, mode]);

  // Fetch Devices
  useEffect(() => {
    if (isOpen && mode === 'video') {
        const getDevices = async () => {
            try {
                await navigator.mediaDevices.getUserMedia({ audio: true, video: true });
                const dev = await navigator.mediaDevices.enumerateDevices();
                setDevices(dev);
                const cams = dev.filter(d => d.kind === 'videoinput');
                const mics = dev.filter(d => d.kind === 'audioinput');
                if (cams.length > 0 && !selectedCamera) setSelectedCamera(cams[0].deviceId);
                if (mics.length > 0 && !selectedMic) setSelectedMic(mics[0].deviceId);
            } catch (e) {
                console.error("Error enumerating devices", e);
            }
        };
        getDevices();
    }
  }, [isOpen, mode]);

  const stopMedia = () => {
      if (streamRef.current) {
        streamRef.current.getTracks().forEach(track => track.stop());
        streamRef.current = null;
      }
  };

  const ensureAudioContext = async () => {
    if (!audioCtxRef.current) {
      audioCtxRef.current = new AudioContext();
      playbackTimeRef.current = audioCtxRef.current.currentTime;
    }
    if (audioCtxRef.current.state === 'suspended') {
      await audioCtxRef.current.resume();
    }
    return audioCtxRef.current;
  };

  const clearPlaybackQueue = () => {
    const ctx = audioCtxRef.current;
    const now = ctx ? ctx.currentTime : 0;
    playbackTimeRef.current = now;
    queuedAudioBuffersRef.current = [];
    queuedAudioDurationRef.current = 0;
  };

  const stopPlaybackImmediately = () => {
    activeAudioSourcesRef.current.forEach((source) => {
      try {
        source.stop();
      } catch {
        // no-op
      }
      source.disconnect();
    });
    activeAudioSourcesRef.current.clear();
    clearPlaybackQueue();
  };

  const scheduleQueuedPlayback = (ctx: AudioContext) => {
    const queue = queuedAudioBuffersRef.current;
    if (queue.length === 0) return;

    const now = ctx.currentTime;
    if (playbackTimeRef.current < now) {
      playbackTimeRef.current = now;
    }

    const hasActivePlayback = activeAudioSourcesRef.current.size > 0;
    const minBufferSeconds = hasActivePlayback
      ? 0
      : PLAYBACK_INITIAL_BUFFER_SECONDS;

    if (queuedAudioDurationRef.current < minBufferSeconds) {
      return;
    }

    while (queue.length > 0 && (playbackTimeRef.current - now) < PLAYBACK_MAX_AHEAD_SECONDS) {
      const audioBuffer = queue.shift();
      if (!audioBuffer) break;
      queuedAudioDurationRef.current = Math.max(0, queuedAudioDurationRef.current - audioBuffer.duration);

      const source = ctx.createBufferSource();
      source.buffer = audioBuffer;
      source.connect(ctx.destination);
      activeAudioSourcesRef.current.add(source);
      source.onended = () => {
        activeAudioSourcesRef.current.delete(source);
        try {
          source.disconnect();
        } catch {
          // no-op
        }
        scheduleQueuedPlayback(ctx);
      };

      const startAt = Math.max(ctx.currentTime + PLAYBACK_SCHEDULE_LEAD_SECONDS, playbackTimeRef.current);
      source.start(startAt);
      playbackTimeRef.current = startAt + audioBuffer.duration;
    }
  };

  const playPcm16Chunk = async (pcmBuffer: ArrayBuffer) => {
    if (!textTtsEnabled) return;
    if (mode !== 'text') return;
    const ctx = await ensureAudioContext();
    const int16 = new Int16Array(pcmBuffer);
    if (int16.length === 0) return;

    const float32 = new Float32Array(int16.length);
    for (let i = 0; i < int16.length; i += 1) {
      float32[i] = int16[i] / 32768;
    }

    const sampleRate = 16000;
    const audioBuffer = ctx.createBuffer(1, float32.length, sampleRate);
    audioBuffer.copyToChannel(float32, 0);
    queuedAudioBuffersRef.current.push(audioBuffer);
    queuedAudioDurationRef.current += audioBuffer.duration;
    scheduleQueuedPlayback(ctx);
  };

  useEffect(() => {
    const handleStream = async () => {
      if (isOpen && mode === 'video' && callStatus === 'active') {
        try {
          stopMedia();
          const constraints = {
              video: selectedCamera ? { deviceId: { exact: selectedCamera } } : true,
              audio: selectedMic ? { deviceId: { exact: selectedMic } } : true
          };
          const stream = await navigator.mediaDevices.getUserMedia(constraints);
          streamRef.current = stream;
          if (videoRef.current) {
            videoRef.current.srcObject = stream;
          }
        } catch (err) {
          console.error("Failed to access camera/mic:", err);
        }
      } else if (callStatus !== 'active') {
        stopMedia();
      }
    };

    handleStream();
    return () => stopMedia();
  }, [mode, isOpen, selectedCamera, selectedMic, callStatus]);

  const handleCall = () => {
    setCallStatus('calling');
    setTimeout(() => {
        setCallStatus('active');
        setMessages([{ role: 'model', text: assistant.opener || "Hello!" }]);
    }, 1500);
  };

  const handleHangup = () => {
    stopMedia();
    setCallStatus('idle');
    setMessages([]);
  };

  const handleSend = async () => {
    if (!inputText.trim()) return;
    const userMsg = inputText;
    assistantDraftIndexRef.current = null;
    setMessages(prev => [...prev, { role: 'user', text: userMsg }]);
    setInputText('');
    setIsLoading(true);

    try {
      if (mode === 'text') {
        if (textTtsEnabled) await ensureAudioContext();
        await ensureWsSession();
        // Interrupt any in-flight response/audio before sending new user utterance.
        if (wsRef.current?.readyState === WebSocket.OPEN) {
          wsRef.current.send(JSON.stringify({ type: 'response.cancel', graceful: false }));
        }
        stopPlaybackImmediately();
        wsRef.current?.send(JSON.stringify({ type: 'input.text', text: userMsg }));
      } else {
        setTimeout(() => {
          setMessages(prev => [...prev, { role: 'model', text: `[Mock Response]: Received "${userMsg}"` }]);
          setIsLoading(false);
        }, 1000);
      }
    } catch (e) {
      console.error(e);
      setMessages(prev => [...prev, { role: 'model', text: "Error: Failed to connect to AI service." }]);
      setIsLoading(false);
    } finally {
      if (mode !== 'text') setIsLoading(false);
    }
  };

  const handleTextLaunch = async () => {
    try {
      setWsError('');
      // Start every text debug run as a fresh session transcript.
      setMessages([]);
      assistantDraftIndexRef.current = null;
      // Force a fresh WS session so updated assistant runtime config
      // (voice/model/provider/speed) is applied on session.start.
      closeWs();
      if (textTtsEnabled) await ensureAudioContext();
      await ensureWsSession();
      setTextSessionStarted(true);
      setMessages([{ role: 'model', text: assistant.opener || 'Hello!' }]);
    } catch (e) {
      console.error(e);
      setWsStatus('error');
      setWsError((e as Error)?.message || 'Failed to connect');
      setTextSessionStarted(false);
    }
  };

  const buildLocalResolvedRuntime = () => {
    const warnings: string[] = [];
    const services: Record<string, any> = {};
    const isExternalLlm = assistant.configMode === 'dify' || assistant.configMode === 'fastgpt';

    if (isExternalLlm) {
      services.llm = {
        provider: 'openai',
        model: '',
        apiKey: assistant.apiKey || '',
        baseUrl: assistant.apiUrl || '',
      };
      if (!assistant.apiUrl) warnings.push(`External LLM API URL is empty for mode: ${assistant.configMode}`);
      if (!assistant.apiKey) warnings.push(`External LLM API key is empty for mode: ${assistant.configMode}`);
    } else if (assistant.llmModelId) {
        const llm = llmModels.find((item) => item.id === assistant.llmModelId);
        if (llm) {
          services.llm = {
            provider: 'openai',
            model: llm.modelName || llm.name,
            apiKey: llm.apiKey,
            baseUrl: llm.baseUrl,
          };
        } else {
          warnings.push(`LLM model not found in loaded list: ${assistant.llmModelId}`);
        }
    } else {
      // Keep empty object to indicate engine should use default provider model.
      services.llm = {};
    }

    if (assistant.asrModelId) {
      const asr = asrModels.find((item) => item.id === assistant.asrModelId);
      if (asr) {
        const asrProvider = isSiliconflowVendor(asr.vendor) ? 'siliconflow' : 'buffered';
        services.asr = {
          provider: asrProvider,
          model: asr.modelName || asr.name,
          apiKey: asrProvider === 'siliconflow' ? asr.apiKey : null,
        };
      } else {
        warnings.push(`ASR model not found in loaded list: ${assistant.asrModelId}`);
      }
    }

    if (assistant.voice) {
      const voice = voices.find((item) => item.id === assistant.voice);
      if (voice) {
        const ttsProvider = isSiliconflowVendor(voice.vendor) ? 'siliconflow' : 'edge';
        services.tts = {
          provider: ttsProvider,
          model: voice.model,
          apiKey: ttsProvider === 'siliconflow' ? voice.apiKey : null,
          voice: resolveRuntimeTtsVoice(assistant.voice, voice),
          speed: assistant.speed || voice.speed || 1.0,
        };
      } else {
        services.tts = {
          voice: assistant.voice,
          speed: assistant.speed || 1.0,
        };
        warnings.push(`Voice resource not found in loaded list: ${assistant.voice}`);
      }
    }

    const localResolved = {
      assistantId: assistant.id,
      warnings,
      sessionStartMetadata: {
        systemPrompt: assistant.prompt || '',
        greeting: assistant.opener || '',
        services,
        history: {
          assistantId: assistant.id,
          userId: 1,
          source: 'debug',
        },
      },
    };

    return localResolved;
  };

  const fetchRuntimeMetadata = async (): Promise<Record<string, any>> => {
    const localResolved = buildLocalResolvedRuntime();
    setResolvedConfigView(JSON.stringify(localResolved, null, 2));
    return localResolved.sessionStartMetadata;
  };

  const closeWs = () => {
    if (wsRef.current && wsRef.current.readyState === WebSocket.OPEN) {
      wsRef.current.send(JSON.stringify({ type: 'session.stop', reason: 'debug_drawer_closed' }));
    }
    wsRef.current?.close();
    wsRef.current = null;
    wsReadyRef.current = false;
    pendingResolveRef.current = null;
    pendingRejectRef.current = null;
    assistantDraftIndexRef.current = null;
    setTextSessionStarted(false);
    stopPlaybackImmediately();
    if (isOpen) setWsStatus('disconnected');
  };

  const ensureWsSession = async () => {
    if (wsRef.current && wsReadyRef.current && wsRef.current.readyState === WebSocket.OPEN) {
      return;
    }

    if (wsRef.current && wsRef.current.readyState === WebSocket.CONNECTING) {
      await new Promise<void>((resolve, reject) => {
        pendingResolveRef.current = resolve;
        pendingRejectRef.current = reject;
      });
      return;
    }

    const metadata = await fetchRuntimeMetadata();
    setWsStatus('connecting');
    setWsError('');

    await new Promise<void>((resolve, reject) => {
      pendingResolveRef.current = resolve;
      pendingRejectRef.current = reject;
      const ws = new WebSocket(wsUrl);
      ws.binaryType = 'arraybuffer';
      wsRef.current = ws;

      ws.onopen = () => {
        ws.send(JSON.stringify({ type: 'hello', version: 'v1' }));
      };

      ws.onmessage = (event) => {
        if (event.data instanceof ArrayBuffer) {
          void playPcm16Chunk(event.data);
          return;
        }
        if (event.data instanceof Blob) {
          void event.data.arrayBuffer().then((buf) => playPcm16Chunk(buf));
          return;
        }
        if (typeof event.data !== 'string') return;
        let payload: any;
        try {
          payload = JSON.parse(event.data);
        } catch {
          return;
        }

        const type = payload?.type;
        if (type === 'hello.ack') {
          ws.send(
            JSON.stringify({
              type: 'session.start',
              audio: { encoding: 'pcm_s16le', sample_rate_hz: 16000, channels: 1 },
              metadata,
            })
          );
          return;
        }
        if (type === 'output.audio.start') {
          // New utterance audio starts: cancel old queued/playing audio to avoid overlap.
          stopPlaybackImmediately();
          return;
        }

        if (type === 'response.interrupted') {
          assistantDraftIndexRef.current = null;
          setIsLoading(false);
          stopPlaybackImmediately();
          return;
        }

        if (type === 'session.started') {
          wsReadyRef.current = true;
          setWsStatus('ready');
          pendingResolveRef.current?.();
          pendingResolveRef.current = null;
          pendingRejectRef.current = null;
          return;
        }

        if (type === 'assistant.response.delta') {
          const delta = String(payload.text || '');
          if (!delta) return;
          setMessages((prev) => {
            const idx = assistantDraftIndexRef.current;
            if (idx === null || !prev[idx] || prev[idx].role !== 'model') {
              const last = prev[prev.length - 1];
              if (last?.role === 'model' && last.text === delta) {
                return prev;
              }
              const next = [...prev, { role: 'model' as const, text: delta }];
              assistantDraftIndexRef.current = next.length - 1;
              return next;
            }
            const next = [...prev];
            next[idx] = { ...next[idx], text: next[idx].text + delta };
            return next;
          });
          return;
        }

        if (type === 'assistant.response.final') {
          const finalText = String(payload.text || '');
          setMessages((prev) => {
            const idx = assistantDraftIndexRef.current;
            assistantDraftIndexRef.current = null;
            if (idx !== null && prev[idx] && prev[idx].role === 'model') {
              const next = [...prev];
              next[idx] = { ...next[idx], text: finalText || next[idx].text };
              return next;
            }
            if (!finalText) return prev;
            const last = prev[prev.length - 1];
            if (last?.role === 'model') {
              if (last.text === finalText) return prev;
              if (finalText.startsWith(last.text) || last.text.startsWith(finalText)) {
                const next = [...prev];
                next[next.length - 1] = { ...last, text: finalText };
                return next;
              }
            }
            return [...prev, { role: 'model', text: finalText }];
          });
          setIsLoading(false);
          return;
        }

        if (type === 'error') {
          const message = String(payload.message || 'Unknown error');
          setWsStatus('error');
          setWsError(message);
          setIsLoading(false);
          const err = new Error(message);
          pendingRejectRef.current?.(err);
          pendingResolveRef.current = null;
          pendingRejectRef.current = null;
        }
      };

      ws.onerror = () => {
        const err = new Error('WebSocket connection error');
        setWsStatus('error');
        setWsError(err.message);
        setIsLoading(false);
        pendingRejectRef.current?.(err);
        pendingResolveRef.current = null;
        pendingRejectRef.current = null;
      };

      ws.onclose = () => {
        wsReadyRef.current = false;
        setTextSessionStarted(false);
        stopPlaybackImmediately();
        if (wsStatus !== 'error') setWsStatus('disconnected');
      };
    });
  };

  useEffect(() => {
    if (!isOpen) return;
    if (!wsRef.current || wsRef.current.readyState !== WebSocket.OPEN) return;
    // If core TTS-related settings changed while drawer stays open,
    // reset the active WS session so the next launch uses new metadata.
    closeWs();
  }, [isOpen, assistant.id, assistant.voice, assistant.speed]);

  useEffect(() => {
    if (!textTtsEnabled) {
      stopPlaybackImmediately();
    }
  }, [textTtsEnabled]);

  useEffect(() => {
    if (!isOpen) return;
    const localResolved = buildLocalResolvedRuntime();
    setResolvedConfigView(JSON.stringify(localResolved, null, 2));
  }, [isOpen, assistant, voices, llmModels, asrModels]);

  const renderLocalVideo = (isSmall: boolean) => (
      <div className={`relative w-full h-full bg-black overflow-hidden ${isSmall ? 'rounded-lg border border-white/20 shadow-lg' : ''}`}>
          <video ref={videoRef} autoPlay muted playsInline className="w-full h-full object-cover transform scale-x-[-1]" />
          <div className="absolute top-2 left-2 bg-black/50 px-2 py-0.5 rounded text-[10px] text-white/80">Me</div>
      </div>
  );

  const renderRemoteVideo = (isSmall: boolean) => (
      <div className={`relative w-full h-full bg-slate-900 overflow-hidden flex flex-col items-center justify-center ${isSmall ? 'rounded-lg border border-white/20 shadow-lg' : ''}`}>
           <div className="relative flex items-center justify-center">
               <div className={`rounded-full bg-primary/20 animate-pulse ${isSmall ? 'w-16 h-16' : 'w-32 h-32'}`}></div>
               <div className={`absolute rounded-full bg-primary flex items-center justify-center shadow-[0_0_30px_hsl(var(--primary))] ${isSmall ? 'w-12 h-12' : 'w-24 h-24'}`}>
                   <Video className={`${isSmall ? 'w-6 h-6' : 'w-10 h-10'} text-primary-foreground`} />
               </div>
           </div>
           {!isSmall && <div className="mt-4 font-mono text-primary animate-pulse text-sm">{assistant.name}</div>}
      </div>
  );

  const settingsPanel = (
    <div className="h-full border-r border-white/10 bg-background/95 backdrop-blur-md shadow-2xl p-3">
      <div className="flex items-center justify-between mb-2">
        <h3 className="text-xs font-black tracking-[0.15em] uppercase text-muted-foreground">调试设置</h3>
        <Button size="icon" variant="ghost" className="h-7 w-7" onClick={() => setSettingsDrawerOpen(false)}>
          <X className="h-3.5 w-3.5" />
        </Button>
      </div>

      <div className="space-y-3">
        <div className="space-y-1.5">
          <label className="text-[10px] text-muted-foreground font-black tracking-widest uppercase block">WebSocket Endpoint</label>
          <Input value={wsUrl} onChange={(e) => setWsUrl(e.target.value)} placeholder="ws://localhost:8000/ws" />
        </div>
        <div className="flex items-center justify-between gap-2">
          <Badge variant="outline" className="text-xs">WS: {wsStatus}</Badge>
          <label className="inline-flex items-center gap-1 text-xs text-muted-foreground px-2 py-1 rounded border border-white/10">
            <input
              type="checkbox"
              checked={textTtsEnabled}
              onChange={(e) => setTextTtsEnabled(e.target.checked)}
              className="accent-primary"
            />
            TTS
          </label>
        </div>
        {wsError && <p className="text-xs text-red-400">{wsError}</p>}

        <div className="rounded-md border border-white/10 bg-black/30">
          <button
            className="w-full px-3 py-2 text-left text-xs text-muted-foreground hover:text-foreground flex items-center justify-between"
            onClick={() => setResolvedConfigOpen((v) => !v)}
          >
            <span>Resolved Runtime Config</span>
            <ChevronDown className={`h-3.5 w-3.5 transition-transform ${resolvedConfigOpen ? 'rotate-180' : ''}`} />
          </button>
          {resolvedConfigOpen && (
            <pre className="px-3 pb-3 text-[11px] leading-5 text-cyan-100/90 whitespace-pre-wrap break-all max-h-64 overflow-auto">
              {resolvedConfigView || 'Connect to load resolved config...'}
            </pre>
          )}
        </div>
      </div>
    </div>
  );

  return (
    <>
    <Drawer isOpen={isOpen} onClose={() => { handleHangup(); onClose(); }} title={`调试: ${assistant.name}`}>
      <div className="relative flex flex-col h-full min-h-0 overflow-hidden">
        <div className="flex items-center gap-2 mb-4 shrink-0">
          <div className="flex-1 flex justify-center bg-white/5 p-1 rounded-lg">
            {(['text', 'voice', 'video'] as const).map(m => (
              <button key={m} className={`flex-1 py-1 text-sm rounded-md transition-all ${mode === m ? 'bg-primary text-primary-foreground shadow' : 'text-muted-foreground hover:bg-white/5'}`} onClick={() => setMode(m)}>
                {m === 'text' && <MessageSquare className="inline w-4 h-4 mr-1" />}
                {m === 'voice' && <Mic className="inline w-4 h-4 mr-1" />}
                {m === 'video' && <Video className="inline w-4 h-4 mr-1" />}
                {m === 'text' ? '文本' : m === 'voice' ? '语音' : '视频'}
              </button>
            ))}
          </div>
        </div>

        <div className="flex-1 min-h-0 overflow-hidden flex flex-col">
            <div className={`flex-1 overflow-hidden flex flex-col min-h-0 ${mode === 'text' && textSessionStarted ? 'mb-0' : 'mb-4'}`}>
              {mode === 'text' ? (
                textSessionStarted ? (
                  <div className="flex-1 min-h-0 overflow-hidden animate-in fade-in flex flex-col">
                    <div className="h-[68vh] min-h-[420px] max-h-[68vh] w-full flex flex-col min-h-0 overflow-hidden">
                      <TranscriptionLog scrollRef={scrollRef} messages={messages} isLoading={isLoading} className="flex-1 min-h-0 h-full" />
                    </div>
                  </div>
                ) : wsStatus === 'connecting' ? (
                  <div className="flex-1 flex flex-col items-center justify-center space-y-6">
                    <div className="h-24 w-24 rounded-full bg-primary/20 flex items-center justify-center animate-bounce">
                      <MessageSquare className="h-10 w-10 text-primary" />
                    </div>
                    <div className="text-center">
                      <p className="text-primary font-mono text-sm tracking-widest animate-pulse">CONNECTING...</p>
                      <p className="text-xs text-muted-foreground mt-2">正在连接文本调试服务</p>
                    </div>
                    <Button onClick={closeWs} variant="destructive" className="rounded-full h-10 px-8">取消</Button>
                  </div>
                ) : (
                  <div className="flex-1 flex flex-col items-center justify-center space-y-6 border border-white/5 rounded-xl bg-black/20 animate-in fade-in zoom-in-95 px-6">
                    <div className="relative">
                      <div className="absolute inset-0 bg-primary/20 rounded-full blur-2xl animate-pulse"></div>
                      <div className="relative h-24 w-24 rounded-full bg-white/5 border border-white/10 flex items-center justify-center">
                        <MessageSquare className="h-10 w-10 text-muted-foreground" />
                      </div>
                    </div>
                    <div className="text-center">
                      <h3 className="text-lg font-bold text-white mb-1">准备就绪</h3>
                      <p className="text-xs text-muted-foreground">点击下方按钮开启人机交互测试</p>
                    </div>
                    <Button
                      onClick={handleTextLaunch}
                      disabled={wsStatus === 'connecting'}
                      className="w-48 h-12 rounded-full bg-green-500 hover:bg-green-600 shadow-[0_0_20px_rgba(34,197,94,0.4)] text-base font-bold"
                    >
                      <PhoneCall className="mr-2 h-5 w-5" /> 发起呼叫
                    </Button>
                  </div>
                )
              ) : callStatus === 'idle' ? (
                <div className="flex-1 flex flex-col items-center justify-center space-y-6 border border-white/5 rounded-xl bg-black/20 animate-in fade-in zoom-in-95">
                  <div className="relative">
                    <div className="absolute inset-0 bg-primary/20 rounded-full blur-2xl animate-pulse"></div>
                    <div className="relative h-24 w-24 rounded-full bg-white/5 border border-white/10 flex items-center justify-center">
                      {mode === 'voice' ? <Mic className="h-10 w-10 text-muted-foreground" /> : <Video className="h-10 w-10 text-muted-foreground" />}
                    </div>
                  </div>
                  <div className="text-center">
                    <h3 className="text-lg font-bold text-white mb-1">准备就绪</h3>
                    <p className="text-xs text-muted-foreground">点击下方按钮开启人机交互测试</p>
                  </div>
                  <Button onClick={handleCall} className="w-48 h-12 rounded-full bg-green-500 hover:bg-green-600 shadow-[0_0_20px_rgba(34,197,94,0.4)] text-base font-bold">
                    <PhoneCall className="mr-2 h-5 w-5" /> 发起呼叫
                  </Button>
                </div>
              ) : callStatus === 'calling' ? (
                <div className="flex-1 flex flex-col items-center justify-center space-y-6">
                  <div className="h-24 w-24 rounded-full bg-primary/20 flex items-center justify-center animate-bounce">
                    <PhoneCall className="h-10 w-10 text-primary" />
                  </div>
                  <div className="text-center">
                    <p className="text-primary font-mono text-sm tracking-widest animate-pulse">CALLING...</p>
                    <p className="text-xs text-muted-foreground mt-2">正在连接 AI 服务</p>
                  </div>
                  <Button onClick={handleHangup} variant="destructive" className="rounded-full h-10 px-8">取消</Button>
                </div>
              ) : (
                <div className="flex-1 flex flex-col min-h-0 space-y-2">
                  {mode === 'voice' ? (
                    <div className="flex flex-col h-full min-h-0 animate-in fade-in">
                      <div className="h-1/3 min-h-[150px] shrink-0 border border-white/5 rounded-md bg-black/20 flex flex-col items-center justify-center text-muted-foreground space-y-4 mb-2 relative overflow-hidden">
                        <div className="h-24 w-24 rounded-full bg-primary/10 flex items-center justify-center animate-pulse relative z-10">
                          <Mic className="h-10 w-10 text-primary" />
                        </div>
                        <p className="text-sm relative z-10">通话中...</p>
                      </div>
                      <h4 className="text-xs font-medium text-muted-foreground px-1 mb-1 uppercase tracking-tight shrink-0">转写日志</h4>
                      <TranscriptionLog scrollRef={scrollRef} messages={messages} isLoading={isLoading} className="flex-1 min-h-0 overflow-y-auto" />
                    </div>
                  ) : (
                    <div className="flex flex-col h-full min-h-0 space-y-2 animate-in fade-in">
                      <div className="h-3/5 shrink-0 flex flex-col gap-2">
                        <div className="flex gap-2 shrink-0">
                          <select className="flex-1 text-xs bg-white/5 border border-white/10 rounded px-2 py-1 text-foreground" value={selectedCamera} onChange={e => setSelectedCamera(e.target.value)}>
                            {devices.filter(d => d.kind === 'videoinput').map(d => <option key={d.deviceId} value={d.deviceId}>{d.label || 'Camera'}</option>)}
                          </select>
                          <select className="flex-1 text-xs bg-white/5 border border-white/10 rounded px-2 py-1 text-foreground" value={selectedMic} onChange={e => setSelectedMic(e.target.value)}>
                            {devices.filter(d => d.kind === 'audioinput').map(d => <option key={d.deviceId} value={d.deviceId}>{d.label || 'Mic'}</option>)}
                          </select>
                        </div>
                        <div className="flex-1 relative rounded-lg overflow-hidden border border-white/10 bg-black min-h-0">
                          <div className="absolute inset-0">{isSwapped ? renderLocalVideo(false) : renderRemoteVideo(false)}</div>
                          <div className="absolute bottom-2 right-2 w-24 h-36 z-10">{isSwapped ? renderRemoteVideo(true) : renderLocalVideo(true)}</div>
                          <button className="absolute top-2 right-2 z-20 h-8 w-8 rounded-full bg-black/50 backdrop-blur flex items-center justify-center text-white border border-white/10 hover:bg-primary/80" onClick={() => setIsSwapped(!isSwapped)}><ArrowLeftRight className="h-3.5 w-3.5" /></button>
                        </div>
                      </div>
                      <TranscriptionLog scrollRef={scrollRef} messages={messages} isLoading={isLoading} className="flex-1 min-h-0 overflow-y-auto" />
                    </div>
                  )}
                  <Button variant="destructive" size="sm" className="w-full h-10 font-bold" onClick={handleHangup}>
                    <PhoneOff className="mr-2 h-4 w-4" /> 挂断通话
                  </Button>
                </div>
              )}
            </div>

            <div className={mode === 'text' && textSessionStarted ? 'shrink-0 mt-3 px-1 mb-3' : 'shrink-0 space-y-2 mt-2 px-1 mb-3'}>
              <div className="w-full flex items-center gap-2 min-w-0">
                {mode === 'text' && textSessionStarted && (
                  <Button
                    variant="destructive"
                    size="sm"
                    className="h-9 px-3 font-bold shrink-0 whitespace-nowrap"
                    onClick={closeWs}
                  >
                    <PhoneOff className="h-4 w-4" />
                    <span className="ml-1.5">结束测试</span>
                  </Button>
                )}
                <Input
                  value={inputText}
                  onChange={e => setInputText(e.target.value)}
                  placeholder={mode === 'text' && !textSessionStarted ? "请先发起呼叫后输入消息..." : (mode === 'text' ? "输入消息..." : "输入文本模拟交互...")}
                  onKeyDown={e => e.key === 'Enter' && handleSend()}
                  disabled={mode === 'text' ? !textSessionStarted : (isLoading || callStatus !== 'active')}
                  className="flex-1 min-w-0"
                />
                <Button
                  size="icon"
                  className="h-9 w-9 shrink-0"
                  onClick={handleSend}
                  disabled={mode === 'text' ? !textSessionStarted : (isLoading || callStatus !== 'active')}
                >
                  <Send className="h-4 w-4" />
                </Button>
              </div>
            </div>
        </div>
      </div>
    </Drawer>
    {isOpen && (
      <div className="fixed inset-y-0 z-[51] right-[min(100vw,32rem)]">
        <button
          className={`absolute inset-y-0 w-10 border-r border-white/10 bg-background/90 backdrop-blur-md text-muted-foreground hover:text-foreground hover:bg-background/95 transition-[right,color,background-color] duration-300 flex flex-col items-center justify-center gap-2 ${settingsDrawerOpen ? 'right-[min(78vw,28rem)]' : 'right-0'}`}
          onClick={() => setSettingsDrawerOpen((v) => !v)}
          title={settingsDrawerOpen ? '收起调试设置' : '展开调试设置'}
        >
          <span className={`text-[10px] font-mono tracking-tight ${settingsDrawerOpen ? 'text-primary animate-pulse' : 'opacity-90 animate-pulse'}`}>
            {settingsDrawerOpen ? '>>' : '<<'}
          </span>
          <Wrench className="h-4 w-4" />
          <span className="text-[10px] tracking-widest [writing-mode:vertical-rl]">调试设置</span>
        </button>
        <div className="absolute inset-y-0 right-0 w-[78vw] max-w-md overflow-hidden pointer-events-none">
          <div className={`h-full transition-transform duration-300 ease-out will-change-transform ${settingsDrawerOpen ? 'translate-x-0 pointer-events-auto' : 'translate-x-full pointer-events-none'}`}>
            {settingsPanel}
          </div>
        </div>
      </div>
    )}
    </>
  );
};
