import { useEffect, useRef, useState, useCallback } from 'react'
import ReactMarkdown from 'react-markdown'
import remarkGfm from 'remark-gfm'
import rehypeRaw from 'rehype-raw'
import {
  Hash,
  Crown,
  Send,
  Square,
  AtSign,
  Copy,
  Check,
  FileText,
  ExternalLink,
  X,
} from 'lucide-react'
import { useStore } from '../store'
import type { Message, TodoItem } from '../types'

function formatTokens(n: number): string {
  if (n >= 1000000) return (n / 1000000).toFixed(1) + 'M'
  if (n >= 1000) return (n / 1000).toFixed(1) + 'K'
  return n.toString()
}

function formatTime(ts: string): string {
  try {
    const d = new Date(ts.includes('T') ? ts : ts + 'Z')
    return d.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false })
  } catch {
    return ''
  }
}

export function ChatView() {
  const { activeRoomId, rooms, messages, sendMessage, user, tokenUsage, todoItems, workingFiles, workStartedAt } = useStore()
  const [input, setInput] = useState('')
  const [mentionQuery, setMentionQuery] = useState<string | null>(null)
  const [mentionIndex, setMentionIndex] = useState(0)
  const inputRef = useRef<HTMLTextAreaElement>(null)
  const bottomRef = useRef<HTMLDivElement>(null)

  const room = rooms.find(r => r.id === activeRoomId)
  const msgs: Message[] = activeRoomId ? (messages[activeRoomId] || []) : []
  const mode = room?.mode || 'plan'

  // Build agent list for @ mentions
  const mentionCandidates = room
    ? [room.master, ...(room.members || [])].filter(Boolean)
    : []

  const filteredMentions = mentionQuery !== null
    ? mentionCandidates.filter(name =>
        name.toLowerCase().includes(mentionQuery.toLowerCase())
      )
    : []

  // Detect @ trigger from input
  const updateMention = useCallback((value: string, cursorPos: number) => {
    const before = value.slice(0, cursorPos)
    const atMatch = before.match(/@(\w*)$/)
    if (atMatch) {
      setMentionQuery(atMatch[1])
      setMentionIndex(0)
    } else {
      setMentionQuery(null)
    }
  }, [])

  const insertMention = useCallback((name: string) => {
    const el = inputRef.current
    if (!el) return
    const cursorPos = el.selectionStart || input.length
    const before = input.slice(0, cursorPos)
    const after = input.slice(cursorPos)
    const atIdx = before.lastIndexOf('@')
    const newInput = before.slice(0, atIdx) + `@${name} ` + after
    setInput(newInput)
    setMentionQuery(null)
    setTimeout(() => {
      const pos = atIdx + name.length + 2
      el.setSelectionRange(pos, pos)
      el.focus()
    })
  }, [input])

  const handleStop = useCallback(() => {
    if (!activeRoomId) return
    const { ws } = useStore.getState()
    const socket = ws[activeRoomId]
    if (socket) {
      socket.send(JSON.stringify({ type: 'stop' }))
    }
  }, [activeRoomId])

  const toggleMode = useCallback(() => {
    if (!activeRoomId) return
    const newMode = mode === 'build' ? 'plan' : 'build'
    // 发送到后端
    const { ws } = useStore.getState()
    const socket = ws[activeRoomId]
    if (socket) {
      socket.send(JSON.stringify({ type: 'set_mode', mode: newMode }))
    }
    // 本地立即更新
    useStore.setState(s => ({
      rooms: s.rooms.map(r => r.id === activeRoomId ? { ...r, mode: newMode } : r)
    }))
  }, [activeRoomId, mode])

  // 智能滚动：用户手动上滚时不强制拉回底部
  const scrollContainerRef = useRef<HTMLDivElement>(null)
  const isNearBottomRef = useRef(true)

  const handleScroll = useCallback(() => {
    const el = scrollContainerRef.current
    if (!el) return
    // 距底部 150px 以内视为"在底部"
    isNearBottomRef.current = el.scrollHeight - el.scrollTop - el.clientHeight < 150
  }, [])

  useEffect(() => {
    if (isNearBottomRef.current) {
      bottomRef.current?.scrollIntoView({ behavior: 'smooth' })
    }
  }, [msgs, room?.status])

  if (!room) {
    return (
      <div className="flex-1 flex flex-col items-center justify-center bg-[var(--bg-primary)] text-[var(--text-muted)]">
        <Hash className="w-16 h-16 mb-4 opacity-20" />
        <p className="text-lg">选择一个项目开始聊天</p>
      </div>
    )
  }

  const statusLabel = room.status === 'working' && room.activeAgent
    ? `${room.activeAgent} ${room.action || ''}`
    : room.status === 'thinking' ? '思考中...' : room.status === 'working' ? '工作中...' : '空闲'

  return (
    <div className="flex flex-1 overflow-hidden bg-[var(--bg-primary)]">
      {/* Main chat area */}
      <div className="flex flex-col flex-1 min-w-0">
        {/* Header */}
        <div className="h-12 px-4 border-b border-[var(--border)] flex items-center gap-2 bg-[var(--bg-secondary)] shadow-sm">
          <Hash className="w-5 h-5 text-[var(--text-muted)]" />
          <span className="font-semibold text-base">{room.name}</span>
          <div className="h-6 w-[1px] bg-[var(--border)] mx-2" />
          <span className="text-sm text-[var(--text-muted)] truncate">
            {room.team || '项目群'}
          </span>
          <div className="flex-1" />
          {/* Status badge */}
          <span
            className={`text-xs px-2 py-1 rounded-full font-medium flex items-center gap-1.5 ${
              room.status === 'pending' ? 'bg-[var(--bg-tertiary)] text-[var(--text-muted)]' :
              room.status === 'thinking' ? 'bg-[var(--color-warning)]/20 text-[var(--color-warning)]' :
              'bg-[var(--color-success)]/20 text-[var(--color-success)]'
            }`}
          >
            <span className={`w-1.5 h-1.5 rounded-full ${
              room.status === 'pending' ? 'bg-[var(--text-muted)]' :
              room.status === 'thinking' ? 'bg-[var(--color-warning)] animate-pulse' :
              'bg-[var(--color-success)] animate-pulse'
            }`} />
            {statusLabel}
          </span>
        </div>

        {/* Messages */}
        <div ref={scrollContainerRef} onScroll={handleScroll} className="flex-1 overflow-y-auto p-4 space-y-4 scrollbar-thin">
          {msgs.length === 0 && (
            <div className="flex flex-col items-center justify-center h-full text-[var(--text-muted)]">
              <Hash className="w-12 h-12 mb-2 opacity-30" />
              <p className="text-sm">这里是 {room.name} 的开始</p>
              <p className="text-xs mt-1">发送消息开始对话</p>
            </div>
          )}
          {renderGroupedMessages(msgs, activeRoomId!, user?.name)}
          {/* Thinking indicator */}
          {room.status === 'thinking' && msgs[msgs.length - 1]?.role === 'user' && (
            <ThinkingBubble agent={room.master} />
          )}
          {/* Working indicator 已移至 AgentStatusBar，不在消息列表重复显示 */}
          <div ref={bottomRef} />
        </div>

        {/* Agent status bar + Input area */}
        <div className="px-4 pb-4">
          {/* 状态条 */}
          <AgentStatusBar
            todos={activeRoomId ? (todoItems[activeRoomId] || []) : []}
            room={room ? { status: room.status, activeAgent: room.activeAgent, action: room.action, master: room.master } : undefined}
            workingFiles={activeRoomId ? (workingFiles[activeRoomId] || []) : []}
            startedAt={activeRoomId ? workStartedAt[activeRoomId] : undefined}
          />
          <div className="relative">
            {/* @ Mention dropdown */}
            {mentionQuery !== null && filteredMentions.length > 0 && (
              <div className="absolute bottom-full left-0 right-0 mb-1 bg-[var(--bg-secondary)] border border-[var(--border)] rounded-lg shadow-lg overflow-hidden z-10">
                <div className="px-3 py-1.5 text-[10px] font-semibold text-[var(--text-muted)] uppercase">
                  成员
                </div>
                {filteredMentions.map((name, i) => {
                  const isMaster = name === room.master
                  return (
                    <button
                      key={name}
                      className={`w-full flex items-center gap-2 px-3 py-1.5 text-sm transition-colors ${
                        i === mentionIndex
                          ? 'bg-[var(--accent)] text-white'
                          : 'hover:bg-[var(--bg-hover)]'
                      }`}
                      onMouseEnter={() => setMentionIndex(i)}
                      onMouseDown={e => {
                        e.preventDefault()
                        insertMention(name)
                      }}
                    >
                      <div className={`w-6 h-6 rounded-full flex items-center justify-center text-white text-xs font-semibold flex-shrink-0 ${
                        isMaster ? 'bg-[var(--color-warning)]' : 'bg-[var(--accent)]'
                      }`}>
                        {isMaster ? <Crown className="w-3 h-3" /> : name[0]?.toUpperCase()}
                      </div>
                      <span className="truncate">{name}</span>
                      {isMaster && (
                        <span className={`text-[10px] px-1 py-0.5 rounded font-medium ml-auto ${
                          i === mentionIndex
                            ? 'bg-white/20 text-white'
                            : 'bg-[var(--color-warning)]/20 text-[var(--color-warning)]'
                        }`}>
                          MASTER
                        </span>
                      )}
                    </button>
                  )
                })}
              </div>
            )}

            {/* Input bar with mode toggle */}
            <div className="bg-[var(--bg-tertiary)] rounded-lg flex items-center gap-0 px-2 py-1.5">
              {/* Mode toggle - OpenCode style */}
              <button
                onClick={toggleMode}
                className={`
                  flex items-center gap-1 px-3 py-1.5 rounded-md text-xs font-bold tracking-wider transition-all duration-200 select-none flex-shrink-0
                  ${mode === 'build'
                    ? 'bg-orange-500 text-white shadow-sm shadow-orange-500/30'
                    : 'bg-blue-500/15 text-blue-400 hover:bg-blue-500/25'
                  }
                `}
                title="Tab 切换模式"
              >
                {mode === 'build' ? 'BUILD' : 'PLAN'}
              </button>
              <div className="h-5 w-[1px] bg-[var(--border)] mx-2 flex-shrink-0" />
              <textarea
                ref={inputRef}
                rows={1}
                className="flex-1 bg-transparent outline-none text-sm placeholder:text-[var(--text-muted)] min-w-0 resize-none max-h-[120px] leading-5 py-0.5"
                placeholder={mode === 'build' ? '发送确认开始执行任务...' : `#${room.name} 中发送消息...`}
                value={input}
                onChange={e => {
                  setInput(e.target.value)
                  // 自动调整高度
                  const el = e.target
                  el.style.height = 'auto'
                  el.style.height = Math.min(el.scrollHeight, 120) + 'px'
                  updateMention(e.target.value, e.target.selectionStart || 0)
                }}
                onKeyDown={e => {
                  // 输入法正在组合（选字）时，不拦截任何按键
                  if (e.nativeEvent.isComposing) return

                  // 提及下拉菜单键盘控制
                  if (mentionQuery !== null && filteredMentions.length > 0) {
                    if (e.key === 'ArrowUp') {
                      e.preventDefault()
                      setMentionIndex(i => (i - 1 + filteredMentions.length) % filteredMentions.length)
                      return
                    }
                    if (e.key === 'ArrowDown') {
                      e.preventDefault()
                      setMentionIndex(i => (i + 1) % filteredMentions.length)
                      return
                    }
                    if (e.key === 'Enter' || e.key === 'Tab') {
                      e.preventDefault()
                      insertMention(filteredMentions[mentionIndex])
                      return
                    }
                    if (e.key === 'Escape') {
                      e.preventDefault()
                      setMentionQuery(null)
                      return
                    }
                  }
                  // Tab 切换模式（无提及下拉时）
                  if (e.key === 'Tab') {
                    e.preventDefault()
                    toggleMode()
                    return
                  }
                  // Shift+Enter 换行（默认行为，不拦截）
                  // Enter 发送消息
                  if (e.key === 'Enter' && !e.shiftKey) {
                    e.preventDefault()
                    if (input.trim()) {
                      sendMessage(room.id, input.trim())
                      setInput('')
                      setMentionQuery(null)
                      // 重置高度
                      const el = inputRef.current
                      if (el) el.style.height = 'auto'
                    }
                  }
                }}
              />
              <button
                className="text-[var(--text-muted)] hover:text-[var(--text-primary)] transition-colors p-1.5"
                title="@ 提及"
                onClick={() => {
                  setInput(prev => prev + '@')
                  setMentionQuery('')
                  setMentionIndex(0)
                  inputRef.current?.focus()
                }}
              >
                <AtSign className="w-5 h-5" />
              </button>
              {room.status !== 'pending' && (
                <button
                  className="text-red-500 hover:text-red-400 transition-colors p-1.5"
                  onClick={handleStop}
                  title="停止"
                >
                  <Square className="w-4 h-4 fill-current" />
                </button>
              )}
              <button
                className="text-[var(--accent)] hover:text-[var(--accent-hover)] transition-colors disabled:opacity-50 p-1.5"
                disabled={!input.trim()}
                onClick={() => {
                  if (input.trim()) {
                    sendMessage(room.id, input.trim())
                    setInput('')
                    setMentionQuery(null)
                  }
                }}
              >
                <Send className="w-5 h-5" />
              </button>
            </div>
            {/* Tab hint + token usage */}
            <div className="flex items-center gap-2 mt-1 px-1">
              <span className="text-[10px] text-[var(--text-muted)] opacity-60">
                <kbd className="px-1 py-0.5 bg-[var(--bg-tertiary)] rounded text-[9px] border border-[var(--border)]">Tab</kbd> 切换模式
              </span>
              {activeRoomId && tokenUsage[activeRoomId] && (
                <>
                  <span className="text-[10px] text-[var(--text-muted)] opacity-40">|</span>
                  <span className="text-[10px] text-[var(--text-muted)] opacity-60 flex items-center gap-1.5">
                    <span title="输入 tokens">
                      <span className="opacity-70">In</span> {formatTokens(tokenUsage[activeRoomId].promptTokens)}
                    </span>
                    <span title="输出 tokens">
                      <span className="opacity-70">Out</span> {formatTokens(tokenUsage[activeRoomId].completionTokens)}
                    </span>
                    <span title="总计 tokens" className="font-medium">
                      = {formatTokens(tokenUsage[activeRoomId].totalTokens)}
                    </span>
                  </span>
                </>
              )}
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}

/* ---- 消息渲染逻辑（按时间顺序） ---- */
function renderGroupedMessages(msgs: Message[], _roomId: string, userName?: string) {
  const elements: React.ReactNode[] = []

  for (const msg of msgs) {
    // 只显示 user / master / member 角色的 text 消息
    // task_assign、tool_use、artifact、challenge 都不在消息列表展示
    if (msg.role === 'task_assign' || msg.role === 'tool_use' || msg.role === 'artifact' || msg.role === 'challenge') continue
    // document part 只在产物面板展示，不在聊天列表
    if (msg.part_type === 'document') continue

    let content = msg.content
    content = stripAssignmentLines(content)
    // 兼容旧数据：如果没有 part_type 标记，master 消息仍用老逻辑剥离文档内容
    if (!msg.part_type && msg.role === 'master') {
      content = stripDocumentContent(content)
    }

    if (content.trim()) {
      elements.push(<MessageBubble key={String(msg.id)} msg={{...msg, content}} userName={userName} />)
    }
  }

  return elements
}

function highlightMentions(content: string): string {
  if (!content) return ''
  return content.replace(/@([\w\u4e00-\u9fff]+)/g, '`@$1`')
}

/** 修复流式输出中未闭合的 markdown 标记 */
function fixStreamingMarkdown(text: string): string {
  // 修复未闭合的粗体 **
  const boldCount = (text.match(/\*\*/g) || []).length
  if (boldCount % 2 !== 0) text += '**'
  // 修复未闭合的斜体 *（排除已闭合的 **）
  const cleanedForItalic = text.replace(/\*\*/g, '')
  const italicCount = (cleanedForItalic.match(/\*/g) || []).length
  if (italicCount % 2 !== 0) text += '*'
  // 修复未闭合的行内代码 `
  const backtickCount = (text.match(/`/g) || []).length
  if (backtickCount % 2 !== 0) text += '`'
  return text
}

/** 移除 master 消息中的文档内容（已作为 artifact 展示） */
function stripDocumentContent(content: string): string {
  const idx = content.indexOf('\n# ')
  if (idx >= 0) return content.slice(0, idx).trim()
  if (content.startsWith('# ')) return ''
  return content
}

/** 过滤 master 消息中的 @成员名 任务分配行（已由 task_assign 卡片展示） */
function stripAssignmentLines(content: string): string {
  if (!content) return ''
  const lines = content.split('\n')
  const filtered = lines.filter(line => !line.trim().match(/^@[\w\u4e00-\u9fff]+\s/))
  const result = filtered.join('\n').trim()
  return result || content // 如果全部被过滤掉，保留原文
}

/* ---- Agent Status Bar (输入框上方) ---- */
function MarqueeText({ text, className }: { text: string; className?: string }) {
  const containerRef = useRef<HTMLDivElement>(null)
  const textRef = useRef<HTMLSpanElement>(null)
  const [needsMarquee, setNeedsMarquee] = useState(false)

  useEffect(() => {
    const container = containerRef.current
    const textEl = textRef.current
    if (!container || !textEl) return
    setNeedsMarquee(textEl.scrollWidth > container.clientWidth)
  }, [text])

  return (
    <div ref={containerRef} className={`overflow-hidden ${className || ''}`}>
      {needsMarquee ? (
        <div className="flex gap-8 animate-marquee w-max">
          <span ref={textRef} className="whitespace-nowrap">{text}</span>
          <span className="whitespace-nowrap">{text}</span>
        </div>
      ) : (
        <span ref={textRef} className="whitespace-nowrap">{text}</span>
      )}
    </div>
  )
}

function ElapsedTimer({ startedAt }: { startedAt: number }) {
  const [, setTick] = useState(0)
  useEffect(() => {
    const id = setInterval(() => setTick(t => t + 1), 1000)
    return () => clearInterval(id)
  }, [])
  const elapsed = Math.floor((Date.now() - startedAt) / 1000)
  const m = Math.floor(elapsed / 60)
  const s = elapsed % 60
  const text = m > 0 ? `${m}m${s.toString().padStart(2, '0')}s` : `${s}s`
  return <span className="text-xs opacity-50 tabular-nums">{text}</span>
}

function AgentStatusBar({ todos, room, workingFiles = [], startedAt }: { todos: TodoItem[]; room?: { status: string; activeAgent?: string; action?: string; master: string }; workingFiles?: { agent: string; filename: string; title?: string }[]; startedAt?: number }) {
  // 只在 master 本人是活跃 agent 或正在思考时显示 master 状态
  const showMaster = room && (
    room.status === 'thinking' ||
    (room.status === 'working' && (!room.activeAgent || room.activeAgent === room.master))
  )
  if (todos.length === 0 && !showMaster && workingFiles.length === 0) return null

  return (
    <div className="flex items-center gap-2 mb-2 px-1 py-1.5 overflow-hidden flex-wrap">
      {startedAt && <ElapsedTimer startedAt={startedAt} />}
      {/* 文件生成状态 */}
      {workingFiles.map(f => (
        <div
          key={`file-${f.agent}-${f.filename}`}
          className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium bg-purple-500/10 text-purple-400 ring-1 ring-purple-500/30"
        >
          <span className="relative flex w-2.5 h-2.5 shrink-0">
            <span className="absolute inset-0 rounded-full bg-purple-500 animate-ping opacity-75" />
            <span className="relative rounded-full w-2.5 h-2.5 bg-purple-500" />
          </span>
          <FileText className="w-3 h-3 shrink-0" />
          <span className="font-semibold shrink-0">{f.agent}</span>
          <span className="opacity-70">正在生成</span>
          <span className="font-semibold">{f.title || f.filename}</span>
        </div>
      ))}
      {showMaster && room && (
        <div className="flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium whitespace-nowrap bg-[var(--color-warning)]/10 text-[var(--color-warning)] ring-1 ring-[var(--color-warning)]/30 min-w-0 max-w-[50%] shrink">
          <span className="relative flex w-2.5 h-2.5 shrink-0">
            <span className="absolute inset-0 rounded-full bg-[var(--color-warning)] animate-ping opacity-75" />
            <span className="relative rounded-full w-2.5 h-2.5 bg-[var(--color-warning)]" />
          </span>
          <span className="font-semibold shrink-0">{room.master}</span>
          <MarqueeText text={room.action || (room.status === 'thinking' ? '思考中...' : '工作中...')} className="opacity-70 font-normal min-w-0" />
        </div>
      )}
      {todos.map(todo => {
        const isDone = todo.status === 'done'
        const isWorking = todo.status === 'working'
        const actionLabel = isDone
          ? '已完成'
          : isWorking
            ? (todo.action || '执行中...')
            : '等待中'
        return (
          <div
            key={todo.id}
            className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium whitespace-nowrap transition-all min-w-0 max-w-[50%] shrink ${
              isDone
                ? 'bg-green-500/10 text-green-600 dark:text-green-400'
                : isWorking
                  ? 'bg-[var(--accent)]/10 text-[var(--accent)] ring-1 ring-[var(--accent)]/30'
                  : 'bg-[var(--bg-tertiary)] text-[var(--text-muted)]'
            }`}
          >
            {isDone ? (
              <svg className="w-3.5 h-3.5 shrink-0" viewBox="0 0 16 16" fill="none">
                <path d="M13.25 4.75L6 12 2.75 8.75" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
              </svg>
            ) : isWorking ? (
              <span className="relative flex w-2.5 h-2.5 shrink-0">
                <span className="absolute inset-0 rounded-full bg-green-500 animate-ping opacity-75" />
                <span className="relative rounded-full w-2.5 h-2.5 bg-green-500" />
              </span>
            ) : (
              <span className="w-2 h-2 rounded-full bg-current animate-pulse shrink-0" />
            )}
            <span className="font-semibold shrink-0">{todo.agent}</span>
            <MarqueeText text={actionLabel} className="opacity-70 font-normal min-w-0" />
          </div>
        )
      })}
    </div>
  )
}

/* ---- Artifact Card (暂时保留，part_type 模型下不再使用) ---- */
// @ts-ignore unused
function _ArtifactCard({ msg, roomId }: { msg: Message; roomId: string }) {
  const [copied, setCopied] = useState(false)
  const [showPreview, setShowPreview] = useState(false)
  const [docContent, setDocContent] = useState('')
  const [loading, setLoading] = useState(false)
  const { fileReaders, markFileRead } = useStore()
  const readers = fileReaders[roomId]?.[msg.filename || ''] || []

  useEffect(() => {
    if (!showPreview) return
    const handler = (e: KeyboardEvent) => {
      if (e.key === 'Escape') setShowPreview(false)
    }
    window.addEventListener('keydown', handler)
    return () => window.removeEventListener('keydown', handler)
  }, [showPreview])

  const avatarColors = [
    'bg-red-500', 'bg-orange-500', 'bg-amber-500', 'bg-yellow-500',
    'bg-lime-500', 'bg-green-500', 'bg-emerald-500', 'bg-teal-500',
    'bg-cyan-500', 'bg-sky-500', 'bg-blue-500', 'bg-indigo-500',
    'bg-violet-500', 'bg-purple-500', 'bg-fuchsia-500', 'bg-pink-500', 'bg-rose-500'
  ]
  const agentName = msg.agent || 'Unknown'
  const colorIndex = agentName.charCodeAt(0) % avatarColors.length

  const openPreview = async () => {
    if (!msg.filename) return
    setLoading(true)
    try {
      const d = await fetch(`/api/rooms/${roomId}/workspace/${msg.filename}`).then(r => r.json())
      setDocContent(d.content || '')
      setShowPreview(true)
      markFileRead(roomId, msg.filename!)
    } finally {
      setLoading(false)
    }
  }

  const copyContent = () => {
    navigator.clipboard.writeText(docContent)
    setCopied(true)
    setTimeout(() => setCopied(false), 2000)
  }

  return (
    <>
      <div className="flex gap-3">
        <div className={`w-10 h-10 rounded-full flex items-center justify-center text-white font-semibold flex-shrink-0 mt-0.5 ${avatarColors[colorIndex]}`}>
          {agentName[0]?.toUpperCase()}
        </div>
        <div className="flex flex-col items-start max-w-[70%]">
          <div className="flex items-center gap-2 mb-0.5">
            <span className="font-medium text-sm text-[var(--text-primary)]">{agentName}</span>
            <span className="text-[10px] px-1.5 py-0.5 bg-emerald-500/20 text-emerald-400 rounded font-medium">
              已完成
            </span>
          </div>
          <button
            onClick={openPreview}
            disabled={loading}
            className="w-full bg-[var(--bg-tertiary)] border border-[var(--border)] rounded-lg p-3 hover:border-[var(--accent)] transition-colors text-left group"
          >
            <div className="flex items-start gap-3">
              <div className="w-10 h-10 rounded-lg bg-[var(--accent)]/10 flex items-center justify-center flex-shrink-0">
                <FileText className="w-5 h-5 text-[var(--accent)]" />
              </div>
              <div className="flex-1 min-w-0">
                <div className="font-medium text-sm text-[var(--text-primary)] truncate">
                  {msg.title || msg.filename}
                </div>
                <div className="text-xs text-[var(--text-muted)] mt-0.5">{msg.filename}</div>
              </div>
              <ExternalLink className="w-4 h-4 text-[var(--text-muted)] opacity-0 group-hover:opacity-100 transition-opacity flex-shrink-0 mt-1" />
            </div>
          </button>
          <div className="flex items-center gap-1 mt-0.5">
            <button
              onClick={openPreview}
              className="flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] text-[var(--text-muted)] hover:text-[var(--accent)] hover:bg-[var(--bg-hover)] transition-colors"
            >
              <FileText className="w-3 h-3" />
              查看文档
            </button>
          </div>
          {/* 查阅人列表 */}
          {readers.length > 0 && (
            <div className="flex items-center gap-1 mt-1 ml-1">
              {readers.map(name => (
                <span
                  key={name}
                  className="inline-flex items-center gap-0.5 text-[10px] text-[var(--text-muted)]"
                  title={`${name} 已查阅`}
                >
                  <span className="w-1.5 h-1.5 rounded-full bg-green-500 flex-shrink-0" />
                  {name}
                </span>
              ))}
            </div>
          )}
        </div>
      </div>

      {/* A4 预览弹窗 */}
      {showPreview && (
        <div
          className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm"
          onClick={e => { if (e.target === e.currentTarget) setShowPreview(false) }}
        >
          <div
            className="bg-[var(--bg-primary)] rounded-xl shadow-2xl flex flex-col overflow-hidden border border-[var(--border)]"
            style={{ width: '90vw', maxWidth: '900px', maxHeight: '85vh' }}
          >
            {/* 弹窗头部 */}
            <div className="flex items-center justify-between px-6 py-3 border-b border-[var(--border)] bg-[var(--bg-secondary)] flex-shrink-0">
              <div className="flex items-center gap-2 min-w-0">
                <FileText className="w-4 h-4 text-[var(--accent)] flex-shrink-0" />
                <span className="font-medium text-sm truncate">{msg.title || msg.filename}</span>
              </div>
              <div className="flex items-center gap-2 flex-shrink-0">
                <button
                  onClick={copyContent}
                  className="flex items-center gap-1 px-2.5 py-1 rounded-md text-xs text-[var(--text-muted)] hover:bg-[var(--bg-hover)] transition-colors"
                >
                  {copied ? <Check className="w-3.5 h-3.5" /> : <Copy className="w-3.5 h-3.5" />}
                  {copied ? '已复制' : '复制'}
                </button>
                <button
                  onClick={() => setShowPreview(false)}
                  className="p-1.5 rounded-md text-[var(--text-muted)] hover:bg-[var(--bg-hover)] transition-colors"
                >
                  <X className="w-4 h-4" />
                </button>
              </div>
            </div>
            {/* A4 文档内容 */}
            <div className="flex-1 overflow-y-auto">
              <div className="mx-auto px-[20mm] py-[15mm] max-w-none" style={{ minHeight: '100%' }}>
                <div className="prose prose-sm dark:prose-invert max-w-none prose-headings:font-bold prose-h1:text-2xl prose-h1:border-b prose-h1:pb-2 prose-h1:mb-6 prose-h2:text-lg prose-h2:mt-6 prose-table:text-sm">
                  <ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>{docContent}</ReactMarkdown>
                </div>
              </div>
            </div>
          </div>
        </div>
      )}
    </>
  )
}

/* ---- Message Bubble ---- */
function MessageBubble({ msg, userName }: { msg: Message; userName?: string }) {
  const isUser = msg.role === 'user'
  const isMaster = msg.role === 'master'
  const isToolUse = msg.role === 'tool_use'
  const agentName = msg.agent || 'Unknown'
  const [copied, setCopied] = useState(false)

  const avatarColors = [
    'bg-red-500', 'bg-orange-500', 'bg-amber-500', 'bg-yellow-500',
    'bg-lime-500', 'bg-green-500', 'bg-emerald-500', 'bg-teal-500',
    'bg-cyan-500', 'bg-sky-500', 'bg-blue-500', 'bg-indigo-500',
    'bg-violet-500', 'bg-purple-500', 'bg-fuchsia-500', 'bg-pink-500', 'bg-rose-500'
  ]
  const colorIndex = agentName.charCodeAt(0) % avatarColors.length
  const displayName = isUser ? (userName || agentName) : agentName

  const copyText = (text: string) => {
    navigator.clipboard.writeText(text)
    setCopied(true)
    setTimeout(() => setCopied(false), 2000)
  }

  return (
    <div className={`flex gap-3 group ${isUser ? 'flex-row-reverse' : ''}`}>
      <div className={`w-10 h-10 rounded-full flex items-center justify-center text-white font-semibold flex-shrink-0 mt-0.5 ${isUser ? 'bg-[var(--accent)]' : avatarColors[colorIndex]}`}>
        {isMaster ? <Crown className="w-5 h-5" /> : displayName[0]?.toUpperCase()}
      </div>
      <div className={`flex flex-col max-w-[70%] ${isUser ? 'items-end' : 'items-start'}`}>
        <div className={`flex items-center gap-2 mb-0.5 ${isUser ? 'flex-row-reverse' : ''}`}>
          <span className={`font-medium text-sm ${isMaster ? 'text-[var(--color-warning)]' : isUser ? 'text-[var(--accent)]' : 'text-[var(--text-primary)]'}`}>
            {displayName}
          </span>
          {isMaster && (
            <span className="text-[10px] px-1 py-0.5 bg-[var(--color-warning)]/20 text-[var(--color-warning)] rounded font-medium">
              MASTER
            </span>
          )}
          {msg.created_at && (
            <span className="text-[10px] text-[var(--text-muted)] opacity-60">
              {formatTime(msg.created_at)}
            </span>
          )}
        </div>
        {isToolUse ? (
          <div className="px-3.5 py-2 rounded-lg text-sm bg-[var(--bg-tertiary)] border border-[var(--border)] rounded-tl-sm flex items-center gap-2">
            {msg.streaming && msg.content === 'thinking' ? (
              <>
                <span className="inline-block w-4 h-4 border-2 border-[var(--color-warning)] border-t-transparent rounded-full animate-spin" />
                <span className="text-[var(--text-muted)]">搜索完成，正在整理结果...</span>
              </>
            ) : msg.streaming ? (
              <>
                <span className="inline-block w-4 h-4 border-2 border-[var(--accent)] border-t-transparent rounded-full animate-spin" />
                <span className="text-[var(--text-muted)]">正在搜索...</span>
              </>
            ) : (
              <>
                <span className="text-[var(--color-success)]">&#10003;</span>
                <span className="text-[var(--text-muted)]">完成</span>
              </>
            )}
          </div>
        ) : (
          <div
            className={`
              px-3.5 py-2 rounded-lg text-sm relative
              ${isUser
                ? 'bg-[var(--accent)] text-white rounded-tr-sm'
                : isMaster
                ? 'bg-[var(--bg-tertiary)] border-l-4 border-[var(--color-warning)] rounded-tl-sm'
                : 'bg-[var(--bg-tertiary)] rounded-tl-sm'
              }
            `}
          >
            <div className={`prose prose-sm max-w-none [&_code]:px-1.5 [&_code]:py-0.5 [&_code]:rounded [&_code]:font-medium [&_code]:text-xs [&_code]:before:content-none [&_code]:after:content-none ${
              isUser
                ? 'prose-invert [&_code]:bg-white/20 [&_code]:text-white'
                : 'dark:prose-invert [&_code]:bg-[var(--accent)]/15 [&_code]:text-[var(--accent)]'
            }`}>
              <ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>
                {msg.streaming ? fixStreamingMarkdown(highlightMentions(msg.content)) : highlightMentions(msg.content)}
              </ReactMarkdown>
            </div>
            {msg.streaming && (
              <span className="inline-block w-1.5 h-3 bg-[var(--text-muted)] animate-pulse ml-0.5" />
            )}
          </div>
        )}
        {!msg.streaming && msg.content && (
          <div className={`flex items-center gap-1 mt-0.5 opacity-0 group-hover:opacity-100 transition-opacity ${isUser ? 'justify-end' : ''}`}>
            <button
              onClick={() => copyText(msg.content)}
              className="flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] text-[var(--text-muted)] hover:text-[var(--text-primary)] hover:bg-[var(--bg-hover)] transition-colors"
              title="复制消息"
            >
              {copied ? <Check className="w-3 h-3" /> : <Copy className="w-3 h-3" />}
              {copied ? '已复制' : '复制'}
            </button>
          </div>
        )}
      </div>
    </div>
  )
}


function ThinkingBubble({ agent }: { agent: string }) {
  return (
    <div className="flex gap-3">
      <div className="w-10 h-10 rounded-full bg-[var(--color-warning)]/20 flex items-center justify-center flex-shrink-0 mt-0.5">
        <Crown className="w-5 h-5 text-[var(--color-warning)]" />
      </div>
      <div className="flex flex-col items-start">
        <span className="font-medium text-sm text-[var(--color-warning)] mb-0.5">{agent || 'Master'}</span>
        <div className="px-3.5 py-2.5 rounded-lg bg-[var(--bg-tertiary)] border-l-4 border-[var(--color-warning)] rounded-tl-sm flex items-center gap-1.5">
          <span className="w-2 h-2 rounded-full bg-[var(--color-warning)] animate-bounce" style={{ animationDelay: '0ms' }} />
          <span className="w-2 h-2 rounded-full bg-[var(--color-warning)] animate-bounce" style={{ animationDelay: '150ms' }} />
          <span className="w-2 h-2 rounded-full bg-[var(--color-warning)] animate-bounce" style={{ animationDelay: '300ms' }} />
        </div>
      </div>
    </div>
  )
}
