<template>
  <MainLayout>
    <div class="page-header">
      <div class="page-header-left">
        <h3>{{ chatMode === 'single' ? (agent ? agent.name : 'AI Agent 对话') : '多 Agent 编排' }}</h3>
      </div>
      <div class="page-header-actions">
        <el-switch
          v-model="chatMode"
          active-value="orchestrate"
          inactive-value="single"
          active-text="编排"
          inactive-text="单 Agent"
          style="margin-right: 12px"
        />

        <!-- 单 Agent 模式：选择 Agent -->
        <template v-if="chatMode === 'single'">
          <el-select v-model="currentAgentId" placeholder="选择 Agent" @change="switchAgent" style="width: 180px" clearable>
            <el-option v-for="a in agents" :key="a.id" :label="a.name" :value="a.id">
              <span>{{ a.name }}</span>
            </el-option>
          </el-select>
        </template>

        <!-- 编排模式：模式选择 + Agent 数 -->
        <template v-if="chatMode === 'orchestrate'">
          <el-select v-model="orchestrateMode" style="width: 130px">
            <el-option label="辩论模式" value="debate" />
            <el-option label="路由模式" value="route" />
            <el-option label="顺序模式" value="sequential" />
          </el-select>
          <el-button @click="showOrchestrateEditor = true" style="margin-left: 8px">
            配置 Agent ({{ orchestrateAgents.length }})
          </el-button>
        </template>

        <el-button @click="clearChat" :disabled="displayMessages.length === 0">清空</el-button>
      </div>
    </div>

    <div class="chat-messages" ref="messagesRef">
      <div v-if="displayMessages.length === 0" class="chat-empty">
        <el-icon :size="48"><ChatLineSquare /></el-icon>
        <p v-if="chatMode === 'single'">选择一个 Agent 开始对话</p>
        <p v-else>配置多个 Agent 后发送消息进行编排对话</p>
        <p class="hint">Agent 可以使用内置工具帮你完成任务</p>
      </div>

      <div v-for="(msg, i) in displayMessages" :key="i" class="message" :class="[msg.role, msg.status === 'error' ? 'error' : '']">
        <div class="message-avatar">
          <el-avatar :size="36" :icon="msg.role === 'user' ? UserFilled : Promotion" />
        </div>
        <div class="message-bubble">
          <!-- 编排模式：显示每个 Agent 的独立输出 -->
          <div v-if="msg.orchestrateResult" class="orchestrate-result">
            <div class="orch-header">
              <el-tag size="small" type="info">{{ msg.orchestrateResult.mode }}</el-tag>
              <span class="orch-agent-count">{{ msg.orchestrateResult.steps.length }} 个 Agent</span>
            </div>
            <div class="orch-final">
              <div class="orch-section-title">最终回答</div>
              <div class="message-text" v-html="renderMarkdown(msg.orchestrateResult.final_answer)"></div>
            </div>
            <div class="orch-steps">
              <div
                v-for="(step, si) in msg.orchestrateResult.steps"
                :key="si"
                class="orch-step"
                :class="{ expanded: step._open }"
              >
                <div class="orch-step-header" @click="step._open = !step._open">
                  <el-icon><CaretRight :style="{ transform: step._open ? 'rotate(90deg)' : '' }" /></el-icon>
                  <el-tag size="small" :type="step.error ? 'danger' : 'success'" round>
                    {{ step.agent_name }}
                  </el-tag>
                  <span class="orch-step-meta">
                    {{ step.iterations_used }} 步 · {{ step.tool_calls_made }} 次工具
                  </span>
                </div>
                <div v-show="step._open" class="orch-step-body">
                  <div class="message-text" v-html="renderMarkdown(step.output)"></div>
                </div>
              </div>
            </div>
          </div>

          <!-- 单 Agent 模式：原有内容 -->
          <template v-if="!msg.orchestrateResult">
            <div class="message-text" v-html="renderMarkdown(msg.content)"></div>
            <div v-if="msg.tool_calls && msg.tool_calls.length > 0" class="tool-calls">
              <div class="tool-calls-header">
                <el-icon><Tools /></el-icon> 工具调用 ({{ msg.tool_calls.length }})
              </div>
              <div v-for="(tc, j) in msg.tool_calls" :key="j" class="tool-call-item">
                <span class="tool-name">{{ tc.function?.name || '?' }}</span>
                <el-tag size="small" type="info">{{ Object.keys(JSON.parse(tc.function?.arguments || '{}')).length }} 个参数</el-tag>
              </div>
            </div>

            <!-- 思考链 -->
            <div v-if="msg.steps && msg.steps.length > 0" class="thinking-trace">
              <div class="trace-header" @click="toggleTrace(msg)">
                <el-icon><CaretRight :style="{ transform: msg._traceOpen ? 'rotate(90deg)' : '' }" /></el-icon>
                <span>思考链 ({{ msg.steps.length }} 步)</span>
              </div>
              <div v-show="msg._traceOpen" class="trace-steps">
                <div v-for="(step, si) in msg.steps" :key="si" class="trace-step" :class="'step-' + step.type">
                  <div class="step-icon">
                    <el-icon v-if="step.type === 'think'"><ChatDotSquare /></el-icon>
                    <el-icon v-else-if="step.type === 'tool_result'"><Tools /></el-icon>
                    <el-icon v-else><Select /></el-icon>
                  </div>
                  <div class="step-body">
                    <div class="step-header">
                      <span class="step-tag" :class="'tag-' + step.type">{{ {think:'思考',tool_result:'工具结果',final:'最终回答'}[step.type] || step.type }}</span>
                      <span class="step-iter">#{{ step.iteration }}</span>
                      <span v-if="step.tool_name" class="step-tool-name">{{ step.tool_name }}</span>
                    </div>
                    <div v-if="step.content" class="step-content" v-html="renderMarkdown(step.content)"></div>
                    <div v-if="step.reasoning" class="step-reasoning">
                      <div class="reasoning-header">推理过程</div>
                      <div class="reasoning-text">{{ step.reasoning }}</div>
                    </div>
                    <div v-if="step.tool_input && Object.keys(step.tool_input).length" class="step-tool-input">
                      <div class="reasoning-header">参数</div>
                      <pre>{{ JSON.stringify(step.tool_input, null, 2) }}</pre>
                    </div>
                    <div v-if="step.tool_result" class="step-tool-result">
                      <div class="reasoning-header">结果</div>
                      <pre>{{ step.tool_result }}</pre>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </template>

          <div class="message-meta">
            {{ msg.role === 'user' ? '用户' : 'Agent' }} · {{ relativeTime(msg.timestamp) }}
            <span v-if="msg.iterations" class="meta-iterations">· {{ msg.iterations }} 步 · {{ msg.tool_calls_made }} 次工具调用</span>
            <span class="meta-actions">
              <el-button v-if="msg.role === 'assistant'" link size="small" @click="copyMessage(msg)" title="复制">
                <el-icon><DocumentCopy /></el-icon>
              </el-button>
              <el-button v-if="msg.status === 'error'" link type="danger" size="small" @click="retryMessage(i)" title="重试">
                <el-icon><Refresh /></el-icon>
              </el-button>
            </span>
          </div>
        </div>
      </div>

      <div v-if="loading && !streamingActive" class="message assistant">
        <div class="message-avatar"><el-avatar :size="36" icon="Promotion" /></div>
        <div class="message-bubble">
          <div class="thinking"><span class="dot"></span><span class="dot"></span><span class="dot"></span></div>
        </div>
      </div>
    </div>

    <div class="chat-input">
      <el-input v-model="inputMessage" type="textarea" :rows="3" placeholder="输入你的问题..." @keydown.enter.exact.prevent="sendMessage" :disabled="loading" />
      <el-button type="primary" @click="sendMessage" :loading="loading" :disabled="!inputMessage.trim()" class="send-btn">
        {{ loading ? '处理中...' : '发送' }}
      </el-button>
    </div>

    <!-- 编排 Agent 编辑器 -->
    <el-dialog v-model="showOrchestrateEditor" title="编排 Agent 配置" width="700px" @closed="saveState">
      <div class="orch-editor">
        <div v-for="(agt, i) in orchestrateAgents" :key="i" class="orch-agent-card">
          <div class="orch-agent-header">
            <span class="orch-agent-num">#{{ i + 1 }}</span>
            <el-input v-model="agt.name" placeholder="名称" style="width: 140px" size="small" />
            <el-input v-model="agt.id" placeholder="ID" style="width: 120px" size="small" />
            <el-button size="small" type="danger" link @click="orchestrateAgents.splice(i, 1)">删除</el-button>
          </div>
          <el-input v-model="agt.system_prompt" type="textarea" :rows="3" placeholder="System Prompt" size="small" />
          <div class="orch-agent-params">
            <el-select v-model="agt.model" size="small" style="width: 160px">
              <el-option label="DeepSeek V4 Flash" value="deepseek-v4-flash" />
              <el-option label="DeepSeek V4 Pro" value="deepseek-v4-pro" />
              <el-option label="GPT-4o Mini" value="gpt-4o-mini" />
              <el-option label="GPT-4o" value="gpt-4o" />
            </el-select>
            <el-input-number v-model="agt.temperature" :min="0" :max="2" :step="0.1" size="small" style="width: 110px" />
            <el-input-number v-model="agt.max_iterations" :min="1" :max="50" size="small" style="width: 110px" />
          </div>
        </div>
        <el-button @click="addOrchestrateAgent" style="width: 100%; margin-top: 8px">
          + 添加 Agent
        </el-button>
      </div>
      <template #footer>
        <el-button @click="showOrchestrateEditor = false">关闭</el-button>
      </template>
    </el-dialog>
  </MainLayout>
</template>

<script setup lang="ts">
import { ref, computed, watch, onMounted, nextTick } from 'vue'
import { useRoute } from 'vue-router'
import { ElMessage } from 'element-plus'
import { ChatLineSquare, UserFilled, Promotion, Tools, CaretRight, ChatDotSquare, Select, DocumentCopy, Refresh } from '@element-plus/icons-vue'
import MainLayout from '@/components/MainLayout.vue'
import api from '@/api'
import type { Agent } from '@/stores/agent'

interface AgentStep {
  iteration: number; type: string; content: string
  tool_name?: string; tool_input?: Record<string, any>; tool_result?: string; reasoning?: string
}
interface OrchestrateStep {
  agent_id: string; agent_name: string; input: string; output: string
  iterations_used: number; tool_calls_made: number; error?: string; _open?: boolean
}
interface OrchestrateResult {
  mode: string; final_answer: string; steps: OrchestrateStep[]; agent_results: any[]
}
interface ChatMessage {
  role: 'user' | 'assistant'; content: string; tool_calls?: any[]; timestamp: number
  iterations?: number; tool_calls_made?: number; status?: string; steps?: AgentStep[]
  _traceOpen?: boolean; orchestrateResult?: OrchestrateResult
}

interface OrchestrateAgentForm {
  id: string; name: string; system_prompt: string; model: string
  temperature: number; max_iterations: number; description: string
}

const STORAGE_KEY = 'agent_chat_state'

interface ChatState {
  messages: Record<string, ChatMessage[]>  // keyed by agentId / '__bare__' / '__orchestrate__'
  sessionId: Record<string, string>
  currentAgentId: string
  chatMode: 'single' | 'orchestrate'
  orchestrateMode: string
  orchestrateAgents: OrchestrateAgentForm[]
}

function saveState() {
  try {
    const state: ChatState = {
      messages: messages.value,
      sessionId: sessionId.value,
      currentAgentId: currentAgentId.value,
      chatMode: chatMode.value,
      orchestrateMode: orchestrateMode.value,
      orchestrateAgents: orchestrateAgents.value,
    }
    localStorage.setItem(STORAGE_KEY, JSON.stringify(state))
  } catch { /* quota exceeded, ignore */ }
}

function loadState(): ChatState | null {
  try {
    const raw = localStorage.getItem(STORAGE_KEY)
    if (!raw) return null
    const parsed = JSON.parse(raw)
    // 兼容旧格式：旧版本 messages 是数组，迁移为 Record
    if (Array.isArray(parsed.messages)) {
      const oldSessionId = parsed.sessionId || ''
      parsed.messages = { '__bare__': parsed.messages }
      parsed.sessionId = { '__bare__': oldSessionId }
    }
    return parsed
  } catch { return null }
}

const route = useRoute()
const agents = ref<Agent[]>([])
const currentAgentId = ref('')
const messages = ref<Record<string, ChatMessage[]>>({})
const inputMessage = ref('')
const loading = ref(false)
const streamingActive = ref(false)
const messagesRef = ref<HTMLElement | null>(null)
const sessionId = ref<Record<string, string>>({})
const agent = ref<Agent | null>(null)

const currentAgentKey = computed(() => {
  if (chatMode.value === 'orchestrate') return '__orchestrate__'
  return currentAgentId.value || '__bare__'
})

const displayMessages = computed(() => {
  return messages.value[currentAgentKey.value] || []
})

// 编排模式
const chatMode = ref<'single' | 'orchestrate'>('single')
const orchestrateMode = ref('debate')
const showOrchestrateEditor = ref(false)
const orchestrateAgents = ref<OrchestrateAgentForm[]>([
  { id: 'agent-a', name: 'Agent A', system_prompt: '你是一个有用的AI助手。', model: 'deepseek-v4-flash', temperature: 0.7, max_iterations: 10, description: '' },
  { id: 'agent-b', name: 'Agent B', system_prompt: '你是一个专业的分析助手。', model: 'deepseek-v4-flash', temperature: 0.7, max_iterations: 10, description: '' },
])

function addOrchestrateAgent() {
  const n = orchestrateAgents.value.length + 1
  orchestrateAgents.value.push({
    id: `agent-${String.fromCharCode(96 + n)}`,
    name: `Agent ${String.fromCharCode(64 + n)}`,
    system_prompt: '你是一个有用的AI助手。',
    model: 'deepseek-v4-flash',
    temperature: 0.7,
    max_iterations: 10,
    description: '',
  })
  saveState()
}

// 模式/配置变化时自动保存
watch(chatMode, saveState)
watch(orchestrateMode, saveState)

onMounted(async () => {
  await loadAgents()

  const saved = loadState()
  if (saved) {
    messages.value = saved.messages
    sessionId.value = saved.sessionId
    chatMode.value = saved.chatMode
    orchestrateMode.value = saved.orchestrateMode
    orchestrateAgents.value = saved.orchestrateAgents
    // 恢复展开状态
    for (const arr of Object.values(messages.value)) {
      arr.forEach(m => { if (m.steps?.length) m._traceOpen = false })
    }
  }

  if (route.params.id) {
    currentAgentId.value = route.params.id as string
    await switchAgent()
  } else if (saved?.currentAgentId) {
    currentAgentId.value = saved.currentAgentId
    await switchAgent()
  }

  nextTick(scrollToBottom)
})

async function loadAgents() {
  try { const resp = await api.get('/api/v1/agents'); agents.value = resp.data || [] }
  catch (e) { console.error('加载 Agent 列表失败:', e) }
}

async function switchAgent() {
  if (!currentAgentId.value) { agent.value = null; saveState(); nextTick(scrollToBottom); return }
  try {
    const resp = await api.get(`/api/v1/agents/${currentAgentId.value}`)
    agent.value = resp.data
    saveState()
    nextTick(scrollToBottom)
  } catch {
    ElMessage.error('加载 Agent 失败')
    agent.value = null
  }
}

async function sendMessage() {
  const text = inputMessage.value.trim()
  if (!text || loading.value) return

  const key = currentAgentKey.value
  if (!messages.value[key]) messages.value[key] = []
  messages.value[key].push({ role: 'user', content: text, timestamp: Date.now() })
  inputMessage.value = ''
  loading.value = true
  scrollToBottom()

  try {
    if (chatMode.value === 'orchestrate') {
      const resp = await api.post('/api/v1/agent-chat/orchestrate', {
        message: text,
        mode: orchestrateMode.value,
        agents: orchestrateAgents.value.map(a => ({
          id: a.id, name: a.name, system_prompt: a.system_prompt,
          model: a.model, temperature: a.temperature, max_iterations: a.max_iterations,
          tools: [], description: a.description,
        })),
      })
      const data = resp.data as OrchestrateResult
      data.steps.forEach(s => { s._open = false })
      messages.value[key].push({
        role: 'assistant', content: data.final_answer, timestamp: Date.now(),
        orchestrateResult: data, _traceOpen: true,
      })
    } else {
      const sessId = sessionId.value[key] || ''
      const streamEndpoint = currentAgentId.value
        ? `/api/v1/agent-chat/${currentAgentId.value}/stream`
        : '/api/v1/agent-chat/bare/stream'

      // 尝试 SSE 流式（带超时控制）
      let usedStreaming = false
      streamingActive.value = false
      const abortController = new AbortController()
      const streamTimeout = setTimeout(() => abortController.abort(), 60000)
      try {
        const token = localStorage.getItem('token') || ''
        const resp = await fetch(streamEndpoint, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            ...(token ? { 'Authorization': `Bearer ${token}` } : {}),
          },
          body: JSON.stringify({ message: text, session_id: sessId || undefined }),
          signal: abortController.signal,
        })

        if (resp.ok && resp.body) {
          usedStreaming = true

          // 创建占位消息，流式更新
          const msg: ChatMessage = {
            role: 'assistant', content: '', timestamp: Date.now(),
            steps: [], _traceOpen: true, iterations: 0, tool_calls_made: 0,
          }
          const idx = messages.value[key].push(msg) - 1
          const currentMsg = messages.value[key][idx]

          const reader = resp.body.getReader()
          const decoder = new TextDecoder()
          let buffer = ''
          let receivedFirstEvent = false

          while (true) {
            const { done, value } = await reader.read()
            if (done) break

            buffer += decoder.decode(value, { stream: true })
            const parts = buffer.split('\n\n')
            buffer = parts.pop() || ''

            for (const part of parts) {
              const lines = part.split('\n')
              let eventType = ''
              let dataStr = ''
              for (const line of lines) {
                if (line.startsWith('event: ')) eventType = line.slice(7)
                else if (line.startsWith('data: ')) dataStr = line.slice(6)
              }
              if (!dataStr) continue

              try {
                const data = JSON.parse(dataStr)

                // 首个事件到达 → 隐藏 loading dots，无论什么事件类型
                if (!receivedFirstEvent) {
                  receivedFirstEvent = true
                  streamingActive.value = true
                }

                if (eventType === 'think') {
                  const thinkContent = data.content || '思考中...'
                  currentMsg.steps!.push({
                    iteration: data.iteration, type: 'think',
                    content: thinkContent,
                    reasoning: data.reasoning,
                    tool_name: data.tool_names?.[0],
                  })
                } else if (eventType === 'tool_call') {
                  currentMsg.steps!.push({
                    iteration: data.iteration, type: 'tool_call',
                    content: `调用工具: ${data.name}`,
                    tool_name: data.name,
                    tool_input: data.input,
                  })
                } else if (eventType === 'tool_result') {
                  currentMsg.steps!.push({
                    iteration: data.iteration, type: 'tool_result',
                    content: `工具 ${data.name} 返回结果`,
                    tool_name: data.name,
                    tool_result: data.result,
                  })
                } else if (eventType === 'final') {
                  currentMsg.content = data.content || ''
                  currentMsg.iterations = data.iterations_used || 0
                  currentMsg.tool_calls_made = data.tool_calls_made || 0
                  if (data.session_id) {
                    sessionId.value[key] = data.session_id
                  }
                  streamingActive.value = false
                } else if (eventType === 'error') {
                  currentMsg.content = data.content || ''
                  currentMsg.status = 'error'
                  streamingActive.value = false
                }
              } catch { /* 跳过畸形事件 */ }
            }
          }
        }
      } catch {
        clearTimeout(streamTimeout)
        // 流式失败时标记为非流式，让 fallback POST 兜底
        usedStreaming = false
        streamingActive.value = false
      }

      if (!usedStreaming) {
        // 降级：标准 POST 请求
        const fallbackEndpoint = currentAgentId.value
          ? `/api/v1/agent-chat/${currentAgentId.value}`
          : '/api/v1/agent-chat/bare'
        const resp = await api.post(fallbackEndpoint, { message: text, session_id: sessId || undefined })
        const data = resp.data
        sessionId.value[key] = data.session_id
        messages.value[key].push({
          role: 'assistant', content: data.content, timestamp: Date.now(),
          iterations: data.iterations_used, tool_calls_made: data.tool_calls_made,
          status: data.truncated ? 'error' : 'success', steps: data.steps || [],
          _traceOpen: data.steps && data.steps.length > 0,
        })
      }
    }
    saveState()
  } catch (e: any) {
    messages.value[key].push({
      role: 'assistant', content: `错误：${e.response?.data?.detail || e.message || '请求失败'}`,
      timestamp: Date.now(), status: 'error',
    })
    saveState()
  } finally {
    loading.value = false; scrollToBottom()
  }
}

function toggleTrace(msg: ChatMessage) { msg._traceOpen = !msg._traceOpen }
function clearChat() {
  const key = currentAgentKey.value
  messages.value[key] = []
  if (chatMode.value === 'single') {
    sessionId.value[key] = ''
  }
  saveState()
}
function scrollToBottom() { nextTick(() => { if (messagesRef.value) messagesRef.value.scrollTop = messagesRef.value.scrollHeight }) }

function relativeTime(ts: number): string {
  const diff = Date.now() - ts
  if (diff < 60000) return '刚刚'
  if (diff < 3600000) return `${Math.floor(diff / 60000)} 分钟前`
  if (diff < 86400000) return `${Math.floor(diff / 3600000)} 小时前`
  const d = new Date(ts)
  return `${d.getMonth() + 1}月${d.getDate()}日 ${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`
}

function copyMessage(msg: ChatMessage) {
  const text = msg.orchestrateResult?.final_answer || msg.content
  if (!text) return
  navigator.clipboard.writeText(text).then(() => {
    ElMessage.success('已复制')
  }).catch(() => {
    ElMessage.warning('复制失败')
  })
}

function retryMessage(idx: number) {
  const key = currentAgentKey.value
  const msgs = messages.value[key]
  if (!msgs) return

  // 查找错误消息之前的最后一条用户消息
  let userMsg = ''
  for (let i = idx - 1; i >= 0; i--) {
    if (msgs[i].role === 'user') {
      userMsg = msgs[i].content
      break
    }
  }
  if (!userMsg) {
    ElMessage.warning('未找到可重试的消息')
    return
  }

  // 移除该错误消息及关联的用户消息
  const removeIndices: number[] = []
  for (let i = idx - 1; i >= 0; i--) {
    if (msgs[i].role === 'user' && msgs[i].content === userMsg) {
      removeIndices.push(i)
      break
    }
  }
  removeIndices.push(idx)

  // 从后往前删除，避免 index 错乱
  removeIndices.sort((a, b) => b - a)
  for (const ri of removeIndices) {
    msgs.splice(ri, 1)
  }

  saveState()
  // 填入输入框并发送
  inputMessage.value = userMsg
  nextTick(() => sendMessage())
}

function renderMarkdown(text: string): string {
  if (!text) return ''
  return text.replace(/</g, '&lt;').replace(/>/g, '&gt;')
    .replace(/```(\w*)\n([\s\S]*?)```/g, '<pre><code class="language-$1">$2</code></pre>')
    .replace(/`([^`]+)`/g, '<code>$1</code>')
    .replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
    .replace(/\n/g, '<br>')
}
</script>

<style scoped>
.agent-chat-page { display: flex; flex-direction: column; height: 100%; max-width: 960px; margin: 0 auto; }
.page-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid var(--el-border-color-light); }
.page-header-left { display: flex; align-items: center; gap: 8px; }
.page-header-left h3 { margin: 0; font-size: 16px; font-weight: 600; }
.page-header-actions { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.chat-messages { flex: 1; overflow-y: auto; padding: 8px 0; display: flex; flex-direction: column; gap: 12px; min-height: 0; }
.chat-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; color: var(--el-text-color-secondary); gap: 12px; }
.chat-empty .hint { font-size: 13px; color: var(--el-text-color-placeholder); }
.message { display: flex; gap: 12px; max-width: 88%; }
.message.user { align-self: flex-end; flex-direction: row-reverse; }
.message.assistant { align-self: flex-start; }
.message-bubble { padding: 10px 14px; border-radius: 12px; background: var(--el-fill-color-light); line-height: 1.6; font-size: 14px; }
.message.user .message-bubble { background: var(--el-color-primary-light-8); }
.message.error .message-bubble { border: 1px solid var(--el-color-danger-light-5); }
.message-text :deep(pre) { background: var(--el-fill-color); padding: 12px; border-radius: 8px; overflow-x: auto; font-size: 13px; }
.message-text :deep(code) { background: var(--el-fill-color); padding: 2px 6px; border-radius: 4px; font-size: 13px; }

/* Tool calls */
.tool-calls { margin-top: 8px; padding-top: 8px; border-top: 1px dashed var(--el-border-color-light); }
.tool-calls-header { display: flex; align-items: center; gap: 4px; font-size: 12px; color: var(--el-text-color-secondary); margin-bottom: 4px; }
.tool-call-item { display: flex; align-items: center; gap: 8px; padding: 4px 8px; font-size: 12px; }
.tool-name { font-weight: 500; color: var(--el-color-primary); }
.message-meta { font-size: 11px; color: var(--el-text-color-placeholder); margin-top: 4px; display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.meta-iterations { color: var(--el-color-info); }
.meta-actions { margin-left: auto; display: flex; gap: 2px; opacity: 0; transition: opacity 0.15s; }
.message-bubble:hover .meta-actions { opacity: 1; }

/* Thinking trace */
.thinking-trace { margin-top: 10px; border-top: 1px solid var(--el-border-color-light); padding-top: 8px; }
.trace-header { display: flex; align-items: center; gap: 4px; cursor: pointer; font-size: 12px; color: var(--el-color-primary); user-select: none; padding: 4px 0; }
.trace-steps { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.trace-step { display: flex; gap: 8px; padding: 8px 10px; border-radius: 8px; background: var(--el-fill-color-lighter); border-left: 3px solid var(--el-border-color); }
.trace-step.step-think { border-left-color: var(--el-color-primary); }
.trace-step.step-tool_result { border-left-color: var(--el-color-warning); }
.trace-step.step-final { border-left-color: var(--el-color-success); }
.step-icon { flex-shrink: 0; width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; font-size: 14px; color: var(--el-text-color-secondary); }
.step-body { flex: 1; min-width: 0; font-size: 13px; }
.step-header { display: flex; align-items: center; gap: 6px; margin-bottom: 4px; }
.step-tag { font-size: 11px; padding: 1px 6px; border-radius: 4px; font-weight: 500; }
.tag-think { background: var(--el-color-primary-light-9); color: var(--el-color-primary); }
.tag-tool_result { background: var(--el-color-warning-light-9); color: var(--el-color-warning); }
.tag-final { background: var(--el-color-success-light-9); color: var(--el-color-success); }
.step-iter { font-size: 11px; color: var(--el-text-color-placeholder); }
.step-tool-name { font-size: 11px; background: var(--el-color-info-light-9); color: var(--el-color-info); padding: 0 6px; border-radius: 4px; font-family: monospace; }
.step-content { line-height: 1.5; }
.step-content :deep(pre) { background: var(--el-fill-color); padding: 8px; border-radius: 6px; overflow-x: auto; font-size: 12px; margin: 4px 0; }
.step-reasoning, .step-tool-input, .step-tool-result { margin-top: 6px; }
.reasoning-header { font-size: 11px; color: var(--el-text-color-secondary); margin-bottom: 2px; font-weight: 500; }
.reasoning-text { font-size: 12px; color: var(--el-text-color-secondary); line-height: 1.5; font-style: italic; }
.step-tool-input pre, .step-tool-result pre { background: var(--el-fill-color-darker); padding: 6px 8px; border-radius: 4px; font-size: 11px; overflow-x: auto; max-height: 200px; margin: 0; }

/* Thinking dots */
.thinking { display: flex; gap: 4px; padding: 8px 0; }
.dot { width: 8px; height: 8px; background: var(--el-text-color-placeholder); border-radius: 50%; animation: bounce 1.4s infinite ease-in-out; }
.dot:nth-child(2) { animation-delay: 0.16s; }
.dot:nth-child(3) { animation-delay: 0.32s; }
@keyframes bounce { 0%,80%,100% { transform: scale(0); } 40% { transform: scale(1); } }

/* Chat input */
.chat-input { display: flex; gap: 12px; padding-top: 12px; border-top: 1px solid var(--el-border-color-light); }
.chat-input .el-textarea { flex: 1; }
.send-btn { align-self: flex-end; min-width: 100px; }

/* Orchestrate result */
.orchestrate-result { font-size: 14px; }
.orch-header { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.orch-agent-count { font-size: 12px; color: var(--el-text-color-secondary); }
.orch-final { margin-bottom: 12px; }
.orch-section-title { font-size: 13px; font-weight: 600; color: var(--el-text-color-primary); margin-bottom: 6px; padding-bottom: 4px; border-bottom: 1px solid var(--el-border-color-light); }
.orch-steps { display: flex; flex-direction: column; gap: 4px; }
.orch-step { border: 1px solid var(--el-border-color-lighter); border-radius: 8px; overflow: hidden; }
.orch-step-header { display: flex; align-items: center; gap: 6px; padding: 6px 10px; cursor: pointer; background: var(--el-fill-color-lighter); font-size: 13px; }
.orch-step-header:hover { background: var(--el-fill-color-light); }
.orch-step-meta { font-size: 11px; color: var(--el-text-color-placeholder); margin-left: auto; }
.orch-step-body { padding: 8px 12px; font-size: 13px; background: var(--el-bg-color); }

/* Orchestrate editor */
.orch-editor { display: flex; flex-direction: column; gap: 12px; max-height: 500px; overflow-y: auto; }
.orch-agent-card { border: 1px solid var(--el-border-color-light); border-radius: 8px; padding: 12px; }
.orch-agent-header { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.orch-agent-num { font-weight: 600; color: var(--el-color-primary); font-size: 14px; }
.orch-agent-params { display: flex; gap: 8px; margin-top: 8px; flex-wrap: wrap; }
</style>
