import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useLocation, useParams, useSearchParams } from 'react-router-dom'
import { Button } from '@heroui/react'
import { motion } from 'framer-motion'
import { useScroll } from 'ahooks'
import styles from './Chat.module.less'
import { type WorkflowControlPayload, type WorkflowParseOptions, enrichWorkflowControlWithPacketQuestion, getLastQaRecordWorkflowContext, hasQaWorkflowContext, hasWorkflowFormCards, isHiddenWorkflowRedirectControl, isHiddenWorkflowRedirectPayload, isWorkflowControlEnd, isWorkflowControlPayloadComplete, isWorkflowEnterSubPayload, isWorkflowPacketEnd, markWorkflowFormSubmittedOnRecord, normalizeStreamAnswerChunk, parseWorkflowControlForStream, processApiResponse, resolveDisplayAnswerText, resolveWorkFlowSessionId } from './helper'
import type { WorkflowParamMap } from './lib/workflowCardForm'
import { ChatWelcome } from './components/ChatWelcome'
import { ChatItemUser } from './components/ChatItem/ChatItemUser'
import { ChatAnswerBox } from './components/ChatItem/ChatAnswerBox'
import { trimSpacesOnly } from './components/ChatItem/markdownFormatter'
import { ChatEditor } from '@/components/ChatEditor'
import type { Answer, ChatRecord, OriginalRecord } from '@/types/chat'
import { fetchSessionConversationId, fetchUserQaRecordPage } from '@/api/conversation'
import { fetchCheckTokenApi } from '@/api/chat'
import { fetchStreamResponse } from '@/api/sdkChat'
import { clearCurrentToolId, clearShouldSendQuestion, exitBusinessInsightSubWorkflow, fetchConversations, setBusinessInsightMainWorkflow, setBusinessInsightPendingQuestion, setBusinessInsightSubWorkflow, setCurrentToolId, updateBusinessInsightParamMap } from '@/store/conversationSlice'
import { getUserRolesForApi, safeSessionStorageGetItem, waitForToken } from '@/lib/utils'
import { findToolRowBySessionToolId, getFirstToolListRow, readToolListFromSessionStorage, resolveSubmitQuestionToolPair } from '@/lib/toolRoleRecordTypes'
import { getWorkflowLastToolPair, setWorkflowLastToolPair } from '@/lib/workflowToolParams'
import type { RootState } from '@/store'
import { useAppDispatch, useAppSelector } from '@/store/hook'
import ScrollBtoIcon from '@/assets/svg/scrollBto.svg?react'
import { setIsAsking } from '@/store/chatSlice'
import SdreamLoading from '@/components/SdreamLoading'

type WorkflowStreamAnswer = Answer & {
  workflowParseText?: string
  workflowAnswerChunk?: string
  workflowPacketEnd?: boolean
  /** 任一分片解析到的工作流控制 JSON，流结束后用于自动续问 */
  workflowDetectedControl?: WorkflowControlPayload
}

const PENDING_WORKFLOW_SILENT_BOOTSTRAP_KEY = 'pendingWorkflowSilentBootstrap'

function findLastAiIndex(items: ChatRecord[]): number {
  for (let i = items.length - 1; i >= 0; i--) {
    if (items[i].role === 'ai')
      return i
  }
  return -1
}

function makeLocalGroupId(prefix: string) {
  try {
    // modern browsers
    return `${prefix}-${crypto.randomUUID()}`
  }
  catch {
    return `${prefix}-${Date.now()}-${Math.random().toString(16).slice(2)}`
  }
}

export const Chat: React.FC = () => {
  const { id } = useParams<{ id: string }>()
  const location = useLocation()
  const [searchParams, setSearchParams] = useSearchParams()
  // 优先从 URL 查询参数读取 toolId（刷新后仍能保留），其次从 location.state 读取
  const rawToolIdFromUrl = searchParams.get('toolId')
  // 只有在非 SPA 导航（location.state 不存在）且链接携带 toolId 时才清空，避免影响站内点击历史记录
  const shouldForceClearUrlToolId = !location.state && Boolean(rawToolIdFromUrl)
  const toolIdFromUrl = shouldForceClearUrlToolId ? null : rawToolIdFromUrl
  // 添加调试日志，查看 location.state 的实际值
  const toolIdFromState = (location.state as { toolId?: string | null } | null)?.toolId
  // 优先使用 URL 中的 toolId，其次使用 state 中的 toolId
  const initialToolId = toolIdFromUrl !== null ? toolIdFromUrl : toolIdFromState
  const [isLoading, setIsLoading] = useState(false)
  const [allItems, setAllItems] = useState<ChatRecord[]>([])
  const dispatch = useAppDispatch()
  const { shouldSendQuestion, currentToolId, conversations, businessInsightWorkflow } = useAppSelector((state: RootState) => state.conversation)
  const isAsking = useAppSelector((state: RootState) => state.chat.isAsking)
  // 标记历史记录是否已加载完成，用于确保自动发送问题在历史记录加载后执行
  const historyLoadedRef = useRef<{ conversationId: string | null }>({ conversationId: null })
  // 保存待发送的问题，用于在历史记录加载完成后发送
  const pendingQuestionRef = useRef<string | null>(null)
  const scrollableRef = useRef<HTMLDivElement | any>(null)
  const position = useScroll(scrollableRef)
  const currentIdRef = useRef<string | undefined>(id)
  const lastSentQuestionRef = useRef<string>('')
  const abortControllerRef = useRef<AbortController | null>(null)
  const [currentToolName, setCurrentToolName] = useState<string | undefined>(undefined)
  // 使用 ref 保存从 location.state 传递的 toolId，避免被异步操作覆盖
  const toolIdFromStateRef = useRef<string | null | undefined>(undefined)
  // 标记是否已通过 toolHistoryLoaded 事件加载数据，避免重复调用接口
  const toolHistoryLoadedRef = useRef<{ conversationId: string | null }>({ conversationId: null })
  // 标记是否已处理从收藏返回的逻辑，避免重复调用
  const fromCollectProcessedRef = useRef<boolean>(false)
  // 保存从收藏返回的标记，避免 location.state 被清除后丢失
  const fromCollectRef = useRef<boolean>(false)
  // 记录正在处理的 conversationId，避免重复调用
  const processingConversationIdRef = useRef<string | null>(null)
  /** 最近一次 query_user_qa_record_list 最后一条上的 workflow 字段，供自动提问透传 */
  const lastLoadedQaWorkflowContextRef = useRef<{
    recordType: string
    workFlowSessionId: string
    busiType?: string
  } | null>(null)
  // 标记是否为提质增效模式的自动调用（用于提取欢迎语）
  const isAutoSubmitQualityImprovementRef = useRef<boolean>(false)
  // 提质增效自动调用欢迎语候选（用于流结束后写入 sessionStorage）
  const autoWelcomeCandidateRef = useRef<string>('')
  const hasAutoWelcomeCandidateRef = useRef<boolean>(false)
  // 业务洞察工作流状态 ref（用于在流结束后访问最新状态）
  const businessInsightWorkflowRef = useRef<{
    mainWorkflowSessionId: string | null
    subWorkflowSessionId: string | null
    subWorkflowRecordType: string | null
    paramMap: WorkflowParamMap
    isInSubWorkflow: boolean
    pendingQuestion: string | null
  }>({
    mainWorkflowSessionId: null,
    subWorkflowSessionId: null,
    subWorkflowRecordType: null,
    paramMap: {},
    isInSubWorkflow: false,
    pendingQuestion: null,
  })
  /** 主工作流返回「进子」JSON 后，已发起空问题「开子」请求，等待本轮流结束写入子 session */
  const expectingSubWorkflowOpenRef = useRef(false)
  const pendingSubOpenRecordTypeRef = useRef<string | null>(null)
  const pendingSubQuestionRef = useRef<string | null>(null)
  /** 最近一次向主工作流提交的用户问题（开子后 cardList 为空时优先用于问子） */
  const lastMainUserQuestionRef = useRef<string | null>(null)
  /** 当前流式请求过程中最后一条 AI 回答的快照（避免流结束回调读到过期的 allItems） */
  const lastStreamedAiAnswerRef = useRef<WorkflowStreamAnswer | null>(null)

  const buildWorkflowParseOptions = useCallback((
    snapshot?: WorkflowStreamAnswer | null,
  ): WorkflowParseOptions | undefined => {
    if (!snapshot?.workflowAnswerChunk && !snapshot?.workflowPacketEnd)
      return undefined
    return {
      ...(snapshot.workflowAnswerChunk ? { answerChunk: snapshot.workflowAnswerChunk } : {}),
      ...(snapshot.workflowPacketEnd ? { packetEnd: true } : {}),
    }
  }, [])
  /** 当前流式请求对应的 AI 气泡 groupId，避免写入错误的历史 AI 记录 */
  const activeStreamingAiGroupIdRef = useRef<string | null>(null)
  /** 工作流静默续跑（空问题开子/子流/表单提交）：提交前已追加独立 AI 占位，流式勿再覆盖上一条 */
  const workflowSilentAppendAiRef = useRef(false)
  /** 本次 paramMap 表单提交对应的 AI 记录标识（recordId 或 groupId），流结束后标记 workflowFormSubmitted */
  const pendingWorkflowFormSourceKeyRef = useRef<string | null>(null)

  const applyLoadedQaWorkflowContext = useCallback((qaRecords: OriginalRecord[] | null | undefined) => {
    const wfCtx = getLastQaRecordWorkflowContext(qaRecords)
    if (wfCtx.recordType && wfCtx.workFlowSessionId) {
      lastLoadedQaWorkflowContextRef.current = {
        recordType: wfCtx.recordType,
        workFlowSessionId: wfCtx.workFlowSessionId,
        busiType: wfCtx.busiType ?? '01',
      }
      dispatch(setBusinessInsightMainWorkflow(wfCtx.workFlowSessionId))
      businessInsightWorkflowRef.current = {
        ...businessInsightWorkflowRef.current,
        mainWorkflowSessionId: wfCtx.workFlowSessionId,
        isInSubWorkflow: false,
      }
      return true
    }
    lastLoadedQaWorkflowContextRef.current = null
    return false
  }, [dispatch])

  // 当外部系统直接以 /chat/:id 链接进入（没有 location.state，且 URL 中也没有 toolId）时，
  // 视为一次新的会话入口：重置为制度活化，清除历史遗留的工具模式状态
  useEffect(() => {
    if (!location.state && !rawToolIdFromUrl) {
      dispatch(clearCurrentToolId())
      // sessionStorage.removeItem('currentToolId')
    }
  }, [location.state, rawToolIdFromUrl, dispatch])
  // 若链接中带有 toolId，进入页面后强制清空（避免沿用历史链接参数）
  useEffect(() => {
    if (!shouldForceClearUrlToolId)
      return

    // 1. 清除 Redux 中的 currentToolId
    dispatch(clearCurrentToolId())
    // 2. 清除 sessionStorage 中的 currentToolId
    // sessionStorage.removeItem('currentToolId')
    // 3. 清除 URL 中的 toolId 参数（如果存在）
    const currentUrl = new URL(window.location.href)
    if (currentUrl.searchParams.has('toolId')) {
      currentUrl.searchParams.delete('toolId')
      window.history.replaceState({}, '', currentUrl.toString())
    }
    // 4. 触发自定义事件，通知 ChatEditor 强制重置为制度活化
    window.dispatchEvent(new CustomEvent('forceResetToGeneralMode'))

    // 同步 react-router 的 searchParams 状态
    const newParams = new URLSearchParams(searchParams)
    newParams.delete('toolId')
    setSearchParams(newParams, { replace: true })
  }, [searchParams, setSearchParams, shouldForceClearUrlToolId, dispatch])
  // 调试用：展示当前页面链接与缓存中的 toolId
  const [debugInfo, setDebugInfo] = useState<{ href: string, sessionToolId: string | null }>({
    href: '',
    sessionToolId: null,
  })

  // 进入聊天页时，同步当前链接和缓存中的 toolId 到页面上展示
  useEffect(() => {
    try {
      const href = window.location.href
      const sessionToolId = sessionStorage.getItem('currentToolId')
      setDebugInfo({
        href,
        sessionToolId,
      })
    }
    catch (error) {
      console.error('同步页面链接与缓存信息失败:', error)
    }
  }, [])

  useEffect(() => {
    if (!debugInfo.href && !debugInfo.sessionToolId)
      return
    console.debug('[Chat] 当前链接 / 缓存 toolId:', debugInfo)
  }, [debugInfo])

  // 历史记录点击时将 toolId 通过路由 state 传入，优先使用该值快速同步高亮
  useEffect(() => {
    // 保存从 location.state 传递的 toolId 到 ref
    toolIdFromStateRef.current = initialToolId
    if (typeof initialToolId === 'undefined')
      return
    if (initialToolId) {
      // 统一转换为字符串，确保类型一致（真实API可能返回数字，需要转换为字符串）
      const normalizedToolId = String(initialToolId)
      dispatch(setCurrentToolId(normalizedToolId))
    }
    else {
      dispatch(clearCurrentToolId())
    }
  }, [dispatch, initialToolId])

  // 同步业务洞察工作流状态到 ref
  useEffect(() => {
    businessInsightWorkflowRef.current = businessInsightWorkflow
  }, [businessInsightWorkflow])

  type SubmitQuestionFn = (
    question: string,
    productCode?: string,
    toolId?: string,
    extraParams?: { paramMap?: WorkflowParamMap, recordId?: string, recordType?: string, workFlowSessionId?: string, isReask?: boolean, busiType?: string, submitViaParamMap?: boolean, omitWorkFlowSessionId?: boolean },
    workFlowSessionId?: string,
    recordId?: string,
  ) => Promise<void>
  const handleSubmitQuestionRef = useRef<SubmitQuestionFn | null>(null)

  interface WorkflowContinueExtra {
    workFlowSessionId: string
    recordType?: string
    busiType?: string
  }

  const buildWorkflowContinueExtraFromRef = (): WorkflowContinueExtra | undefined => {
    const wf = lastLoadedQaWorkflowContextRef.current
    const sessionId = wf?.workFlowSessionId?.trim()
    if (!sessionId)
      return undefined
    const recordType = wf?.recordType?.trim()
    return {
      workFlowSessionId: sessionId,
      ...(recordType ? { recordType } : {}),
      busiType: wf?.busiType ?? '01',
    }
  }

  const resolveWorkflowToolId = useCallback(() => {
    try {
      return (sessionStorage.getItem('currentToolId') ?? '').trim()
        || (currentToolId != null ? String(currentToolId).trim() : '')
    }
    catch {
      return currentToolId != null ? String(currentToolId).trim() : ''
    }
  }, [currentToolId])

  /** 流式结束后根据最新 answer 驱动主/子工作流（避免读取过期的 allItems 闭包） */
  const processBusinessInsightWorkflowAfterStream = useCallback((
    lastAnswer: WorkflowStreamAnswer,
  ) => {
    const streamSnapshot = lastStreamedAiAnswerRef.current
    const parseOptions = buildWorkflowParseOptions(streamSnapshot ?? lastAnswer)
    const packetQuestion = (lastAnswer as WorkflowStreamAnswer & { question?: string }).question
    const wfParseText = trimSpacesOnly(
      lastAnswer.workflowParseText ?? streamSnapshot?.workflowParseText ?? lastAnswer.answer ?? '',
    )
    let parsedAnswer = parseWorkflowControlForStream(wfParseText, parseOptions, packetQuestion)
    if (!parsedAnswer) {
      const detected = lastAnswer.workflowDetectedControl ?? streamSnapshot?.workflowDetectedControl
      const enriched = enrichWorkflowControlWithPacketQuestion(detected, packetQuestion, wfParseText)
      if (enriched && isWorkflowControlPayloadComplete(enriched))
        parsedAnswer = enriched
    }
    const lastAnswerSessionId = resolveWorkFlowSessionId(lastAnswer)
    const hasHiddenRedirect = isHiddenWorkflowRedirectPayload(parsedAnswer)
    const hasEnterSubSignal = isWorkflowEnterSubPayload(parsedAnswer)

    const wf = businessInsightWorkflowRef.current
    const shouldHandleWorkflowAnswer = Boolean(lastAnswerSessionId)
      || Boolean(wf?.mainWorkflowSessionId)
      || Boolean(wf?.isInSubWorkflow)
      || hasEnterSubSignal
      || hasHiddenRedirect

    if (!shouldHandleWorkflowAnswer)
      return

    /** showFlag=false：用 answer 内新 recordType + question 静默续问，本条不在页面展示 */
    if (hasHiddenRedirect && parsedAnswer) {
      const hasCards = Array.isArray(lastAnswer.cardList) && lastAnswer.cardList.length > 0
      // c.txt：任一包 cardList 非空时不得自动续问，需等用户通过卡片提交 paramMap
      if (hasCards)
        return

      const nextRecordType = String(parsedAnswer.recordType).trim()
      const nextQuestion = String(parsedAnswer.question ?? '').trim()
      const sessionId = (
        lastAnswerSessionId
        || wf?.mainWorkflowSessionId?.trim()
        || lastLoadedQaWorkflowContextRef.current?.workFlowSessionId?.trim()
        || ''
      )
      if (nextRecordType && nextQuestion) {
        if (sessionId && !wf?.mainWorkflowSessionId) {
          dispatch(setBusinessInsightMainWorkflow(sessionId))
          businessInsightWorkflowRef.current = {
            ...businessInsightWorkflowRef.current,
            mainWorkflowSessionId: sessionId,
            isInSubWorkflow: false,
          }
        }
        if (sessionId) {
          lastLoadedQaWorkflowContextRef.current = {
            recordType: nextRecordType,
            workFlowSessionId: sessionId,
            busiType: '01',
          }
        }
        queueMicrotask(() => {
          void handleSubmitQuestionRef.current?.(nextQuestion, undefined, resolveWorkflowToolId() || undefined, {
            recordType: nextRecordType,
            busiType: '01',
            ...(sessionId ? { workFlowSessionId: sessionId } : {}),
          })
        })
      }
      return
    }

    if (expectingSubWorkflowOpenRef.current && lastAnswerSessionId) {
      expectingSubWorkflowOpenRef.current = false
      const subSessionId = lastAnswerSessionId
      const subRt = (
        pendingSubOpenRecordTypeRef.current?.trim()
        || (lastAnswer.recordType != null ? String(lastAnswer.recordType).trim() : '')
        || ''
      ).trim()
      pendingSubOpenRecordTypeRef.current = null
      if (subRt && subSessionId) {
        dispatch(setBusinessInsightSubWorkflow({
          sessionId: subSessionId,
          recordType: subRt,
        }))
        businessInsightWorkflowRef.current = {
          ...businessInsightWorkflowRef.current,
          subWorkflowSessionId: subSessionId,
          subWorkflowRecordType: subRt,
          isInSubWorkflow: true,
          paramMap: {},
        }
        const hasCards = Array.isArray(lastAnswer.cardList) && lastAnswer.cardList.length > 0
        const pq = pendingSubQuestionRef.current
        pendingSubQuestionRef.current = null
        if (!hasCards && pq && pq.trim()) {
          queueMicrotask(() => {
            void handleSubmitQuestionRef.current?.(pq, undefined, resolveWorkflowToolId() || undefined, {
              busiType: '01',
              recordType: subRt,
              workFlowSessionId: subSessionId,
            })
          })
        }
      }
      return
    }

    if (expectingSubWorkflowOpenRef.current) {
      expectingSubWorkflowOpenRef.current = false
      pendingSubOpenRecordTypeRef.current = null
      pendingSubQuestionRef.current = null
      return
    }

    if (hasEnterSubSignal && parsedAnswer) {
      const subRecordType = String(parsedAnswer.recordType).trim()
      const controlQ = parsedAnswer.question != null ? String(parsedAnswer.question).trim() : ''
      const mainUserQ = lastMainUserQuestionRef.current?.trim() || ''
      const pendingQ = mainUserQ || controlQ
      if (subRecordType) {
        const mainSessionId = (
          lastAnswerSessionId || businessInsightWorkflowRef.current.mainWorkflowSessionId?.trim() || ''
        )
        if (mainSessionId && !businessInsightWorkflowRef.current.mainWorkflowSessionId) {
          dispatch(setBusinessInsightMainWorkflow(mainSessionId))
          businessInsightWorkflowRef.current = {
            ...businessInsightWorkflowRef.current,
            mainWorkflowSessionId: mainSessionId,
            isInSubWorkflow: false,
          }
        }
        dispatch(setBusinessInsightPendingQuestion(pendingQ || null))
        pendingSubQuestionRef.current = pendingQ || null
        pendingSubOpenRecordTypeRef.current = subRecordType
        expectingSubWorkflowOpenRef.current = true
        console.log('[Chat WF] 自动开子工作流', { recordType: subRecordType, pendingQ, mainSessionId })
        queueMicrotask(() => {
          void handleSubmitQuestionRef.current?.('', undefined, resolveWorkflowToolId() || undefined, {
            recordType: subRecordType,
            busiType: '01',
            ...(mainSessionId ? { workFlowSessionId: mainSessionId } : {}),
          })
        })
      }
      return
    }

    if (businessInsightWorkflowRef.current.isInSubWorkflow) {
      if (isWorkflowControlEnd(parsedAnswer?.end) || parseOptions?.packetEnd) {
        const reopenRt = parsedAnswer?.recordType != null ? String(parsedAnswer.recordType).trim() : ''
        const followQ = parsedAnswer?.question != null ? String(parsedAnswer.question).trim() : ''
        const mainSessionId = businessInsightWorkflowRef.current.mainWorkflowSessionId?.trim() || ''
        dispatch(exitBusinessInsightSubWorkflow())
        businessInsightWorkflowRef.current = {
          ...businessInsightWorkflowRef.current,
          isInSubWorkflow: false,
          subWorkflowSessionId: null,
          subWorkflowRecordType: null,
          paramMap: {},
        }
        dispatch(setBusinessInsightPendingQuestion(null))
        queueMicrotask(() => {
          void handleSubmitQuestionRef.current?.('', undefined, resolveWorkflowToolId() || undefined, {
            busiType: '01',
            ...(reopenRt ? { recordType: reopenRt } : {}),
            ...(mainSessionId ? { workFlowSessionId: mainSessionId } : {}),
          })
          if (followQ) {
            window.setTimeout(() => {
              void handleSubmitQuestionRef.current?.(followQ, undefined, resolveWorkflowToolId() || undefined, {
                busiType: '01',
                ...(reopenRt ? { recordType: reopenRt } : {}),
                ...(mainSessionId ? { workFlowSessionId: mainSessionId } : {}),
              })
            }, 400)
          }
        })
      }
      return
    }

    if (lastAnswerSessionId && !businessInsightWorkflowRef.current.mainWorkflowSessionId) {
      const mainSessionId = lastAnswerSessionId
      if (mainSessionId) {
        dispatch(setBusinessInsightMainWorkflow(mainSessionId))
        businessInsightWorkflowRef.current = {
          ...businessInsightWorkflowRef.current,
          mainWorkflowSessionId: mainSessionId,
          isInSubWorkflow: false,
        }
      }
    }
  }, [buildWorkflowParseOptions, dispatch, resolveWorkflowToolId])

  /** 处理正常stream的数据 */
  const handleStreamMesageData = (msg: any, question: string) => {
    const packetData = msg.content?.data || {}
    const answerChunk = normalizeStreamAnswerChunk(packetData.answer)
    const prevRef = lastStreamedAiAnswerRef.current
    const previousRaw = prevRef?.workflowParseText ?? ''
    const originalAnswer = previousRaw + answerChunk
    let filteredAnswer = originalAnswer.replace(/\([^)]*\)/g, (match, offset, str) => {
      return str[offset - 1] === ']' ? match : ''
    })
    filteredAnswer = filteredAnswer.replace(/\[参考文档(?:《[^》]*》\s*)+\]/g, '')
    filteredAnswer = trimSpacesOnly(filteredAnswer)
    const packetEnd = isWorkflowPacketEnd(packetData)
    const streamParseOptions: WorkflowParseOptions | undefined = (
      answerChunk || packetEnd
    )
      ? {
          ...(answerChunk ? { answerChunk } : {}),
          ...(packetEnd ? { packetEnd: true } : {}),
        }
      : undefined
    const { endAnswerFlag: _syncOmitEnd, ...dataWithoutEndFlag } = packetData
    const wfParseText = trimSpacesOnly(originalAnswer)
    const packetControl = parseWorkflowControlForStream(
      wfParseText,
      streamParseOptions,
      dataWithoutEndFlag.question,
    )
    filteredAnswer = resolveDisplayAnswerText(filteredAnswer, streamParseOptions, dataWithoutEndFlag.question)
    const syncSessionId = resolveWorkFlowSessionId(
      { ...prevRef, ...dataWithoutEndFlag },
      prevRef?.workFlowSessionId,
    )
    // 同步写入 ref：流 fetch resolve 时 React 可能尚未执行 setAllItems，避免读不到控制 JSON
    if (answerChunk || packetEnd || packetControl) {
      lastStreamedAiAnswerRef.current = {
        ...(prevRef || {}),
        ...dataWithoutEndFlag,
        workFlowSessionId: syncSessionId,
        recordId: dataWithoutEndFlag.recordId ?? prevRef?.recordId,
        cardList: Array.isArray(dataWithoutEndFlag.cardList) && dataWithoutEndFlag.cardList.length > 0
          ? dataWithoutEndFlag.cardList
          : prevRef?.cardList,
        answer: filteredAnswer,
        workflowParseText: wfParseText,
        workflowAnswerChunk: answerChunk,
        workflowPacketEnd: packetEnd || prevRef?.workflowPacketEnd,
        ...(packetControl ? { workflowDetectedControl: packetControl } : {}),
      }
      if (packetControl) {
        console.log('[Chat WF] 分片识别到控制 JSON', {
          recordType: packetControl.recordType,
          question: packetControl.question,
          packetQuestion: dataWithoutEndFlag.question,
          parseTextTail: trimSpacesOnly(originalAnswer).slice(-120),
          complete: isWorkflowControlPayloadComplete(packetControl),
          displayAnswer: filteredAnswer,
          packetEnd,
        })
      }
    }

    setAllItems((prevItems) => {
      const newItems = [...prevItems] // 创建数组的浅拷贝
      const isEmptyQuestion = !question.trim()
      // 提质增效自动欢迎语场景：空问题的流式回答应追加在历史记录之后，避免复用最后一条历史 AI 记录
      const shouldAlwaysAppendAi = isAutoSubmitQualityImprovementRef.current && isEmptyQuestion

      // 提质增效模式下，自动调用时收集首个有效 answer 作为欢迎语候选（按流式增量累积）
      if (isAutoSubmitQualityImprovementRef.current && isEmptyQuestion) {
        const chunkAnswer = msg?.content?.data?.answer
        if (typeof chunkAnswer === 'string') {
          // 仅对当前分片做基础清洗，避免重复累积整段答案导致内容重复
          let cleanedChunk = chunkAnswer.replace(/\([^)]*\)/g, '')
          cleanedChunk = cleanedChunk.replace(/\[参考文档(?:《[^》]*》\s*)+\]/g, '')
          cleanedChunk = trimSpacesOnly(cleanedChunk)
          if (cleanedChunk.trim()) {
            autoWelcomeCandidateRef.current = (autoWelcomeCandidateRef.current || '') + cleanedChunk
            hasAutoWelcomeCandidateRef.current = true
          }
        }
      }

      // 提质增效模式下，自动调用时第一个数据块作为欢迎语（兼容旧逻辑，仍保留对 system.welcomeText 的写入）
      if (isAutoSubmitQualityImprovementRef.current && isEmptyQuestion && msg.content.data.answer) {
        // 判断是否为第一个数据块（只有 system 记录或最后一个记录是 system）
        const isFirstChunk = newItems.length === 1 && newItems[0].role === 'system'

        if (isFirstChunk) {
          // 提取欢迎语并更新到 system 记录
          const welcomeText = msg.content.data.answer
          const systemIndex = newItems.findIndex(item => item.role === 'system')
          if (systemIndex >= 0) {
            newItems[systemIndex] = {
              ...newItems[systemIndex],
              welcomeText,
            } as ChatRecord
          }
          // 清除标记，后续数据块不再处理为欢迎语
          isAutoSubmitQualityImprovementRef.current = false
          return newItems
        }
      }

      let lastAiIndex = findLastAiIndex(newItems)
      const streamingGroupId = activeStreamingAiGroupIdRef.current
      if (streamingGroupId) {
        let streamingIndex = newItems.findIndex(
          item => item.role === 'ai' && item.groupId === streamingGroupId,
        )
        if (streamingIndex < 0) {
          newItems.push({
            role: 'ai',
            groupId: streamingGroupId,
            answerList: [{ answer: '', isShow: false }],
          } as ChatRecord)
          streamingIndex = newItems.length - 1
        }
        lastAiIndex = streamingIndex
      }

      // 如果是空问题且需要追加新的 AI 记录（历史末尾没有 AI，或提质增效自动欢迎语场景），则添加 AI 回答占位
      if (isEmptyQuestion && (lastAiIndex < 0 || shouldAlwaysAppendAi)) {
        newItems.push({
          role: 'ai',
          groupId: makeLocalGroupId('ai'),
          answerList: [{ answer: '' }],
        } as ChatRecord)
        lastAiIndex = newItems.length - 1
      }

      if (lastAiIndex >= 0) {
        // 创建最后一项的新对象，合并现有数据和新的 answer
        const existingAnswer = newItems[lastAiIndex].answerList?.[0] || {}
        const mergedFromRef = lastStreamedAiAnswerRef.current
        const chunkToolId = dataWithoutEndFlag.toolId != null ? String(dataWithoutEndFlag.toolId).trim() : ''
        const chunkRecordType = dataWithoutEndFlag.recordType != null ? String(dataWithoutEndFlag.recordType).trim() : ''
        if (chunkToolId && chunkRecordType)
          setWorkflowLastToolPair(chunkToolId, chunkRecordType)
        const mergedAnswer: WorkflowStreamAnswer = {
          ...existingAnswer,
          ...(mergedFromRef || dataWithoutEndFlag),
          workFlowSessionId: mergedFromRef?.workFlowSessionId
            ?? resolveWorkFlowSessionId({ ...existingAnswer, ...dataWithoutEndFlag }, existingAnswer.workFlowSessionId),
          recordId: mergedFromRef?.recordId ?? dataWithoutEndFlag.recordId ?? existingAnswer.recordId,
          cardList: mergedFromRef?.cardList ?? (
            Array.isArray(dataWithoutEndFlag.cardList) && dataWithoutEndFlag.cardList.length > 0
              ? dataWithoutEndFlag.cardList
              : existingAnswer.cardList
          ),
          isShow: false,
          answer: mergedFromRef?.answer ?? filteredAnswer,
          workflowParseText: mergedFromRef?.workflowParseText ?? trimSpacesOnly(originalAnswer),
          workflowDetectedControl: packetControl ?? mergedFromRef?.workflowDetectedControl,
        }
        newItems[lastAiIndex] = {
          ...newItems[lastAiIndex],
          question: isEmptyQuestion ? undefined : question,
          answerList: [mergedAnswer],
        }
      }
      return newItems
    })
  }

  /** 处理超过最大条数的数据 */
  const handleChatMaxCount = (msg: any, question: string) => {
    setAllItems((prevItems) => {
      const newItems = [...prevItems] // 创建数组的浅拷贝
      const lastIndex = newItems.length - 1
      const isEmptyQuestion = !question.trim()

      // 如果是空问题且最后一项不是AI回答，需要添加AI回答
      if (isEmptyQuestion && (lastIndex < 0 || newItems[lastIndex].role !== 'ai')) {
        newItems.push({
          role: 'ai',
          groupId: makeLocalGroupId('ai'),
          answerList: [{ answer: '' }],
        } as ChatRecord)
      }

      const currentIndex = newItems.length - 1
      if (currentIndex >= 0 && newItems[currentIndex].role === 'ai') {
        // 创建最后一项的新对象，合并现有数据和新的 answer
        newItems[currentIndex] = {
          ...newItems[currentIndex],
          question: isEmptyQuestion ? undefined : question,
          answerList: [
            {
              ...msg.content.data,
              isShow: false,
              isChatMaxCount: true,
              endAnswerFlag: true,
              answer: '已达上限',
            },
          ],
        }
      }
      return newItems
    })
  }

  const workflowBootstrapDoneRef = useRef<string | null>(null)

  /** 提交问题 */
  const handleSubmitQuestion = async (
    question: string,
    productCode?: string,
    toolId?: string,
    extraParams?: { paramMap?: WorkflowParamMap, recordId?: string, recordType?: string, workFlowSessionId?: string, isReask?: boolean, busiType?: string, submitViaParamMap?: boolean, omitWorkFlowSessionId?: boolean },
    workFlowSessionId?: string,
    recordId?: string,
  ) => {
    // 优先读取缓存中的 toolId，再回退到传参或 Redux
    const sessionToolId = sessionStorage.getItem('currentToolId') ?? undefined
    const resolvedToolId = toolId ?? sessionToolId ?? currentToolId ?? undefined
    const omitWorkFlowSessionId = extraParams?.omitWorkFlowSessionId === true
    const isEmptyQuestion = !question.trim()
    if (
      !isEmptyQuestion
      && !extraParams?.submitViaParamMap
      && !businessInsightWorkflowRef.current.isInSubWorkflow
    ) {
      lastMainUserQuestionRef.current = question.trim()
    }
    // 业务洞察等工作流参数：有主/子会话态时合并进 extra（新建会话首包静默提问不传 session）
    let workflowExtraParams = extraParams
    if (!omitWorkFlowSessionId && businessInsightWorkflowRef.current) {
      const workflowState = businessInsightWorkflowRef.current
      if (workflowState.isInSubWorkflow) {
        workflowExtraParams = {
          ...extraParams,
          recordType: workflowState.subWorkflowRecordType || undefined,
          workFlowSessionId: workflowState.subWorkflowSessionId || workFlowSessionId,
          paramMap: extraParams?.paramMap
            ? { ...workflowState.paramMap, ...extraParams.paramMap }
            : workflowState.paramMap,
        }
      }
      else if (workflowState.mainWorkflowSessionId) {
        workflowExtraParams = {
          ...extraParams,
          workFlowSessionId: workflowState.mainWorkflowSessionId,
        }
      }
    }

    const submitPairFromList = resolveSubmitQuestionToolPair(resolvedToolId)
    if (!workflowExtraParams?.recordType) {
      const resolvedRecordType = (
        lastLoadedQaWorkflowContextRef.current?.recordType?.trim()
        || getWorkflowLastToolPair()?.recordType?.trim()
        || submitPairFromList.recordType?.trim()
        || ''
      )
      if (resolvedRecordType) {
        workflowExtraParams = {
          ...workflowExtraParams,
          recordType: resolvedRecordType,
        }
      }
    }

    // 空问题 + 显式传入 recordType/busiType（如工具入口 autoSubmit）时走自动欢迎语流式逻辑；
    // 开子工作流、已带 session、或已有主会话的空问题不应走欢迎语分支
    const bootstrapBusiType = workflowExtraParams?.busiType ?? extraParams?.busiType ?? '01'
    const isNewSessionSilentBootstrap = isEmptyQuestion
      && omitWorkFlowSessionId
      && Boolean(workflowExtraParams?.recordType && bootstrapBusiType)
    const isAutoToolEmptySubmit = isNewSessionSilentBootstrap || (
      isEmptyQuestion
      && Boolean(workflowExtraParams?.recordType && bootstrapBusiType)
      && !businessInsightWorkflowRef.current.mainWorkflowSessionId
      && !expectingSubWorkflowOpenRef.current
      && !extraParams?.workFlowSessionId
      && !workflowExtraParams?.workFlowSessionId
    )
    isAutoSubmitQualityImprovementRef.current = isAutoToolEmptySubmit
    if (isAutoSubmitQualityImprovementRef.current) {
      // 重置欢迎语候选
      autoWelcomeCandidateRef.current = ''
      hasAutoWelcomeCandidateRef.current = false
    }
    // 停止之前的请求
    if (abortControllerRef.current) {
      abortControllerRef.current.abort()
    }

    const isParamMapFormSubmit = Boolean(
      workflowExtraParams?.submitViaParamMap
      || (workflowExtraParams?.paramMap && Object.keys(workflowExtraParams.paramMap).length > 0),
    )
    const isWorkflowSilentSubmit = isEmptyQuestion && (
      expectingSubWorkflowOpenRef.current
      || isParamMapFormSubmit
      || Boolean(workflowExtraParams?.recordType && (workflowExtraParams?.busiType || workflowExtraParams?.workFlowSessionId))
      || businessInsightWorkflowRef.current.isInSubWorkflow
    )
    workflowSilentAppendAiRef.current = isWorkflowSilentSubmit

    lastStreamedAiAnswerRef.current = null
    activeStreamingAiGroupIdRef.current = null

    const isNew = allItems.length <= 1
    dispatch(setIsAsking(true))

    // 工作流静默续跑：单独追加 AI 占位，避免覆盖上一条（含 JSON 控制指令）的回答
    if (isWorkflowSilentSubmit) {
      const silentAiGroupId = makeLocalGroupId('ai')
      activeStreamingAiGroupIdRef.current = silentAiGroupId
      setAllItems(prevItems => [
        ...prevItems,
        {
          role: 'ai',
          groupId: silentAiGroupId,
          answerList: [{ answer: '', isShow: false }],
        } as ChatRecord,
      ])
    }

    // 如果 question 不为空，才添加用户问题和空的 AI 回答（静默提问不展示用户气泡）
    if (!isEmptyQuestion) {
      const questionText = question.trim()
      const aiGroupId = makeLocalGroupId('ai')
      activeStreamingAiGroupIdRef.current = aiGroupId
      setAllItems(prevItems => [
        ...prevItems,
        {
          role: 'user',
          groupId: makeLocalGroupId('user'),
          question: questionText,
        } as ChatRecord,
        {
          role: 'ai',
          groupId: aiGroupId,
          question: questionText,
          answerList: [{ answer: '', isShow: false }],
        } as ChatRecord,
      ])
    }

    // 检查 token：失败不阻断提问（流式请求仍带 X-Token，由服务端校验）
    try {
      await fetchCheckTokenApi()
    }
    catch (e) {
      console.warn('[Chat] check_token 失败，仍尝试发起流式提问:', e)
    }

    // 创建新的 AbortController
    abortControllerRef.current = new AbortController()

    let fetchUrl = `/conversation/api/conversation/mobile/v1/submit_question_stream`

    const viteOutputObj = import.meta.env.VITE_OUTPUT_OBJ || 'open'

    let proxy = ''
    if (viteOutputObj === 'open') {
      proxy = import.meta.env.MODE !== 'prod' ? '/api' : '/dev-sdream-api'
    }
 else {
      proxy = import.meta.env.MODE === 'dev' ? '/api' : '/dev-sdream-api'
    }

    fetchUrl = proxy + fetchUrl

    // 根据 toolId 添加相应的参数（paramMap 表单提交不传 question）
    const requestBody: Record<string, any> = {
      conversationId: currentIdRef.current,
      stream: true,
      productCode,
      toolId: resolvedToolId ?? '',
    }
    if (!isParamMapFormSubmit)
      requestBody.question = question

    // 如果是 workflow 表单提交，添加 paramMap、recordId 和 workFlowSessionId
    if (isParamMapFormSubmit && workflowExtraParams?.paramMap)
      requestBody.paramMap = workflowExtraParams.paramMap
    // 优先使用 workflowExtraParams 中的值，其次使用直接传递的参数
    if (workflowExtraParams?.recordId) {
      requestBody.recordId = workflowExtraParams.recordId
    }
    else if (recordId) {
      requestBody.recordId = recordId
    }
    const isReask = workflowExtraParams?.isReask === true
    const isAutoQualityImprovement = isAutoSubmitQualityImprovementRef.current
    const explicitWorkFlowSessionId = omitWorkFlowSessionId
      ? undefined
      : (workflowExtraParams?.workFlowSessionId || workFlowSessionId)
    const workflowRecordType = (
      workflowExtraParams?.recordType
      || submitPairFromList.recordType
      || ''
    ).trim()
    // 有提问流 recordType 即走工作流（含无 toolId 的制度活化），首包 omitWorkFlowSessionId 时不带 session
    const shouldUseWorkflowSession = Boolean(workflowRecordType) && !omitWorkFlowSessionId && !isReask
    if (shouldUseWorkflowSession) {
      let resolvedWorkflowSessionId: string | undefined
      if (explicitWorkFlowSessionId) {
        resolvedWorkflowSessionId = explicitWorkFlowSessionId
      }
      else if (lastLoadedQaWorkflowContextRef.current?.workFlowSessionId) {
        resolvedWorkflowSessionId = lastLoadedQaWorkflowContextRef.current.workFlowSessionId
      }
      else if (!isAutoQualityImprovement) {
        for (let i = allItems.length - 1; i >= 0; i--) {
          const item = allItems[i]
          const fromHistory = item.role === 'ai' ? resolveWorkFlowSessionId(item.answerList?.[0]) : undefined
          if (fromHistory) {
            resolvedWorkflowSessionId = fromHistory
            break
          }
        }
      }
      if (resolvedWorkflowSessionId) {
        requestBody.workFlowSessionId = resolvedWorkflowSessionId
      }
    }

    const hasSessionTool = Boolean(resolvedToolId && String(resolvedToolId).trim())
    requestBody.toolId = hasSessionTool ? (submitPairFromList.toolId || resolvedToolId) : ''

    if (workflowExtraParams?.recordType) {
      requestBody.recordType = workflowExtraParams.recordType
      if (workflowExtraParams?.busiType)
        requestBody.busiType = workflowExtraParams.busiType
    }
    else {
      requestBody.recordType = submitPairFromList.recordType || '01'
      requestBody.busiType = workflowExtraParams?.busiType ?? extraParams?.busiType ?? '01'
    }

    fetchStreamResponse(
      fetchUrl,
      requestBody,
      (msg) => {
        // 检查是否已被取消
        if (abortControllerRef.current?.signal.aborted) {
          return
        }

        // 处理错误
        if (msg?.type === 'ERROR') {
          // 出错时需要重置 isAsking 状态
          dispatch(setIsAsking(false))
          // 如果是 AbortError，不显示错误
          if (msg.content?.name === 'AbortError') {
            return
          }
          // 设置最后一条 AI 回答的 endAnswerFlag，用于停止打字效果
          setAllItems((prevItems) => {
            const newItems = [...prevItems]
            // 从后往前找到最后一个 AI 项
            for (let i = newItems.length - 1; i >= 0; i--) {
              if (newItems[i].role === 'ai' && newItems[i].answerList?.[0]) {
                const prevAnswer = newItems[i].answerList[0].answer
                newItems[i] = {
                  ...newItems[i],
                  answerList: [
                    {
                      ...newItems[i].answerList[0],
                      endAnswerFlag: true,
                      // 兜底：如果流结束但 answer 仍为空字符串，避免一直显示加载态
                      answer: prevAnswer === '' ? '暂无内容' : prevAnswer,
                    },
                  ],
                }
                break
              }
            }
            return newItems
          })
          return
        }

        // 正常的stream数据
        if (msg?.type === 'DATA' && msg?.content?.code === '00000000') {
          console.log('========================流式输出1111====================', msg)
          console.log('========================流式输出字符串====================', msg.content?.data)
          handleStreamMesageData(msg, question)
        }
        if (msg?.type === 'DATA' && msg?.content?.code === '01010005') {
          handleChatMaxCount(msg, question)
        }
      },
      abortControllerRef.current.signal,
    )
      .then(() => {
        // 流结束时执行（通过 Promise resolve 判断）
        // 如果是提质增效自动调用且本轮存在有效欢迎语候选，将其写入 sessionStorage 供欢迎语使用
        if (isAutoSubmitQualityImprovementRef.current && hasAutoWelcomeCandidateRef.current) {
          try {
            const key = 'toolStreamWelcomeText'
            sessionStorage.setItem(key, autoWelcomeCandidateRef.current)
          }
          catch (error) {
            console.error('[Chat] 写入提质增效欢迎语到 sessionStorage 失败:', error)
          }
        }
        // 清除自动调用标记
        isAutoSubmitQualityImprovementRef.current = false
        autoWelcomeCandidateRef.current = ''
        hasAutoWelcomeCandidateRef.current = false
        const streamSnapshotForWf = lastStreamedAiAnswerRef.current
        let workflowLastAnswer: WorkflowStreamAnswer | null = null
        setAllItems((prevItems) => {
          const newItems = [...prevItems]
          const streamed = lastStreamedAiAnswerRef.current
          const streamingGroupId = activeStreamingAiGroupIdRef.current
          let mergeAiIndex = -1
          if (streamingGroupId) {
            mergeAiIndex = newItems.findIndex(
              item => item.role === 'ai' && item.groupId === streamingGroupId,
            )
          }
          if (mergeAiIndex < 0)
            mergeAiIndex = findLastAiIndex(newItems)
          if (mergeAiIndex >= 0 && newItems[mergeAiIndex].role === 'ai' && newItems[mergeAiIndex].answerList?.[0]) {
            const i = mergeAiIndex
            const base = newItems[i].answerList![0]
              const mergedCardList = streamed?.cardList ?? base.cardList
              const hasCards = Array.isArray(mergedCardList) && mergedCardList.length > 0
              const rawParseText = streamed?.workflowParseText ?? streamed?.answer ?? base.answer ?? ''
              const streamParseOptions = buildWorkflowParseOptions(streamed)
              const packetQuestion = (streamed as WorkflowStreamAnswer & { question?: string })?.question
              const isHiddenRedirect = isHiddenWorkflowRedirectControl(rawParseText, streamParseOptions)
              const parsedCtrl = parseWorkflowControlForStream(rawParseText, streamParseOptions, packetQuestion)
              const isEnterSub = isWorkflowEnterSubPayload(parsedCtrl)
              const displayAnswer = resolveDisplayAnswerText(rawParseText, streamParseOptions, packetQuestion)
              const merged: Answer = streamed
                ? {
                    ...base,
                    ...streamed,
                    answer: isHiddenRedirect
                      ? ''
                      : (displayAnswer === '' && !hasCards && !isEnterSub ? '暂无内容' : displayAnswer),
                    workFlowSessionId: resolveWorkFlowSessionId(streamed, resolveWorkFlowSessionId(base)),
                    recordId: streamed.recordId ?? base.recordId,
                    recordType: streamed.recordType ?? base.recordType,
                    cardList: mergedCardList,
                    endAnswerFlag: true,
                    workflowParseText: rawParseText,
                    // 含工作流表单卡片时保持 Parser，便于流式过程中展示表单；其余 cardList 流结束后走 Shower
                    isShow: isHiddenRedirect || (hasCards && hasWorkflowFormCards(mergedCardList))
                      ? false
                      : (hasCards && !base.firstQaFlag && !streamed?.firstQaFlag ? true : base.isShow),
                  }
                : {
                    ...base,
                    endAnswerFlag: true,
                    answer: base.answer === '' ? '暂无内容' : base.answer,
                  }
              workflowLastAnswer = {
                ...merged,
                workflowParseText: rawParseText,
                workflowAnswerChunk: streamed?.workflowAnswerChunk,
                workflowPacketEnd: streamed?.workflowPacketEnd,
                workflowDetectedControl: parsedCtrl ?? streamed?.workflowDetectedControl,
              }
              if (isHiddenRedirect && !hasCards) {
                newItems.splice(i, 1)
              }
              else {
                newItems[i] = { ...newItems[i], answerList: [merged] }
              }
          }
          if (!workflowLastAnswer && streamed) {
            workflowLastAnswer = {
              ...streamed,
              answer: streamed.answer || '暂无内容',
              isShow: false,
              cardList: streamed.cardList ?? [],
              endAnswerFlag: true,
            }
          }
          const formSourceKey = pendingWorkflowFormSourceKeyRef.current
          if (formSourceKey)
            return markWorkflowFormSubmittedOnRecord(newItems, formSourceKey)
          return newItems
        })
        pendingWorkflowFormSourceKeyRef.current = null
        dispatch(setIsAsking(false))
        workflowSilentAppendAiRef.current = false
        const wfParseOpts = buildWorkflowParseOptions(streamSnapshotForWf)
        const wfPacketQuestion = (streamSnapshotForWf as WorkflowStreamAnswer & { question?: string })?.question
        const wfRawParseText = trimSpacesOnly(
          streamSnapshotForWf?.workflowParseText
          ?? streamSnapshotForWf?.answer
          ?? '',
        )
        const wfParsedCtrl = parseWorkflowControlForStream(wfRawParseText, wfParseOpts, wfPacketQuestion)
        const wfCtxRecordType = wfParsedCtrl?.recordType != null
          ? String(wfParsedCtrl.recordType).trim()
          : (streamSnapshotForWf?.recordType != null ? String(streamSnapshotForWf.recordType).trim() : '')
        const wfCtxSessionId = resolveWorkFlowSessionId(streamSnapshotForWf)
        if (wfCtxRecordType && wfCtxSessionId) {
          lastLoadedQaWorkflowContextRef.current = {
            recordType: wfCtxRecordType,
            workFlowSessionId: wfCtxSessionId,
            busiType: streamSnapshotForWf?.busiType != null ? String(streamSnapshotForWf.busiType).trim() : '01',
          }
        }
        const wfAnswerForFollowUp: WorkflowStreamAnswer | null = workflowLastAnswer
          ?? (streamSnapshotForWf
            ? {
                ...streamSnapshotForWf,
                endAnswerFlag: true,
                workflowDetectedControl: wfParsedCtrl ?? streamSnapshotForWf.workflowDetectedControl,
              }
            : null)
        if (wfAnswerForFollowUp) {
          processBusinessInsightWorkflowAfterStream(wfAnswerForFollowUp)
        }
        else {
          console.warn('[Chat WF] 流结束未续问：无 workflow 快照', {
            parsed: wfParsedCtrl,
            hasRef: Boolean(streamSnapshotForWf),
          })
        }
        lastStreamedAiAnswerRef.current = null
        activeStreamingAiGroupIdRef.current = null
        if (isNew) {
          setTimeout(() => {
            dispatch(fetchConversations())
          }, 2000)
        }
      })
      .catch(() => {
        // 流式请求出错时的处理
        console.error('[Chat] 流式请求失败:')
        // 清除自动调用标记
        isAutoSubmitQualityImprovementRef.current = false
        // 设置最后一条 AI 回答的 endAnswerFlag，用于停止打字效果
        setAllItems((prevItems) => {
          const newItems = [...prevItems]
          // 从后往前找到最后一个 AI 项
          for (let i = newItems.length - 1; i >= 0; i--) {
            if (newItems[i].role === 'ai' && newItems[i].answerList?.[0]) {
              const prevAnswer = newItems[i].answerList[0].answer
              newItems[i] = {
                ...newItems[i],
                answerList: [
                  {
                    ...newItems[i].answerList[0],
                    endAnswerFlag: true,
                    answer: prevAnswer === '' ? '暂无内容' : prevAnswer,
                  },
                ],
              }
              break
            }
          }
          return newItems
        })
        dispatch(setIsAsking(false))
        workflowSilentAppendAiRef.current = false
        pendingWorkflowFormSourceKeyRef.current = null
        lastStreamedAiAnswerRef.current = null
        activeStreamingAiGroupIdRef.current = null
      })
  }

  handleSubmitQuestionRef.current = handleSubmitQuestion

  /** c.txt：拉 QA 后无 workflow 上下文则静默首包；制度活化（无 toolId）与有 tool 一致 */
  const runWorkflowSilentBootstrapIfNeeded = (
    conversationId: string,
    sessionToolId?: string | null,
    qaRecords?: OriginalRecord[] | null,
  ) => {
    if (workflowBootstrapDoneRef.current === conversationId)
      return
    const qaList = Array.isArray(qaRecords) ? qaRecords : []
    if (hasQaWorkflowContext(qaList))
      return
    const wf = lastLoadedQaWorkflowContextRef.current
    if (wf?.recordType && wf?.workFlowSessionId)
      return
    const pair = resolveSubmitQuestionToolPair(sessionToolId ?? '')
    if (!pair.recordType)
      return
    workflowBootstrapDoneRef.current = conversationId
    queueMicrotask(() => {
      if (currentIdRef.current !== conversationId)
        return
      handleSubmitQuestion('', undefined, sessionToolId || undefined, {
        recordType: pair.recordType,
        busiType: '01',
        omitWorkFlowSessionId: true,
      })
    })
  }

  const consumePendingWorkflowSilentBootstrap = (conversationId: string) => {
    try {
      const raw = sessionStorage.getItem(PENDING_WORKFLOW_SILENT_BOOTSTRAP_KEY)
      if (!raw)
        return
      const pending = JSON.parse(raw) as {
        conversationId?: string
        toolId?: string
        recordType?: string
        busiType?: string
      }
      if (pending.conversationId !== conversationId)
        return
      sessionStorage.removeItem(PENDING_WORKFLOW_SILENT_BOOTSTRAP_KEY)
      runWorkflowSilentBootstrapIfNeeded(
        conversationId,
        pending.toolId ?? '',
        null,
      )
    }
    catch {
      sessionStorage.removeItem(PENDING_WORKFLOW_SILENT_BOOTSTRAP_KEY)
    }
  }

  /** 处理 workflow 表单提交：paramMap 提交不传 question */
  const handleWorkflowSubmit = useCallback(async (paramMap: WorkflowParamMap, recordId?: string, workFlowSessionId?: string) => {
    dispatch(updateBusinessInsightParamMap(paramMap))
    if (businessInsightWorkflowRef.current.isInSubWorkflow) {
      businessInsightWorkflowRef.current = {
        ...businessInsightWorkflowRef.current,
        paramMap: { ...businessInsightWorkflowRef.current.paramMap, ...paramMap },
      }
    }
    let sourceKey = recordId
    if (!sourceKey) {
      for (let i = allItems.length - 1; i >= 0; i--) {
        const item = allItems[i]
        if (item.role === 'ai' && hasWorkflowFormCards(item.answerList?.[0]?.cardList)) {
          sourceKey = item.answerList?.[0]?.recordId || item.groupId
          break
        }
      }
    }
    pendingWorkflowFormSourceKeyRef.current = sourceKey != null ? String(sourceKey) : null
    const recordType = businessInsightWorkflowRef.current.subWorkflowRecordType?.trim() || 'A01'
    await handleSubmitQuestion('', undefined, undefined, {
      paramMap,
      recordId,
      workFlowSessionId,
      recordType,
      busiType: '01',
      submitViaParamMap: true,
    })
  }, [allItems, dispatch])

  /** 最后一条 AI 含未提交的工作流表单时，禁用底部输入框 */
  const disableChatEditorForWorkflowForm = useMemo(() => {
    for (let i = allItems.length - 1; i >= 0; i--) {
      const item = allItems[i]
      if (item.role !== 'ai')
        continue
      const answer = item.answerList?.[0]
      if (!answer)
        return false
      return hasWorkflowFormCards(answer.cardList) && !answer.workflowFormSubmitted
    }
    return false
  }, [allItems])

  /** 获取qa记录 */
  const getUserQaRecordPage = useCallback(async (conversationId: string, toolId?: string) => {
    // 检查是否正在处理相同的 conversationId，避免重复调用
    if (processingConversationIdRef.current === conversationId) {
      return
    }
    setIsLoading(true)
    // 标记正在处理
    processingConversationIdRef.current = conversationId
    // 定义 finalConversationId 在 try 块外，以便 finally 块可以访问
    let finalConversationId = conversationId
    try {
      // 检测是否从收藏页返回（优先使用 ref 中保存的值，避免 location.state 被清除后丢失）
      // 增加 sessionStorage 检查，用于 navigate(-1) 的情况
      const fromCollect = fromCollectRef.current || Boolean(location.state?.fromCollect) || Boolean(sessionStorage.getItem('fromCollect'))
      // 如果从收藏页返回，先调用 get_user_conversation_session 获取新的会话ID
      if (fromCollect && !fromCollectProcessedRef.current) {
        fromCollectProcessedRef.current = true
        const sessionToolId = safeSessionStorageGetItem('currentToolId') || ''
        try {
          await waitForToken()
          const sessionRes = await fetchSessionConversationId({ busiId: sessionToolId })
          if (sessionRes?.data?.conversationId) {
            finalConversationId = sessionRes.data.conversationId
            // 如果获取到新会话ID，设置标记避免路由变化时重复调用
            toolHistoryLoadedRef.current.conversationId = finalConversationId
            // 更新 processingConversationIdRef 为新会话ID，避免使用旧ID重复调用
            processingConversationIdRef.current = finalConversationId
          }
        }
        catch (error) {
          console.error('[Chat] 从收藏返回 - 获取会话ID失败:', error)
          // 如果获取失败，继续使用原来的 conversationId
        }
      }

      // 如果从收藏页返回，触发事件通知 HomeNew 刷新问题列表
      if (fromCollect) {
        const eventToolId = toolId || currentToolId || safeSessionStorageGetItem('currentToolId') || ''
        // 清除 sessionStorage 中的标记，避免影响后续路由
        sessionStorage.removeItem('fromCollect')
        // 延迟触发事件，确保 Home 组件的监听器已注册
        setTimeout(() => {
          window.dispatchEvent(new CustomEvent('refreshQuestionsFromCollect', {
            detail: {
              toolId: eventToolId,
            },
          }))
        }, 100)
      }
      await waitForToken()
      const res = await fetchUserQaRecordPage(finalConversationId, toolId || safeSessionStorageGetItem('currentToolId') || '')
      const qaRecords = res.data || []
      applyLoadedQaWorkflowContext(qaRecords)
      const messages = [{ role: 'system' } as ChatRecord, ...processApiResponse(qaRecords)]
      // 处理历史记录中的参考文档标记
      const processedMessages = messages.map((item) => {
        if (item.role === 'ai' && item.answerList?.[0]?.answer) {
          return {
            ...item,
            answerList: item.answerList.map(answerItem => ({
              ...answerItem,
              answer: answerItem.answer
                // 移除括号内容，但保留 Markdown 链接语法中的 ](...)
                ?.replace(/\([^)]*\)/g, (match, offset, str) => (str[offset - 1] === ']' ? match : ''))
                .replace(/\[参考文档(?:《[^》]*》\s*)+\]/g, '')
                .trim(),
            })),
          }
        }
        return item
      })
      setAllItems(processedMessages)
      // 优先从 qaRecords 中查找 toolId（这是实际使用的）
      const latestToolId = [...qaRecords].reverse().find(item => Boolean(item.toolId))?.toolId?.trim?.()
      const hasQaRecords = qaRecords.length > 0
      // 如果 qaRecords 中没有 toolId，尝试从 conversations 中查找当前会话的 toolId（会话级别）
      const conversationToolId = latestToolId || (conversations.find(conv => conv.conversationId === finalConversationId)?.toolId?.trim?.())
        // 优先使用从 location.state 传递的 toolId（如果存在），这是从历史记录点击传递过来的
      const toolIdFromState = toolIdFromStateRef.current !== undefined
        ? (toolIdFromStateRef.current ? String(toolIdFromStateRef.current) : null)
        : undefined

      // 确定最终使用的 toolId：优先使用从 location.state 传递的，其次使用 qaRecords 中的，最后使用 conversation 中的
      // 如果从 location.state 传递了 toolId，直接使用它（最高优先级）
      if (toolIdFromState !== undefined) {
        if (toolIdFromState) {
          // 只有当 Redux 中的 toolId 与最终确定的 toolId 不一致时，才更新
          if (currentToolId !== toolIdFromState) {
            dispatch(setCurrentToolId(toolIdFromState))
          }
          // 无条件同步到 sessionStorage，确保 ChatEditor 和 handleSubmitQuestion 能获取到正确的 toolId
          sessionStorage.setItem('currentToolId', toolIdFromState)
        }
        else {
          // 如果从 location.state 传递的是 null，清除 toolId
          if (currentToolId) {
            dispatch(clearCurrentToolId())
          }
          // 清除 sessionStorage，确保制度活化模式正确
          sessionStorage.removeItem('currentToolId')
        }
        // 清除 ref，避免下次路由变化时误用
        toolIdFromStateRef.current = undefined
      }
      else {
        // 如果没有从 location.state 传递 toolId（可能是嵌套路由传递失败），使用原来的逻辑
        // 优先使用 qaRecords 中的 toolId，其次使用 conversation 中的 toolId
        const finalToolId = latestToolId || conversationToolId || undefined

        if (finalToolId) {
          // 只有当 Redux 中的 toolId 与最终确定的 toolId 不一致时，才更新
          if (currentToolId !== finalToolId) {
            dispatch(setCurrentToolId(finalToolId))
            // 从收藏返回时，同步到 sessionStorage，避免 ChatEditor 清除 toolId
            if (fromCollect) {
              sessionStorage.setItem('currentToolId', finalToolId)
            }
          }
          else {
            // 从收藏返回时，确保 sessionStorage 中有值
            if (fromCollect && !sessionStorage.getItem('currentToolId')) {
              sessionStorage.setItem('currentToolId', finalToolId)
            }
          }
        }
        else {
          // 如果 qaRecords 和 conversation 中都没有 toolId
          // 如果有历史记录但没有 toolId，说明是制度活化，应该清除
          if (hasQaRecords && currentToolId) {
            dispatch(clearCurrentToolId())
          }
          // 如果没有历史记录，可能是新会话，但如果 Redux 中已经有 toolId（从 HistoryBarList 设置的），暂时保留
          // 因为可能是刚点击历史记录但 API 还没返回，或者 location.state 传递失败但 Redux 中已有正确的值
          else if (!hasQaRecords && currentToolId) {
            if (fromCollect && !sessionStorage.getItem('currentToolId')) {
              sessionStorage.setItem('currentToolId', currentToolId)
            }
          }
        }
      }
    }
    catch {
      lastLoadedQaWorkflowContextRef.current = null
    }
    finally {
      setIsLoading(false)
      // 标记该会话的历史记录已加载完成
      historyLoadedRef.current.conversationId = finalConversationId
      // finalConversationId 可能是新会话ID（从收藏返回时）或原来的 conversationId
      if (processingConversationIdRef.current === finalConversationId) {
        processingConversationIdRef.current = null
      }
      // 如果历史记录加载完成且有待发送的问题，触发自动发送
      if (pendingQuestionRef.current && finalConversationId === currentIdRef.current) {
        const questionToSend = pendingQuestionRef.current
        // 检查是否已经发送过相同的问题
        if (questionToSend !== lastSentQuestionRef.current) {
          lastSentQuestionRef.current = questionToSend
          pendingQuestionRef.current = null
          // 清除 shouldSendQuestion（如果还存在）
          dispatch(clearShouldSendQuestion())
          // 延迟发送，确保状态已更新
          setTimeout(() => {
            // 左侧常见问题自动发送：若 QA 列表最后一条带 recordType + workFlowSessionId，一并透传
            const autoExtra = buildWorkflowContinueExtraFromRef()
            handleSubmitQuestion(questionToSend, undefined, currentToolId, autoExtra)
          }, 100)
        }
      }
    }
  }, [dispatch, currentToolId, conversations, location.state])

  /** 点击滚动到底部 */
  const scrollToBottom = () => {
    scrollableRef.current.scrollTo(scrollableRef.current.scrollHeight, { behavior: 'smooth' })
  }

  useEffect(() => {
    if (id) {
      // 检查是否已通过 toolHistoryLoaded 事件加载过相同会话的数据
      // 如果已加载，跳过接口调用，避免重复请求
      if (toolHistoryLoadedRef.current.conversationId === id) {
        // 已加载，清除标记，避免影响后续正常的路由切换
        toolHistoryLoadedRef.current.conversationId = null
        // 更新 currentIdRef，确保状态一致
        currentIdRef.current = id
        return
      }

      // 检查是否正在通过工具切换加载历史记录（ChatEditor 正在处理）
      // 通过 sessionStorage 标记判断，避免时序问题导致的重复调用
      const toolHistoryLoadingId = sessionStorage.getItem('toolHistoryLoading')
      if (toolHistoryLoadingId === id) {
        currentIdRef.current = id
        workflowBootstrapDoneRef.current = null
        // 更新 toolId 相关状态（如果有）
        if (initialToolId !== undefined) {
          toolIdFromStateRef.current = initialToolId
        }
        return
      }

      // 检查是否跳过历史记录加载（点击常见问题时）
      const skipHistoryLoad = Boolean((location.state as { skipHistoryLoad?: boolean } | null)?.skipHistoryLoad)
      if (skipHistoryLoad) {
        lastLoadedQaWorkflowContextRef.current = null
        setAllItems(prev => (prev.length > 0 ? prev : ([{ role: 'system' } as ChatRecord])))
        currentIdRef.current = id
        // 更新 toolId 相关状态（如果有）
        if (initialToolId !== undefined) {
          toolIdFromStateRef.current = initialToolId
        }
        return
      }

      // 如果 id 没有变化，只是 location.state 变化，需要判断是否需要重新加载历史记录
      if (currentIdRef.current === id) {
        // 检查是否从收藏页返回（需要重新加载历史记录）
        const fromCollect = Boolean(location.state?.fromCollect) || Boolean(sessionStorage.getItem('fromCollect'))
        // 如果 allItems 为空，说明是首次加载，需要加载历史记录
        if (!fromCollect && allItems.length > 0) {
          return
        }
      }

      // 停止之前的请求
      if (abortControllerRef.current) {
        abortControllerRef.current.abort()
        dispatch(setIsAsking(false))
      }
      currentIdRef.current = id
      workflowBootstrapDoneRef.current = null
      lastSentQuestionRef.current = '' // 重置标记
      pendingQuestionRef.current = null // 清除待发送的问题
      // 重置历史记录加载标记，确保新会话时能正确触发自动发送
      historyLoadedRef.current.conversationId = null
      lastLoadedQaWorkflowContextRef.current = null
      lastMainUserQuestionRef.current = null
      // 保存 fromCollect 标记到 ref，避免 location.state 被清除后丢失
      // 优先使用 location.state，其次使用 sessionStorage（用于 navigate(-1) 的情况）
      fromCollectRef.current = Boolean(location.state?.fromCollect) || Boolean(sessionStorage.getItem('fromCollect'))
      // 重置 fromCollect 处理标记，确保每次路由变化时都能正确处理
      fromCollectProcessedRef.current = false
      // 清除处理标记，允许新的 conversationId 处理
      processingConversationIdRef.current = null

      // 等待 token 就绪后再调用接口
      waitForToken().then(() => {
        getUserQaRecordPage(id)
      })
    }
  }, [id, location.state])

  // 工具切换后若 toolHistoryLoaded 未送达（如页面晚挂载），消费 pending 触发静默首包
  useEffect(() => {
    if (!id)
      return
    const timer = window.setTimeout(() => {
      if (
        toolHistoryLoadedRef.current.conversationId === id
        || historyLoadedRef.current.conversationId === id
      ) {
        return
      }
      consumePendingWorkflowSilentBootstrap(id)
    }, 250)
    return () => window.clearTimeout(timer)
  }, [id])

  // 处理shouldSendQuestion的变化 - 自动发送问题
  useEffect(() => {
    if (
      shouldSendQuestion
      && currentIdRef.current
      && !isLoading
      && shouldSendQuestion !== lastSentQuestionRef.current
    ) {
      // 如果当前会话已有历史记录（allItems.length > 1），需要等待历史记录加载完成
      // 如果历史记录已加载完成（historyLoadedRef.current.conversationId === currentIdRef.current），可以立即发送
      // 如果是新会话（allItems.length <= 1），可以立即发送
      const canSend = historyLoadedRef.current.conversationId === currentIdRef.current || allItems.length <= 1
      if (canSend) {
        const questionToSend = shouldSendQuestion
        lastSentQuestionRef.current = questionToSend
        pendingQuestionRef.current = null
        // 立即清除shouldSendQuestion，防止重复发送
        dispatch(clearShouldSendQuestion())
        // 确保历史记录加载完成后再发送问题
        setTimeout(() => {
          const autoExtra = buildWorkflowContinueExtraFromRef()
          handleSubmitQuestion(questionToSend, undefined, currentToolId, autoExtra)
        }, 100)
      }
      else {
        // 如果历史记录还在加载中，保存待发送的问题，等待历史记录加载完成后再发送
        pendingQuestionRef.current = shouldSendQuestion
      }
    }
  }, [shouldSendQuestion, isLoading, allItems.length, currentToolId, id])

  // 根据 currentToolId 获取对应的 toolName
  useEffect(() => {
    const getToolNameFromToolId = async () => {
      if (currentToolId) {
        try {
          const cachedRow = findToolRowBySessionToolId(readToolListFromSessionStorage(), currentToolId)
          if (cachedRow?.toolName?.trim()) {
            setCurrentToolName(cachedRow.toolName.trim())
            return
          }
          // 兼容旧缓存键
          const userRoles = getUserRolesForApi()
          const legacyKey = `toolList_${JSON.stringify(userRoles)}`
          const cached = sessionStorage.getItem(legacyKey)

          if (cached) {
            try {
              const { toolList, timestamp } = JSON.parse(cached)
              if (Date.now() - timestamp < 5 * 60 * 1000) {
                const tool = toolList.find((t: any) => String(t.toolId) === String(currentToolId))
                if (tool?.toolName) {
                  setCurrentToolName(String(tool.toolName).trim())
                }
              }
            }
            catch {
              // 缓存解析失败，继续调用接口
            }
          }
        }
        catch (error) {
          console.error('获取工具名称失败:', error)
        }
      }
      else {
        const list = readToolListFromSessionStorage()
        const first = getFirstToolListRow(list)
        setCurrentToolName(first?.toolName?.trim() || undefined)
      }
    }
    getToolNameFromToolId()
  }, [currentToolId])

  // 监听工具按钮点击事件，更新 ChatWelcome 提示语和 toolId
  useEffect(() => {
    const handleToolClickEvent = (event: CustomEvent) => {
      const { isToolBtn, toolId, toolName } = event.detail
      // 保存当前选择的 toolName
      setCurrentToolName(toolName)
      // 保存当前选择的 toolId 到 Redux
      if (!isToolBtn && toolId) {
        // 提质增效模式，保存 toolId
        dispatch(setCurrentToolId(toolId))
      }
      else {
        // 制度活化，清除 toolId
        dispatch(clearCurrentToolId())
      }
      // 历史记录加载已由 ChatEditor 通过 toolHistoryLoaded 事件处理，此处不再重复调用
    }
    window.addEventListener('toolButtonClick', handleToolClickEvent as EventListener)
    return () => {
      window.removeEventListener('toolButtonClick', handleToolClickEvent as EventListener)
    }
  }, [dispatch])

  // 监听工具历史记录加载事件，渲染历史会话
  useEffect(() => {
    const handleToolHistoryLoaded = (event: CustomEvent) => {
      const { conversationId, toolId, toolName, qaRecords } = event.detail
      const qaList = Array.isArray(qaRecords) ? qaRecords : []
      setIsLoading(true)
      try {
        applyLoadedQaWorkflowContext(qaList)
        // 标记已通过事件加载数据，避免 useEffect 重复调用接口
        toolHistoryLoadedRef.current.conversationId = conversationId
        // 更新当前会话ID
        currentIdRef.current = conversationId
        // 处理历史记录数据
        const messages = [{ role: 'system' } as ChatRecord, ...processApiResponse(qaList)]
        // 处理历史记录中的参考文档标记
        const processedMessages = messages.map((item) => {
          if (item.role === 'ai' && item.answerList?.[0]?.answer) {
            return {
              ...item,
              answerList: item.answerList.map(answerItem => ({
                ...answerItem,
                answer: answerItem.answer
                  // 移除括号内容，但保留 Markdown 链接语法中的 ](...)
                  ?.replace(/\([^)]*\)/g, (match, offset, str) => (str[offset - 1] === ']' ? match : ''))
                  .replace(/\[参考文档(?:《[^》]*》\s*)+\]/g, '')
                  .trim(),
              })),
            }
          }
          return item
        })
        setAllItems(processedMessages)
        // 更新 toolName
        if (toolName) {
          setCurrentToolName(toolName)
        }
        // 更新 toolId
        if (toolId) {
          dispatch(setCurrentToolId(toolId))
        }
      }
      catch (error) {
        console.error('处理历史记录失败:', error)
        lastLoadedQaWorkflowContextRef.current = null
      }
      finally {
        setIsLoading(false)
        // 标记该会话的历史记录已加载完成（通过工具切换事件加载）
        historyLoadedRef.current.conversationId = conversationId
        // 清除 toolHistoryLoading 标记，避免影响后续路由切换
        const toolHistoryLoadingId = sessionStorage.getItem('toolHistoryLoading')
        if (toolHistoryLoadingId === conversationId) {
          sessionStorage.removeItem('toolHistoryLoading')
        }
        sessionStorage.removeItem(PENDING_WORKFLOW_SILENT_BOOTSTRAP_KEY)
        runWorkflowSilentBootstrapIfNeeded(conversationId, toolId ?? '', qaList)
      }
    }
    window.addEventListener('toolHistoryLoaded', handleToolHistoryLoaded as EventListener)
    return () => {
      window.removeEventListener('toolHistoryLoaded', handleToolHistoryLoaded as EventListener)
    }
  }, [dispatch])

  // 监听自动提问事件（左侧新建会话 / 工具首包等静默提问）
  useEffect(() => {
    const handleAutoSubmitQuestion = (event: CustomEvent) => {
      const { conversationId, toolId, recordType, busiType } = event.detail
      // 确保当前会话ID匹配
      if (conversationId === currentIdRef.current) {
        runWorkflowSilentBootstrapIfNeeded(conversationId, toolId ?? '', null)
      }
      else {
        try {
          sessionStorage.setItem(PENDING_WORKFLOW_SILENT_BOOTSTRAP_KEY, JSON.stringify({
            conversationId,
            toolId: toolId ?? '',
            recordType,
            busiType: busiType ?? '01',
          }))
        }
        catch {
          // ignore
        }
      }
    }
    window.addEventListener('autoSubmitQuestion', handleAutoSubmitQuestion as EventListener)
    return () => {
      window.removeEventListener('autoSubmitQuestion', handleAutoSubmitQuestion as EventListener)
    }
  }, [handleSubmitQuestion])

  return (
    <div className={styles.scrollView}>
      <div className={`${styles.chatPage} relative`}>
        <div className={`${styles.content}`}>
          {isLoading && (
            <div className="w-full h-full flex justify-center items-center">
              <SdreamLoading />
            </div>
          )}
          {!isLoading && (
            <motion.div
              ref={scrollableRef}
              initial={{ opacity: 0, y: -10 }}
              animate={{ opacity: 1, y: 0 }}
              transition={{
                duration: 0.3,
                opacity: { duration: 0.1 },
              }}
              className={`${styles.scrollable} scrollbar-hide scroll-smooth`}
            >
              <div className={styles.inter}>
                {allItems.map((record, index) => {
                  const recordId = record.answerList?.[0]?.recordId || record.groupId
                  const uniqueKey = recordId
                    ? `${record.role}-${recordId}`
                    : `${record.role}-${index}`

                  const questionText = typeof record.question === 'string' ? record.question.trim() : ''
                  const firstAnswer = record.answerList?.[0]
                  const hasAnswer = typeof firstAnswer?.answer === 'string' && firstAnswer.answer.trim().length > 0
                  const hasCardList = Array.isArray(firstAnswer?.cardList) && firstAnswer!.cardList.length > 0
                  const hasQuestion = questionText.length > 0
                  // firstQaFlag 且无 cardList：隐藏 UI 但保留挂载（静默自动提问）；有 cardList 时必须展示表单/卡片
                  const shouldHideHistoryItem = record.role === 'ai'
                    && firstAnswer?.firstQaFlag === true
                    && !hasCardList
                  // 静默提问（空问题流式占位且尚无内容）：隐藏占位
                  const shouldHideEmptyQuestionAnswer = record.role === 'ai'
                    && !hasQuestion
                    && firstAnswer?.isShow === false
                    && !hasAnswer
                    && !hasCardList
                  const shouldForceShow = record.role === 'ai'
                    && (hasCardList || (hasQuestion && (hasAnswer || hasCardList)))

                  return (
                  <div
                    className="w-full chatItem mx-auto"
                    key={uniqueKey}
                    style={(!shouldForceShow && (shouldHideHistoryItem || shouldHideEmptyQuestionAnswer)) ? { visibility: 'hidden', height: 0, overflow: 'hidden', margin: 0, padding: 0 } : undefined}
                  >
                    {record.role === 'system' && <ChatWelcome toolName={currentToolName} welcomeText={(record as any).welcomeText} />}
                    {record.role === 'user' && <ChatItemUser record={record} />}
                    {record.role === 'ai' && (
                      <ChatAnswerBox
                        onSubmitQuestion={handleSubmitQuestion}
                        isLastAnswer={index === allItems.length - 1}
                        showIndex={0}
                        record={record}
                        index={index}
                        onWorkflowSubmit={handleWorkflowSubmit}
                        isWorkflowSubmitting={isAsking}
                        allItems={allItems}
                      />
                    )}
                  </div>
                  )
                })}
              </div>
            </motion.div>
          )}
        </div>
        <div className="relative box-border px-[0] mx-auto iptContainer w-full flex-shrink-0 sm:px-0 pb-[18px]">
          <div className="absolute left-1/2 ml-[-20px] top-[-45px] sm:top-[-65px]">
            <motion.div
              initial="hidden"
              animate={(position?.top as number) < -20 ? 'visible' : 'hidden'}
              variants={{
                hidden: { opacity: 0, y: 20, pointerEvents: 'none' as const },
                visible: { opacity: 1, y: 0, pointerEvents: 'auto' as const },
              }}
              transition={{ duration: 0.3, ease: 'easeInOut' }}
            >
              <Button onPress={scrollToBottom} radius="full" isIconOnly color="primary">
                <ScrollBtoIcon />
              </Button>
            </motion.div>
          </div>
          <ChatEditor
            inputDisabled={disableChatEditorForWorkflowForm}
            onSubmit={(question, toolId) => {
              const extra = buildWorkflowContinueExtraFromRef()
              handleSubmitQuestion(question, undefined, toolId, extra)
            }}
            onToolClick={(isToolBtn, toolId, toolName, shouldChangeStyle, conversationId, isLoading, toolMeta) => {
            // 如果传递了 loading 状态，更新 isLoading
            if (isLoading !== undefined) {
              setIsLoading(isLoading)
            }
            // 只在真正的工具切换时触发事件（有 conversationId 时），避免 loading 状态更新时重复触发
            if (conversationId) {
              window.dispatchEvent(new CustomEvent('toolButtonClick', {
                detail: { isToolBtn, toolId, toolName, shouldChangeStyle, conversationId, source: 'toolButton', toolMeta },
              }))
            }
          }}
            placeholders={[]}
          />
          <div className="hidden sm:block w-full text-center mt-[12px] text-[#3333334d] text-[12px]">
            内容由AI模型生成，其准确性和完整性无法保证，仅供参考
          </div>
        </div>
      </div>
    </div>
  )
}
