'use client'
import './h5-client-app.css'

import { useState, useEffect, useLayoutEffect, useRef, useCallback } from 'react'
import type React from 'react'
import { Plus, Send, ArrowUp, Square, Paperclip, MoreHorizontal, Trash2, Pencil } from 'lucide-react'
import type { ContentBlock } from '@/lib/types'
import { themeToInlineStyle, type H5Theme } from '@/lib/h5/theme'
import type { H5VisualStyle, H5WelcomeHighlight, H5WelcomeLayout } from '@/lib/h5/theme-presets'
import type { SessionItem, MessageItem, PageInfo, PendingAttachment, H5BootstrapResponse } from '@/lib/h5/types'
import { formatRelativeTime } from '@/lib/h5/format'
import { renderUserContent, renderAssistantContent, extractAssistantText, H5CopyButton, H5AttachmentList } from './h5-message-render'
import { useH5ChatStream } from './use-h5-chat-stream'
import { H5Header } from './h5-header'
import { H5Toast } from './h5-toast'
import { H5AttachTray } from './h5-attach-tray'
import { H5StreamingBubble } from './h5-streaming-bubble'
import { H5DeleteModal } from './h5-delete-modal'
import { H5RenameModal } from './h5-rename-modal'
import { H5WelcomeCard } from './h5-welcome-card'

/**
 * H5ClientApp — 外部用户对话页主组件（§7）。
 *
 * 由 /h5/c/page.tsx 渲染，从 props 接收 slug。
 * 通过 /api/h5/* API 与后端交互（forge_h5 cookie 鉴权）。
 * 401 → 渲染过期态。
 *
 * 样式独立化（P0）：所有视觉值引用 --h5-* CSS 变量（由 /h5/layout.tsx 注入默认值），
 * 不消费宿主 Forge 的 --bg-page / --text-primary 等变量，不依赖 globals.css。
 * markdown 渲染由 h5-markdown-renderer.tsx 独立完成（零 Shiki 依赖）。
 */

interface H5ClientAppProps {
  slug: string
  /** 对接 APP 品牌主题覆盖（由 /h5/c/page.tsx 从 forge_h5_theme cookie 读取）。undefined = 用默认深色。 */
  theme?: H5Theme
  /** 结构风格独立于颜色主题。默认使用统一 editorial 骨架，bubble 仅作兼容。 */
  visualStyle?: H5VisualStyle
  /** 仅控制空会话欢迎区域，不影响消息流和输入区。 */
  welcomeLayout?: H5WelcomeLayout
  /** profile-card 欢迎布局展示的能力摘要。 */
  welcomeHighlights?: readonly H5WelcomeHighlight[]
  /** 欢迎卡是否展示 Beta 徽章。 */
  showBetaBadge?: boolean
  /** Server Component 预查的页面信息，用作首屏初始值，避免标题闪烁。 */
  initialPageInfo?: PageInfo
}

export function H5ClientApp({
  slug,
  theme,
  visualStyle = 'editorial',
  welcomeLayout = 'standard',
  welcomeHighlights = [],
  showBetaBadge = false,
  initialPageInfo,
}: H5ClientAppProps): React.JSX.Element {
  // P2.5 主题预设：theme 直接 inline 到容器 style，特异性最高（1,0,0,0），
  // 压过 layout 的 <style> 规则，避免层叠冲突。
  const themeStyle = themeToInlineStyle(theme)

  const [mounted, setMounted] = useState(false)
  const [sessions, setSessions] = useState<SessionItem[]>([])
  const [currentSessionId, setCurrentSessionId] = useState<string | null>(null)
  const [messages, setMessages] = useState<MessageItem[]>([])
  const [input, setInput] = useState('')
  const [expired, setExpired] = useState(false)
  const [loadingSessions, setLoadingSessions] = useState(true)
  const [loadingMessages, setLoadingMessages] = useState(false)
  const [drawerOpen, setDrawerOpen] = useState(false)
  const [attachments, setAttachments] = useState<PendingAttachment[]>([])
  const [pageInfo, setPageInfo] = useState<PageInfo | null>(initialPageInfo ?? null)
  const [menuSessionId, setMenuSessionId] = useState<string | null>(null)
  /** 当前进入"删除确认态"的 session id（内联二次确认，替代 window.confirm） */
  const [confirmDeleteId, setConfirmDeleteId] = useState<string | null>(null)
  const [deletingSessionId, setDeletingSessionId] = useState<string | null>(null)
  const [deleteError, setDeleteError] = useState<string | null>(null)
  /** 全局 toast 消息（如删除成功提示），非 null 时显示，自动消失 */
  const [toastMessage, setToastMessage] = useState<string | null>(null)
  /** 重命名中的会话 id（非 null 时弹重命名 modal） */
  const [renameId, setRenameId] = useState<string | null>(null)
  /** 重命名 modal 的输入值 */
  const [renameValue, setRenameValue] = useState('')
  /** 重命名提交中（禁用按钮 + 文案切换） */
  const [renaming, setRenaming] = useState(false)
  const messagesRef = useRef<HTMLDivElement>(null)
  const messagesEndRef = useRef<HTMLDivElement>(null)
  const shouldFollowMessagesRef = useRef(true)
  const fileInputRef = useRef<HTMLInputElement>(null)
  const textareaRef = useRef<HTMLTextAreaElement>(null)
  /** 首次对话 done 后静默切换 session 时置 true：让下方 effect 用 silent 模式加载消息，跳过骨架屏 */
  const silentSessionSwitchRef = useRef(false)

  // 抽屉常驻 DOM（不再用 drawerVisible 条件渲染），打开/关闭都靠 drawerOpen
  // 切换 class 触发 transform/opacity 过渡——保证打开也有滑入动画。
  // 关闭时清空会话菜单/删除确认等内联状态。
  useEffect(() => {
    if (!drawerOpen) {
      setMenuSessionId(null)
      setConfirmDeleteId(null)
      setDeleteError(null)
    }
  }, [drawerOpen])

  // toast 显示 2.5s 后自动消失
  useEffect(() => {
    if (!toastMessage) return
    const t = setTimeout(() => setToastMessage(null), 2500)
    return () => clearTimeout(t)
  }, [toastMessage])

  // 首屏一次性加载：page + sessions + 默认会话消息（去掉 list→messages 串行瀑布）
  const loadBootstrap = useCallback(async () => {
    setLoadingSessions(true)
    setLoadingMessages(true)
    try {
      const res = await fetch('/api/h5/bootstrap')
      if (res.status === 401) {
        setExpired(true)
        return
      }
      if (!res.ok) return
      const data = (await res.json()) as H5BootstrapResponse
      setPageInfo(data.page)
      setSessions(data.sessions)
      setMessages(data.messages)
      // 仅在有默认会话时跳过 effect 二次拉 messages；无会话时不置 silent，避免 ref 卡住
      if (data.currentSessionId) {
        silentSessionSwitchRef.current = true
      }
      setCurrentSessionId(data.currentSessionId)
    } catch {
      // ignore
    } finally {
      setLoadingSessions(false)
      setLoadingMessages(false)
    }
  }, [])

  // 加载会话列表（流式 done 后静默刷新新建 session、侧栏维护；首屏改走 bootstrap）
  const loadSessions = useCallback(async (silent = false): Promise<string | null> => {
    // silent=true：首次对话 done 后静默拉取新建 session，不触发骨架屏。
    // 返回新建（或首个）session id，供 done 分支手动静默加载消息。
    if (!silent) setLoadingSessions(true)
    try {
      const res = await fetch('/api/h5/session/list')
      if (res.status === 401) {
        setExpired(true)
        return null
      }
      if (res.ok) {
        const data = (await res.json()) as SessionItem[]
        setSessions(data)
        if (data.length > 0 && !currentSessionId) {
          setCurrentSessionId(data[0].id)
          if (!silent) setLoadingMessages(true)
          return data[0].id
        }
      }
    } catch {
      // ignore
    } finally {
      if (!silent) setLoadingSessions(false)
    }
    return null
  }, [currentSessionId])

  // 加载会话消息（切换会话 / 流式 done 刷新；content 已由后端 strip）
  const loadMessages = useCallback(async (sessionId: string, silent = false) => {
    // silent=true：AI 回复完成后的静默刷新，不触发骨架屏，避免整列表闪烁抖动
    if (!silent) setLoadingMessages(true)
    try {
      const res = await fetch(`/api/h5/messages?sessionId=${encodeURIComponent(sessionId)}`)
      if (res.status === 401) {
        setExpired(true)
        return
      }
      if (res.ok) {
        const data = (await res.json()) as MessageItem[]
        setMessages(data)
      }
    } catch {
      // ignore
    } finally {
      if (!silent) setLoadingMessages(false)
    }
  }, [])


  // SSE 流式收发：流式 state + 解析逻辑抽到 useH5ChatStream，组件只管乐观插入与 done 后的消息同步。
  // 时序硬约束在 hook 内（done → await onDone → 清流式 → finally setStreaming(false) 同步段）。
  const handleStreamDone = async () => {
    // 完成后静默刷新消息列表（持久 assistant 消息已落库，含完整 blocks）。
    // silent=true 跳过骨架屏，避免整列表闪烁抖动。
    if (currentSessionId) {
      await loadMessages(currentSessionId, true)
    } else {
      // 首次对话后 currentSessionId 为空，静默重载会话列表获取新建的 session，
      // 再手动 silent 加载该 session 的消息——确保持久化 assistant 消息在
      // 流式气泡消失前就位。silentSessionSwitchRef 让随后 setCurrentSessionId
      // 触发的 effect 跳过，防止重复加载。
      silentSessionSwitchRef.current = true
      const newId = await loadSessions(true)
      if (newId) await loadMessages(newId, true)
    }
  }
  const { streaming, streamingText, streamingError, streamingAttachments, streamingActivity, stream, stop } = useH5ChatStream({
    onDone: handleStreamDone,
    onUnauthorized: () => setExpired(true),
  })

  // mounted gate：SSR + 首次客户端 render 输出纯骨架占位（样式在 layout 静态 CSS），
  // hydration 后切到完整 UI——避免 hydration 前裸 HTML 闪烁。
  useEffect(() => {
    setMounted(true)
  }, [])

  // 首屏：单次 bootstrap（page + sessions + 默认会话 messages）
  useEffect(() => {
    void loadBootstrap()
  }, [loadBootstrap])

  useEffect(() => {
    shouldFollowMessagesRef.current = true
    if (currentSessionId) {
      // silentSessionSwitchRef 为 true：bootstrap 或 done 分支已手动加载过该 session 的消息，
      // 跳过避免重复加载与骨架屏闪烁。
      if (silentSessionSwitchRef.current) {
        silentSessionSwitchRef.current = false
        return
      }
      void loadMessages(currentSessionId)
    } else {
      // bootstrap 无会话时 messages 已是 []；用户删光当前会话时也需清空
      setMessages([])
    }
  }, [currentSessionId, loadMessages])

  // 仅当用户仍在消息底部附近时跟随流式输出；浏览历史时不强制拉回。
  const handleMessagesScroll = useCallback(() => {
    const el = messagesRef.current
    if (!el) return
    const distanceToBottom = el.scrollHeight - el.scrollTop - el.clientHeight
    shouldFollowMessagesRef.current = distanceToBottom < 96
  }, [])

  useEffect(() => {
    if (!shouldFollowMessagesRef.current) return
    messagesEndRef.current?.scrollIntoView({ behavior: streaming ? 'auto' : 'smooth' })
  }, [messages, streamingText, streamingError, streamingAttachments, streaming])

  // textarea 自适应高度：绘制前完成尺寸更新，达到上限后才启用内部滚动。
  useLayoutEffect(() => {
    const el = textareaRef.current
    if (!el) return
    el.style.height = 'auto'
    const nextHeight = Math.min(el.scrollHeight, 120)
    el.style.height = `${nextHeight}px`
    el.style.overflowY = el.scrollHeight > 120 ? 'auto' : 'hidden'
  }, [input])

  // 新建会话
  const handleNewSession = async () => {
    try {
      const res = await fetch('/api/h5/session/new', { method: 'POST' })
      if (res.status === 401) {
        setExpired(true)
        return
      }
      if (res.ok) {
        const data = (await res.json()) as SessionItem
        setSessions((prev) => [data, ...prev])
        setCurrentSessionId(data.id)
        setMessages([])
        setDrawerOpen(false)
      }
    } catch {
      // ignore
    }
  }

  // 删除会话（二次确认由 confirmDeleteId 驱动的居中 modal 完成）
  const handleDeleteSession = async (sessionId: string) => {
    setDeletingSessionId(sessionId)
    setDeleteError(null)
    try {
      const res = await fetch(`/api/h5/sessions/${encodeURIComponent(sessionId)}`, {
        method: 'DELETE',
      })
      if (res.status === 401) {
        setExpired(true)
        return
      }
      if (!res.ok) {
        setDeleteError('删除失败，请重试')
        return
      }
      setSessions((prev) => prev.filter((s) => s.id !== sessionId))
      setConfirmDeleteId(null)
      setMenuSessionId(null)
      setToastMessage('已删除')
      if (currentSessionId === sessionId) {
        setCurrentSessionId(null)
        setMessages([])
      }
    } catch {
      setDeleteError('网络异常，请重试')
    } finally {
      setDeletingSessionId(null)
    }
  }

  // 重命名会话（PATCH /api/h5/sessions/[id]），成功后本地更新标题 + toast
  const handleRenameSession = async () => {
    if (!renameId) return
    const title = renameValue.trim()
    if (!title) return
    setRenaming(true)
    try {
      const res = await fetch(`/api/h5/sessions/${encodeURIComponent(renameId)}`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ title }),
      })
      if (res.status === 401) {
        setExpired(true)
        return
      }
      if (!res.ok) {
        setToastMessage('重命名失败')
        return
      }
      const id = renameId
      setSessions((prev) => prev.map((s) => (s.id === id ? { ...s, title } : s)))
      setRenameId(null)
      setRenameValue('')
      setToastMessage('已重命名')
    } catch {
      setToastMessage('网络异常')
    } finally {
      setRenaming(false)
    }
  }

  // 上传文件（与桌面端 chat-view 同样走 /api/upload，无鉴权 open route）
  // 移动端 <input type="file" multiple> 由系统调起文件选择器；
  // iOS/Android WebView 中能否弹出文件选择器取决于宿主 APP 是否实现了
  // onShowFileChooser (Android) / WKWebView UIDelegate (iOS)。
  const handleFileUpload = useCallback(async (files: FileList | null) => {
    if (!files || files.length === 0) return
    for (const file of Array.from(files)) {
      const tempId = `pending-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
      // 先占一个 uploading 位
      setAttachments((prev) => [
        ...prev,
        {
          name: file.name,
          filename: tempId,
          mimeType: file.type || 'application/octet-stream',
          tier: 'binary',
          isImage: (file.type || '').startsWith('image/'),
          uploading: true,
        },
      ])
      const formData = new FormData()
      formData.append('file', file)
      try {
        const res = await fetch('/api/upload', { method: 'POST', body: formData })
        if (!res.ok) {
          setAttachments((prev) => prev.filter((a) => a.filename !== tempId))
          continue
        }
        const data = (await res.json()) as {
          filename: string
          originalName: string
          mimeType: string
          tier: 'image' | 'pdf' | 'text' | 'binary'
          isImage: boolean
        }
        setAttachments((prev) =>
          prev.map((a) =>
            a.filename === tempId
              ? {
                  ...a,
                  filename: data.filename,
                  name: data.originalName,
                  mimeType: data.mimeType,
                  tier: data.tier,
                  isImage: data.isImage,
                  uploading: false,
                }
              : a,
          ),
        )
      } catch {
        setAttachments((prev) => prev.filter((a) => a.filename !== tempId))
      }
    }
    if (fileInputRef.current) fileInputRef.current.value = ''
  }, [])

  const removeAttachment = useCallback((filename: string) => {
    setAttachments((prev) => prev.filter((a) => a.filename !== filename))
  }, [])

  // 发送消息：乐观插入用户消息后交给 useH5ChatStream（SSE 解析 + 流式 state 在 hook 内）。
  const handleSend = async () => {
    const text = input.trim()
    // 附件未上传完成时不允许发送
    const stillUploading = attachments.some((a) => a.uploading)
    if (streaming || stillUploading) return
    if (!text && attachments.length === 0) return

    const sentAttachments = attachments
    const sentText = text

    shouldFollowMessagesRef.current = true
    setInput('')
    setAttachments([])

    // 即时显示用户消息（ContentBlock[] 格式，与后端存储一致，含 text + 附件块）
    const userBlocks: ContentBlock[] = []
    if (sentText) userBlocks.push({ type: 'text', text: sentText })
    for (const att of sentAttachments) {
      const url = `/api/upload/${att.filename}`
      if (att.isImage) {
        userBlocks.push({ type: 'image_attachment', url, name: att.name })
      } else {
        userBlocks.push({ type: 'file_attachment', url, name: att.name, size: 0, mimeType: att.mimeType })
      }
    }
    const userMsg: MessageItem = {
      id: `temp-${Date.now()}`,
      role: 'user',
      content: JSON.stringify(userBlocks),
      createdAt: new Date().toISOString(),
    }
    setMessages((prev) => [...prev, userMsg])

    await stream({ sessionId: currentSessionId, message: sentText, attachments: sentAttachments })
  }
  // 过期态
  if (expired) {
    return (
      <div className="h5-expired-view" style={themeStyle}>
        <div className="h5-expired-content">
          <p className="h5-expired-title">会话已过期</p>
          <p className="h5-expired-hint">请从 APP 重新进入</p>
        </div>
      </div>
    )
  }

  // mounted gate：hydration 前只输出骨架占位。
  // 样式完全来自 layout 静态 CSS（.h5-skeleton + shimmer），不依赖 styled-jsx，
  // 避免 dev 模式下 styled-jsx 注入延迟导致的裸 HTML 闪烁。
  if (!mounted) {
    return (
      <div
        className="h5-app"
        data-h5-visual-style={visualStyle}
        style={{ ...themeStyle, backgroundColor: 'var(--h5-bg, #0a0a0a)', minHeight: '100dvh' }}
      >
        <div className="h5-skeleton-list" style={{ padding: '16px 12px' }}>
          <div className="h5-skeleton h5-skeleton-bubble assistant" />
          <div className="h5-skeleton h5-skeleton-bubble assistant short" />
          <div className="h5-skeleton h5-skeleton-bubble user" />
          <div className="h5-skeleton h5-skeleton-bubble assistant" />
        </div>
      </div>
    )
  }

  return (
    <div className="h5-app" data-h5-visual-style={visualStyle} style={themeStyle}>
      {/* 顶栏 */}
      <H5Header
        title={pageInfo?.pageTitle || pageInfo?.workspaceName || '对话'}
        onMenuClick={() => setDrawerOpen(!drawerOpen)}
        showBetaBadge={showBetaBadge}
      />

      {/* 侧栏抽屉 */}
        <div
          className={`h5-drawer-overlay ${drawerOpen ? 'open' : ''}`}
          onClick={() => setDrawerOpen(false)}
          aria-hidden={!drawerOpen}
        >
          <div
            className={`h5-drawer ${drawerOpen ? 'open' : ''}`}
            onClick={(e) => e.stopPropagation()}
          >
            <div className="h5-drawer-header">
              <span className="h5-drawer-title">我的对话</span>
              <button
                className="h5-drawer-new-btn"
                onClick={() => void handleNewSession()}
                aria-label="新建对话"
              >
                <Plus size={16} />
              </button>
            </div>
            <div className="h5-drawer-list">
              {loadingSessions ? (
                <div className="h5-empty">加载中...</div>
              ) : sessions.length === 0 ? (
                <div className="h5-empty">暂无会话</div>
              ) : (
                sessions.map((s) => (
                  <div
                    key={s.id}
                    className={`h5-session-item ${s.id === currentSessionId ? 'active' : ''}`}
                    onClick={() => {
                      setCurrentSessionId(s.id)
                      setDrawerOpen(false)
                      setMenuSessionId(null)
                      setConfirmDeleteId(null)
                      setDeleteError(null)
                    }}
                  >
                    <div className="h5-session-title">{s.title}</div>
                    <div className="h5-session-time">{formatRelativeTime(s.updatedAt)}</div>
                    <button
                      type="button"
                      className="h5-session-more"
                      onClick={(e) => {
                        e.stopPropagation()
                        const opening = menuSessionId !== s.id
                        setMenuSessionId(opening ? s.id : null)
                        setConfirmDeleteId(null)
                        setDeleteError(null)
                      }}
                      aria-label="更多操作"
                    >
                      <MoreHorizontal size={14} />
                    </button>
                    {menuSessionId === s.id && (
                      <div className="h5-session-menu">
                        <button
                          type="button"
                          className="h5-session-menu-item"
                          onClick={(e) => {
                            e.stopPropagation()
                            setRenameId(s.id)
                            setRenameValue(s.title)
                            setMenuSessionId(null)
                          }}
                        >
                          <Pencil size={13} />
                          重命名
                        </button>
                        <button
                          type="button"
                          className="h5-session-menu-item danger"
                          onClick={(e) => {
                            e.stopPropagation()
                            setConfirmDeleteId(s.id)
                            setMenuSessionId(null)
                            setDeleteError(null)
                          }}
                        >
                          <Trash2 size={13} />
                          删除对话
                        </button>
                      </div>
                    )}
                  </div>
                ))
              )}
            </div>
          </div>
        </div>

      {/* 消息流 */}
      <main className="h5-main">
        <div ref={messagesRef} className="h5-messages" onScroll={handleMessagesScroll}>
          {loadingMessages || loadingSessions ? (
            <div className="h5-skeleton-list">
              <div className="h5-skeleton h5-skeleton-bubble assistant" />
              <div className="h5-skeleton h5-skeleton-bubble assistant short" />
              <div className="h5-skeleton h5-skeleton-bubble user" />
              <div className="h5-skeleton h5-skeleton-bubble assistant" />
            </div>
          ) : messages.length === 0 && !streaming ? (
            pageInfo ? (
              <H5WelcomeCard pageInfo={pageInfo} layout={welcomeLayout} highlights={welcomeHighlights} />
            ) : (
              <div className="h5-welcome">
                <p>开始一段新对话</p>
              </div>
            )
          ) : (
            messages.map((m) => {
              const isAssistant = m.role === 'assistant'
              const assistantText = isAssistant ? extractAssistantText(m.content) : ''
              return (
                <div key={m.id} className={`h5-msg ${m.role}`}>
                  <div className="h5-msg-content">
                    {isAssistant ? renderAssistantContent(m.content) : renderUserContent(m.content)}
                    {isAssistant && assistantText ? <H5CopyButton text={assistantText} /> : null}
                  </div>
                </div>
              )
            })
          )}

          {/* 流式输出 */}
          {streaming ? (
            <H5StreamingBubble
              streamingText={streamingText}
              streamingError={streamingError}
              streamingAttachments={streamingAttachments}
              streamingActivity={streamingActivity}
            />
          ) : null}
          <div ref={messagesEndRef} />
        </div>

        {/* 输入栏：附件预览 + textarea + 附件按钮 + 发送按钮 */}
        <H5AttachTray attachments={attachments} onRemove={removeAttachment} />

        <div className="h5-input-bar">
          <div className="h5-composer">
            <button
              type="button"
              className="h5-icon-btn h5-attach-btn"
              onClick={() => fileInputRef.current?.click()}
              disabled={streaming}
              aria-label="添加附件"
            >
              <Paperclip size={20} />
            </button>
            <input
              ref={fileInputRef}
              type="file"
              multiple
              className="h5-file-input"
              onChange={(e) => void handleFileUpload(e.target.files)}
              // accept 让移动端文件选择器优先展示常见类型；不强制限制（用户可选任意文件）
              accept="image/*,application/pdf,text/*,.md,.json,.yaml,.yml,.csv,.txt,.tsx,.ts,.jsx,.js,.py,.go,.rs"
            />
            <textarea
              ref={textareaRef}
              className="h5-input"
              value={input}
              onChange={(e) => setInput(e.target.value)}
              placeholder="输入消息..."
              rows={1}
              onKeyDown={(e) => {
                if (e.key === 'Enter' && !e.shiftKey) {
                  e.preventDefault()
                  // 流式期间允许撰写下一条草稿，但不并发发送；停止操作由右侧按钮承担。
                  if (!streaming) void handleSend()
                }
              }}
            />
            <button
              className="h5-send-btn"
              onClick={() => (streaming ? stop() : void handleSend())}
              disabled={(!input.trim() && attachments.length === 0 && !streaming) || attachments.some((a) => a.uploading)}
              aria-label={streaming ? '停止生成' : '发送消息'}
            >
              {streaming ? (
                <Square size={14} fill="currentColor" />
              ) : visualStyle === 'editorial' ? (
                <ArrowUp size={18} />
              ) : (
                <Send size={16} />
              )}
            </button>
          </div>
          <p className="h5-disclaimer">内容由AI生成，仅供参考</p>
        </div>
      </main>

      {/* 删除确认弹框（居中 modal） */}
      {confirmDeleteId ? (
        <H5DeleteModal
          deleting={!!deletingSessionId}
          error={deleteError}
          onConfirm={() => void handleDeleteSession(confirmDeleteId)}
          onClose={() => {
            if (!deletingSessionId) {
              setConfirmDeleteId(null)
              setDeleteError(null)
            }
          }}
        />
      ) : null}

      {/* 重命名弹框（居中 modal + 输入框） */}
      {renameId ? (
        <H5RenameModal
          value={renameValue}
          renaming={renaming}
          onValueChange={setRenameValue}
          onConfirm={() => void handleRenameSession()}
          onClose={() => {
            if (!renaming) {
              setRenameId(null)
              setRenameValue('')
            }
          }}
        />
      ) : null}

      {/* 全局 toast（删除成功等） */}
      {toastMessage ? <H5Toast message={toastMessage} /> : null}

    </div>
  )
}
