// 问答功能独立聊天页
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useLocation, useNavigate, useParams, useSearchParams } from 'react-router-dom'
import { useLocalStorageState, useScroll } from 'ahooks'
import { Button } from '@heroui/react'
import { motion } from 'framer-motion'
import styles from '../Chat/Chat.module.less'
import { processApiResponse } from '../Chat/helper'
import { ChatItemUser } from '../Chat/components/ChatItem/ChatItemUser'
import { ChatAnswerBox } from '../Chat/components/ChatItem/ChatAnswerBox'
import { trimSpacesOnly } from '../Chat/components/ChatItem/markdownFormatter'
import { TacticsWelcome } from './components/TacticsWelcome'
import DeleteIcon from '@/assets/svg/delete.svg?react'
import RefreshIcon from '@/assets/svg/refresh.svg?react'
import { TacticsChatEditor } from '@/components/TacticsChatEditor'
import type { ChatRecord } from '@/types/chat'
import { fetchDeleteTacticsConversation, fetchTacticsQaRecordPage } from '@/api/tactics'
import { fetchCheckTokenApi, fetchStreamResponse } from '@/api/chat'
import { clearTacticsNavigationFlag, clearTacticsShouldSendQuestion, createTacticsConversation, fetchTacticsConversations } from '@/store/tacticsSlice'
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'
import useToast from '@/hooks/useToast'
import { safeLocalStorageGetItem, waitForToken } from '@/lib/utils'

export const TacticsChat: React.FC = () => {
  const { id } = useParams<{ id: string }>()
  const location = useLocation()
  const navigate = useNavigate()
  const logFullUrl = useCallback((reason?: string) => {
    console.log('[TacticsChat] full url snapshot', {
      reason,
      href: window.location.href,
      pathname: location.pathname,
      search: location.search,
      state: location.state,
    })
  }, [location.pathname, location.search, location.state])
  useEffect(() => {
    logFullUrl('mount or location change')
  }, [logFullUrl])
  const [isLoading, setIsLoading] = useState(false)
  const [allItems, setAllItems] = useState<ChatRecord[]>([])
  const [historyDividers, setHistoryDividers] = useState<{ index: number, time: string }[]>([])
  const [hasCleared, setHasCleared] = useState(false)
  const [showClearConfirm, setShowClearConfirm] = useState(false)
  // 标记当前会话是否已有历史记录；null 表示尚未完成查询
  const [hasHistory, setHasHistory] = useState<boolean | null>(null)
  // 标记当前会话中最后一条 AI 回答的推荐问题是否仍在加载中（v1/query_recommend_question）
  const [isRecommendLoading, setIsRecommendLoading] = useState(false)
  const dispatch = useAppDispatch()
  const {
    shouldSendQuestion: shouldSendQuestionFromState,
    shouldNavigateToNewConversation,
    currentConversationId,
  } = useAppSelector((state: RootState) => state.tactics)
  const isAsking = useAppSelector((state: RootState) => state.chat.isAsking)
  const [searchParams] = useSearchParams()
  const showToast = useToast()
  // 使用 useLocalStorageState 管理 token，与原有逻辑保持一致
  const [token] = useLocalStorageState<string | undefined>('__TOKEN__', {
    defaultValue: '',
  })
  // 优先从 location.state 获取，其次从 Redux state 获取
  const shouldSendQuestion = (location.state as { shouldSendQuestion?: string } | null)?.shouldSendQuestion || shouldSendQuestionFromState
  const scrollableRef = useRef<HTMLDivElement | any>(null)
  const position = useScroll(scrollableRef)
  const currentIdRef = useRef<string | undefined>(id)
  // 标记当前会话是否由「清空记录」操作创建，用于控制是否需要自动调用提问接口
  const createdFromClearRef = useRef(false)
  const lastSentQuestionRef = useRef<string>('')
  const abortControllerRef = useRef<AbortController | null>(null)
  // 读取 from=tactics 的额外参数（直接从 query 读取，不再使用 sessionStorage）
  const tacticsMeta = useMemo(() => {
    const from = searchParams.get('from')
    if (from !== 'tactics') {
      return undefined
    }
    const taskId = searchParams.get('taskId') || undefined
    return {
      version: searchParams.get('version'),
      taskId,
      pinBeginTime: searchParams.get('pinBeginTime') || undefined,
      pinEndTime: searchParams.get('pinEndTime') || undefined,
      partOrAll: searchParams.get('partOrAll') || undefined,
      channel: searchParams.get('channel') || undefined,
      channelName: searchParams.get('channelName') || undefined,
      // from=tactics 场景下，新建会话需要的业务识别参数
      busiType: '02',
      busiId: taskId,
    }
  }, [searchParams])

  // 读取 place=user 时的 userMeta 参数（直接从 query 读取，不再使用 sessionStorage）
  const userMeta = useMemo(() => {
    const place = searchParams.get('place')
    if (place !== 'user') {
      return undefined
    }
    return {
      place,
      cstId: searchParams.get('cstId') || undefined,
      userId: searchParams.get('userId') || undefined,
      numberType: searchParams.get('numberType') || undefined,
    }
  }, [searchParams])

  // 读取 from=tactics&place=order 时的 orderMeta 参数（直接从 query 读取，不再使用 sessionStorage）
  const orderMeta = useMemo(() => {
    const from = searchParams.get('from')
    const place = searchParams.get('place')
    console.log('[TacticsChat] orderMeta useMemo:', {
      from,
      place,
      allSearchParams: Object.fromEntries(searchParams.entries()),
    })
    if (from !== 'tactics' || place !== 'order') {
      return undefined
    }
    // 打印完整链接
    console.log('[TacticsChat] from=tactics&place=order 完整链接:', window.location.href)
    const workOrderIds = searchParams.get('workOrderIds')
    const result = {
      workOrderIds: workOrderIds !== null ? workOrderIds : undefined,
    }
    console.log('[TacticsChat] orderMeta result:', result)
    // 如果 URL 中有 workOrderIds 参数（包括空字符串），则使用它；否则为 undefined
    return result
  }, [searchParams])

  // 仅用于创建会话的额外参数（create_conversation）
  const getConversationExtra = useCallback(() => {
    if (orderMeta) {
      console.log('orderMeta', orderMeta)
      // 从 localStorage 读取 userId
      const userIdFromStorage = safeLocalStorageGetItem('__USER_NAME__')
      let userId = ''
      if (userIdFromStorage) {
        try {
          // useLocalStorageState 会将值序列化为 JSON，需要解析
          const parsed = JSON.parse(userIdFromStorage)
          userId = parsed || userIdFromStorage
        }
        catch {
          // 如果不是 JSON 格式，直接使用原始值
          userId = userIdFromStorage
        }
      }
      return {
        busiType: '02',
        busiId: userId,
        workOrderIds: orderMeta.workOrderIds,
      } as Partial<any>
    }
    if (userMeta?.place === 'user') {
      return {
        busiType: '02',
        // place=user 场景下，业务方要求 busiId 取 userId:numberType，若numberType为空则只传userId
        busiId: userMeta.userId && userMeta.numberType
          ? `${userMeta.userId}:${userMeta.numberType}`
          : userMeta.userId || userMeta.numberType,
      } as Partial<any>
    }
    if (tacticsMeta) {
      return {
        busiType: '02',
        busiId: tacticsMeta.taskId,
      } as Partial<any>
    }
    return {} as Partial<any>
  }, [orderMeta, tacticsMeta, userMeta])

  // 根据 userMeta.numberType 与调用类型（自动/正常）计算请求参数用的 numberType（A03/A04...）
  const getNumberTypeWithUserMeta = useCallback(
    (defaultNumberType: string, isAuto: boolean): string => {
      const numberType = userMeta?.numberType
      if (!numberType)
        return defaultNumberType

      // numberType 仅在 3~6 时生效，其它保持默认
      if (isAuto) {
        // 自动调用
        switch (numberType) {
          case '3':
            return 'A03'
          case '4':
            return 'A05'
          case '5':
            return 'A07'
          case '6':
            return 'A09'
          default:
            return defaultNumberType
        }
      }
      // 正常问答
      switch (numberType) {
        case '3':
          return 'A04'
        case '4':
          return 'A06'
        case '5':
          return 'A08'
        case '6':
          return 'A10'
        default:
          return defaultNumberType
      }
    },
    [userMeta],
  )

  const formatCurrentTime = () => {
    const date = new Date()
    const pad = (value: number) => value.toString().padStart(2, '0')
    return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`
  }

  /** 处理正常stream的数据 */
  const handleStreamMesageData = (msg: any, question: string) => {
    setAllItems((prevItems) => {
      const newItems = [...prevItems] // 创建数组的浅拷贝
      // 从后往前找到最后一个 AI 项
      let lastAiIndex = -1
      for (let i = newItems.length - 1; i >= 0; i--) {
        if (newItems[i].role === 'ai') {
          lastAiIndex = i
          break
        }
      }

      console.log('[TacticsChat] handleStreamMesageData', {
        lastAiIndex,
        itemsLength: newItems.length,
        msgData: msg.content?.data,
        prevItems: prevItems.map(item => ({ role: item.role, hasAnswer: !!item.answerList?.[0]?.answer })),
      })
      if (lastAiIndex >= 0) {
        // 确保 answer 字段存在，如果不存在则使用空字符串，null 替换为 \n
        const newAnswer = msg.content?.data?.answer ?? '\n'
        // 创建最后一项的新对象，合并现有数据和新的 answer
        const existingAnswerText = newItems[lastAiIndex].answerList?.[0]?.answer || ''
        const originalAnswer = existingAnswerText + newAnswer
        // 移除 ♪ 符号之后的所有文本（与历史记录保持一致）
        let filteredAnswer = originalAnswer.split('♪')[0]
        // 移除所有括号及其内容
        filteredAnswer = filteredAnswer.replace(/\([^)]*\)/g, '')
        // 去除 [参考文档《任意内容》 《任意内容》...] 格式的内容
        filteredAnswer = filteredAnswer.replace(/\[参考文档(?:《[^》]*》\s*)+\]/g, '')
        // 只移除空格和制表符，保留换行符（避免移除末尾的 \n\n）
        filteredAnswer = trimSpacesOnly(filteredAnswer)

        // 在流式响应过程中，忽略服务器返回的 endAnswerFlag，避免过早禁用输入框
        const { endAnswerFlag: _omitEndAnswerFlag, ...dataWithoutEndFlag } = msg.content.data || {}
        // 保留之前保存的 workFlowSessionId 和 recordId，避免被覆盖
        const existingAnswer = newItems[lastAiIndex].answerList?.[0] || {}
        newItems[lastAiIndex] = {
          ...newItems[lastAiIndex],
          question,
          answerList: [
            {
              ...existingAnswer,
              ...dataWithoutEndFlag,
              // 如果新的数据中没有 workFlowSessionId，保留之前的值
              workFlowSessionId: dataWithoutEndFlag.workFlowSessionId ?? existingAnswer.workFlowSessionId,
              // 如果新的数据中没有 recordId，保留之前的值
              recordId: dataWithoutEndFlag.recordId ?? existingAnswer.recordId,
              isShow: false,
              answer: filteredAnswer,
            },
          ],
        }

        console.log('[TacticsChat] handleStreamMesageData:updated', {
          filteredAnswer,
          answerList: newItems[lastAiIndex].answerList,
          updatedItem: newItems[lastAiIndex],
        })
      }
      else {
        // 如果没有找到 AI 项，创建一个新的 AI 项

        console.warn('[TacticsChat] handleStreamMesageData: no AI item found, creating new one')
        const newAnswer = msg.content?.data?.answer ?? '\n'
        // 移除 ♪ 符号之后的所有文本（与历史记录保持一致）
        let filteredAnswer = newAnswer.split('♪')[0]
        // 移除所有括号及其内容
        filteredAnswer = filteredAnswer.replace(/\([^)]*\)/g, '')
        // 去除 [参考文档《任意内容》 《任意内容》...] 格式的内容
        filteredAnswer = filteredAnswer.replace(/\[参考文档(?:《[^》]*》\s*)+\]/g, '')
        // 只移除空格和制表符，保留换行符（避免移除末尾的 \n\n）
        filteredAnswer = trimSpacesOnly(filteredAnswer)
        newItems.push({
          role: 'ai',
          question,
          answerList: [
            {
              ...msg.content.data,
              isShow: false,
              answer: filteredAnswer,
            },
          ],
        } as ChatRecord)
      }
      return newItems
    })
  }

  /** 处理超过最大条数的数据 */
  const handleChatMaxCount = (msg: any, question: string) => {
    setAllItems((prevItems) => {
      const newItems = [...prevItems]
      // 从后往前找到最后一个 AI 项
      let lastAiIndex = -1
      for (let i = newItems.length - 1; i >= 0; i--) {
        if (newItems[i].role === 'ai') {
          lastAiIndex = i
          break
        }
      }
      if (lastAiIndex >= 0) {
        newItems[lastAiIndex] = {
          ...newItems[lastAiIndex],
          question,
          answerList: [
            {
              ...msg.content.data,
              isShow: false,
              isChatMaxCount: true,
              endAnswerFlag: true,
              answer: '已达上限',
            },
          ],
        }
      }
      return newItems
    })
    // 达到最大轮数时，没有打字效果，需要手动设置 isAsking 为 false
    dispatch(setIsAsking(false))
  }

  /** 提交问题 */
  const handleSubmitQuestion = async (
    question: string | undefined,
    productCode?: string,
    toolId?: string,
    extra?: { busiType?: string, recordType?: string, numberType?: string, includeQuestion?: boolean, includeTacticsMeta?: boolean, includeUserMeta?: boolean, includeOrderMeta?: boolean, isReanalyze?: boolean, paramMap?: Record<string, string>, recordId?: string, workFlowSessionId?: string },
  ) => {
    // 优先读取缓存中的 toolId，再回退到传参
    const sessionToolId = sessionStorage.getItem('currentToolId') ?? undefined
    const resolvedToolId = toolId ?? sessionToolId ?? undefined
    // 停止之前的请求
    if (abortControllerRef.current) {
      abortControllerRef.current.abort()
    }

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

    // 检查token
    await fetchCheckTokenApi()
    // 一次性添加用户问题和空的AI回答；如果不需要问题（自动触发场景），只添加 AI 占位
    const shouldIncludeQuestion = extra?.includeQuestion !== false && !!question
    const isReanalyze = extra?.isReanalyze === true
    // 重新分析时，在添加新记录之前计算分割线位置
    // 分割线应该显示在已有对话记录之后、新记录之前
    // 渲染时使用 divider.index === index + 1 来匹配，所以分割线的 index 应该是新记录的位置
    // 判断 allItems 中是否存在除了 system 之外的记录（user 或 ai）
    // 场景1: 重新分析时显示分割线
    // 场景2: orderMeta 场景下，有历史记录且自动调用时也显示分割线
    const shouldCreateDivider = isReanalyze || (orderMeta && hasHistory === true && !question)
    if (shouldCreateDivider) {
      const hasExistingRecords = allItems.some(item => item.role !== 'system')
      if (hasExistingRecords) {
        // 基于当前 allItems 长度计算分割线位置
        // 分割线会在新添加的第一个记录之前显示（即 prevItems.length + 1）
        const nextDividerIndex = allItems.length + 1
        const currentTime = formatCurrentTime()
        setHistoryDividers((prev) => {
          // 避免重复添加相同位置的分割线
          if (prev.some(divider => divider.index === nextDividerIndex)) {
            return prev
          }
          return [...prev, { index: nextDividerIndex, time: currentTime }]
        })
      }
    }
    setAllItems(prevItems => [
      ...prevItems,
      ...(shouldIncludeQuestion
        ? [
            {
              role: 'user',
              question,
            } as ChatRecord,
          ]
        : []),
      {
        role: 'ai',
        answerList: [{ answer: '' }],
      } as ChatRecord,
    ])

    // 创建新的 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

    const shouldSendQuestion = extra?.includeQuestion !== false
    const shouldIncludeTacticsMeta = extra?.includeTacticsMeta !== false
    const shouldIncludeUserMeta = extra?.includeUserMeta !== false
    const shouldIncludeOrderMeta = extra?.includeOrderMeta !== false

    // 去除缓存中可能存在的 from 字段和 busiType 字段，避免作为参数传递（提问接口中 busiType 需要单独处理）
    const { from: _omitFrom, busiType: _omitBusiType, ...safeTacticsMeta } = (tacticsMeta || {}) as Record<string, any>

    const requestBody: Record<string, any> = {
      conversationId: currentIdRef.current,
      stream: true,
      productCode,
      toolId: resolvedToolId,
      ...(extra?.busiType ? { busiType: extra.busiType } : {}),
      ...(extra?.recordType ? { recordType: extra.recordType } : {}),
      ...(extra?.numberType ? { numberType: extra.numberType } : {}),
    }
    // 优先级：orderMeta > userMeta > tacticsMeta
    // 优先使用 orderMeta，如果存在 orderMeta 且需要包含，则传入 orderMeta 的字段
    if (orderMeta && shouldIncludeOrderMeta) {
      console.log('[TacticsChat] handleSubmitQuestion: orderMeta', orderMeta)
      Object.assign(requestBody, orderMeta)
      console.log('[TacticsChat] handleSubmitQuestion: requestBody after orderMeta', {
        workOrderIds: requestBody.workOrderIds,
        hasWorkOrderIds: 'workOrderIds' in requestBody,
      })
    }
    // 如果没有 orderMeta 或不需要包含 orderMeta，则使用 userMeta
    else if (userMeta && shouldIncludeUserMeta) {
      // 注意：这里显式剔除 userMeta 自带的 numberType 和 place，避免覆盖映射后的 numberType，并按需去掉 place
      const { place: _omitPlace, ...safeUserMeta } = userMeta
      Object.assign(requestBody, safeUserMeta)
    }
    // 如果没有 userMeta 或不需要包含 userMeta，则使用 tacticsMeta
    else if (tacticsMeta && shouldIncludeTacticsMeta) {
      Object.assign(requestBody, safeTacticsMeta)
    }
    // from=tactics 场景下补充业务识别参数
    if (orderMeta && shouldIncludeOrderMeta) {
      requestBody.busiType ??= '02'
      // 从 localStorage 读取 userId
      const userIdFromStorage = safeLocalStorageGetItem('__USER_NAME__')
      let userId = ''
      if (userIdFromStorage) {
        try {
          // useLocalStorageState 会将值序列化为 JSON，需要解析
          const parsed = JSON.parse(userIdFromStorage)
          userId = parsed || userIdFromStorage
        }
        catch {
          // 如果不是 JSON 格式，直接使用原始值
          userId = userIdFromStorage
        }
      }
      requestBody.busiId = userId
    }
    else if (userMeta?.place === 'user') {
      requestBody.busiType ??= '02'
      // place=user 场景下，业务方要求 busiId 取 userId:numberType
      requestBody.busiId ??= userMeta.numberType
      // 当缓存内存在 place=user 时，始终使用缓存中的 numberType 作为接口参数
      if (userMeta.numberType) {
        requestBody.numberType = userMeta.numberType
      }
    }
    else if (tacticsMeta) {
      requestBody.busiType ??= '03'
      requestBody.busiId ??= tacticsMeta.taskId
    }
    // 如果 includeQuestion 为 false，不展示问题但接口参数中仍要传递 question
    if (shouldSendQuestion || (question && extra?.includeQuestion === false)) {
      requestBody.question = question ?? ''
    }
    // 如果是 workflow 提交，添加 paramMap、recordId 和 workFlowSessionId
    if (extra?.paramMap) {
      requestBody.paramMap = extra.paramMap
    }
    if (extra?.recordId) {
      requestBody.recordId = extra.recordId
    }
    if (extra?.workFlowSessionId) {
      requestBody.workFlowSessionId = extra.workFlowSessionId
    }
    // 调试日志：检查最终请求参数
    console.log('[TacticsChat] handleSubmitQuestion: final requestBody', {
      workOrderIds: requestBody.workOrderIds,
      hasWorkOrderIds: 'workOrderIds' in requestBody,
      orderMeta,
      shouldIncludeOrderMeta,
      requestBodyKeys: Object.keys(requestBody),
      requestBodyStringified: JSON.stringify(requestBody),
    })
    fetchStreamResponse(
      fetchUrl,
      requestBody,
      (msg) => {
        console.log('[TacticsChat] handleSubmitQuestion:onMessage', 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]) {
                newItems[i] = {
                  ...newItems[i],
                  answerList: [
                    {
                      ...newItems[i].answerList[0],
                      endAnswerFlag: true,
                    },
                  ],
                }
                break
              }
            }
            return newItems
          })
          // 其他错误也可以在这里显示错误消息
          return
        }

        // 正常的stream数据
        if (msg?.type === 'DATA' && msg?.content?.code === '00000000') {
          console.log('========================流式输出====================', 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 判断，而不是 END 消息）
        console.log('[TacticsChat] 流式响应已完成')
        // 设置最后一条 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]) {
              newItems[i] = {
                ...newItems[i],
                answerList: [
                  {
                    ...newItems[i].answerList[0],
                    endAnswerFlag: true,
                  },
                ],
              }
              break
            }
          }
          return newItems
        })
        dispatch(setIsAsking(false))
        if (isNew) {
          setTimeout(() => {
            dispatch(fetchTacticsConversations())
          }, 2000)
        }
      })
      .catch((error) => {
        // 流式请求出错时的处理
        console.error('[TacticsChat] 流式请求失败:', error)
        // 设置最后一条 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]) {
              newItems[i] = {
                ...newItems[i],
                answerList: [
                  {
                    ...newItems[i].answerList[0],
                    endAnswerFlag: true,
                  },
                ],
              }
              break
            }
          }
          return newItems
        })
        dispatch(setIsAsking(false))
      })
  }

  /** 处理 workflow 表单提交 */
  const handleWorkflowSubmit = useCallback(async (paramMap: Record<string, string>, recordId?: string, workFlowSessionId?: string) => {
    // workflow 提交时，question 可以为空字符串，但需要传递 paramMap、recordId 和 workFlowSessionId
    await handleSubmitQuestion(undefined, undefined, undefined, {
      includeQuestion: false,
      paramMap,
      recordId,
      workFlowSessionId,
      recordType: 'A01',
    })
  }, [])

  /** 获取qa记录 */
  const getUserQaRecordPage = useCallback(async (conversationId: string) => {
    setIsLoading(true)
    // 在异步操作开始前保存 createdFromClearRef 的值，避免在异步过程中被其他逻辑修改
    const wasCreatedFromClear = createdFromClearRef.current
    try {
      await waitForToken()
      const res = await fetchTacticsQaRecordPage(conversationId)
      const qaRecords = res.data || []
      const hasHistoryFlag = qaRecords.length > 0
      // 始终添加 system 角色作为欢迎语
      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
                ?.replace(/\([^)]*\)/g, '')
                .replace(/\[参考文档(?:《[^》]*》\s*)+\]/g, '')
                .trim(),
            })),
          }
        }
        return item
      })
      // 使用函数式更新，避免覆盖正在进行的流式响应
      setAllItems((prevItems) => {
        // 如果当前有正在进行的流式响应（存在空的AI项），保留它们
        const hasPendingAi = prevItems.some(item => item.role === 'ai' && (!item.answerList?.[0]?.answer || item.answerList[0].answer === ''))
        if (hasPendingAi && processedMessages.length > 1) {
          // 如果历史记录不为空，合并历史记录和正在进行的响应
          const lastPendingAiIndex = prevItems.length - 1
          const lastPendingAi = prevItems[lastPendingAiIndex]
          if (lastPendingAi?.role === 'ai') {
            return [...processedMessages, lastPendingAi]
          }
        }
        return processedMessages
      })
      // 统一管理 hasCleared 状态：
      // 1. 如果是清除记录创建的新会话且没有历史记录，保持 hasCleared 为 true
      // 2. 其他情况都设置为 false
      // 使用保存的值而不是 ref.current，避免异步过程中的时序问题
      setHasCleared(wasCreatedFromClear && !hasHistoryFlag)
      setHistoryDividers([])
      setHasHistory(hasHistoryFlag)
    }
    catch {
      // 如果获取失败，至少显示欢迎语
      setAllItems((prevItems) => {
        const hasPendingAi = prevItems.some(item => item.role === 'ai' && (!item.answerList?.[0]?.answer || item.answerList[0].answer === ''))
        if (hasPendingAi) {
          const lastPendingAiIndex = prevItems.length - 1
          const lastPendingAi = prevItems[lastPendingAiIndex]
          if (lastPendingAi?.role === 'ai') {
            return [{ role: 'system' } as ChatRecord, lastPendingAi]
          }
        }
        return [{ role: 'system' } as ChatRecord]
      })
      // 如果是清除记录创建的新会话，保持 hasCleared 为 true
      // 使用保存的值而不是 ref.current，避免异步过程中的时序问题
      setHasCleared(wasCreatedFromClear)
      setHistoryDividers([])
      // 拉取失败时视为无历史记录，后续可按需触发自动提问
      setHasHistory(false)
    }
    finally {
      setIsLoading(false)
    }
  }, [])

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

  const handleClearRecord = useCallback(async () => {
    if (!currentIdRef.current)
      return
    setShowClearConfirm(false)
    try {
      await waitForToken()
      // 1. 获取问答记录
      const res = await fetchTacticsQaRecordPage(currentIdRef.current)
      const qaRecords = res.data || []

      // 2. 提取所有 recordId
      const recordIdList: string[] = []
      qaRecords.forEach((record) => {
        if (record.answerList && record.answerList.length > 0) {
          record.answerList.forEach((answer) => {
            if (answer.recordId) {
              recordIdList.push(answer.recordId)
            }
          })
        }
      })

      // 3. 调用删除接口
      await fetchDeleteTacticsConversation(currentIdRef.current, recordIdList)

      // 停止正在进行的请求
      if (abortControllerRef.current) {
        abortControllerRef.current.abort()
        dispatch(setIsAsking(false))
      }
      // 清空对话列表，只保留欢迎语
      setAllItems([{ role: 'system' } as ChatRecord])
      setHasCleared(true)
      setHistoryDividers(() => [])
      // 标记接下来创建的新会话来源于「清空记录」，用于阻止自动触发一次提问
      createdFromClearRef.current = true
      dispatch(
        createTacticsConversation({
          conversationData: getConversationExtra(),
          // 清空记录后创建的新会话也需要更新路由中的 conversationId
          // 因此这里改为 shouldNavigate: true，让下方 useEffect 触发 navigate
          shouldNavigate: true,
          shouldSendQuestion: '',
        }),
      )
    }
    catch (error) {
      console.error('清除记录失败:', error)
    }
  }, [dispatch, getConversationExtra])

  const handleReanalyze = useCallback(async () => {
    // 正在回答或尚未就绪时不触发重新分析
    if (!currentIdRef.current || isLoading || isAsking) {
      console.log('[TacticsChat] handleReanalyze blocked', {
        currentId: currentIdRef.current,
        isLoading,
        isAsking,
      })
      return
    }
    // 重新触发一次提问，与首次自动调用保持一致：
    // - 有 orderMeta：传 recordType=A11，使用 orderMeta
    // - 有 userMeta：按 userMeta.numberType 映射 numberType（A03/A05/A07/A09），不传 recordType，仅传 userMeta
    // - 无 userMeta：沿用原逻辑，传 recordType=A02，使用 tacticsMeta
    if (orderMeta) {
      await handleSubmitQuestion(undefined, undefined, undefined, {
        busiType: '02',
        recordType: 'A11',
        includeQuestion: false,
        includeTacticsMeta: false,
        includeOrderMeta: true,
        isReanalyze: true,
      })
    }
    else {
      const hasUserMeta = !!userMeta
      if (hasUserMeta) {
        // place=user 场景下，重新分析也视为"自动调用"：
        // - numberType 为 3/4/5/6：映射为 A03/A05/A07/A09
        // - 其它值或不存在：recordType=A01
        const mappedNumberType = getNumberTypeWithUserMeta('A01', true)
        await handleSubmitQuestion(undefined, undefined, undefined, {
          busiType: '02',
          recordType: mappedNumberType,
          includeQuestion: false,
          includeTacticsMeta: false,
          includeUserMeta: true,
          isReanalyze: true,
        })
      }
      else {
        await handleSubmitQuestion(undefined, undefined, undefined, {
          busiType: '03',
          recordType: 'A01',
          includeQuestion: false,
          includeTacticsMeta: true,
          isReanalyze: true,
        })
      }
    }
    // 重新拉取会话列表，保持原有行为
    await dispatch(fetchTacticsConversations())
    if (currentIdRef.current) {
      logFullUrl('before navigate reanalyze')
      navigate(`/tactics/chat/${currentIdRef.current}${location.search}`)
    }
  }, [dispatch, navigate, handleSubmitQuestion, orderMeta, userMeta, getNumberTypeWithUserMeta, isLoading, isAsking, logFullUrl, location.search])

  const handleCancelClear = useCallback(() => {
    setShowClearConfirm(false)
  }, [])

  const handleOpenClearConfirm = useCallback(() => {
    // 检查是否有对话记录（排除 system 角色）
    const hasConversationRecords = allItems.some(item => item.role !== 'system')
    // 检查是否有历史记录
    const hasHistoryRecords = hasHistory === true
    // 如果两者都没有，显示提示并返回
    if (!hasConversationRecords && !hasHistoryRecords) {
      showToast('暂无记录', 'default')
      return
    }
    // 有记录时正常打开确认弹窗
    setShowClearConfirm(true)
  }, [allItems, hasHistory, showToast])

  useEffect(() => {
    if (id) {
      // 停止之前的请求
      if (abortControllerRef.current) {
        abortControllerRef.current.abort()
        dispatch(setIsAsking(false))
      }

      currentIdRef.current = id
      lastSentQuestionRef.current = ''
      // 每次切换会话时重置历史标记，等待新会话的历史查询结果
      setHasHistory(null)
      setHistoryDividers([])
      // hasCleared 状态由 getUserQaRecordPage 统一管理，避免时序问题
      getUserQaRecordPage(id)
    }
    else {
      // 如果没有 id，显示欢迎语，等待登录成功后创建新会话
      setAllItems([{ role: 'system' } as ChatRecord])
      setHistoryDividers([])
      setIsLoading(false)
      setHasHistory(false)
      setHasCleared(false)
    }
  }, [id, getUserQaRecordPage, dispatch])

  // 当 token 存在时，获取会话列表
  useEffect(() => {
    if (token) {
      dispatch(fetchTacticsConversations())
    }
  }, [token, dispatch])

  // 进入会话后自动触发一次提交，默认 question 为"策略分析"，busiType 02 / recordType A02
  const hasAutoSubmittedRef = useRef(false)
  // 记录上次调用时的 searchParams，用于 orderMeta 场景检测参数变化
  const lastSearchParamsRef = useRef<string>('')
  useEffect(() => {
    if (id) {
      // 当 id 变化时，重置自动提交标志
      hasAutoSubmittedRef.current = false
      lastSearchParamsRef.current = ''
    }
  }, [id])
  useEffect(() => {
    // 对于 orderMeta 场景：历史记录加载完成后自动触发（无论是否有历史记录），每次进入页面都调用
    // 对于其他场景：仅在历史记录查询完成且为空时，才自动触发一次提交
    const isOrderMetaScenario = !!orderMeta
    const hasHistoryLoaded = hasHistory !== null
    const currentSearchParams = location.search
    // orderMeta 场景：如果 searchParams 变化了，重置标志以允许重新调用
    if (isOrderMetaScenario && lastSearchParamsRef.current !== currentSearchParams) {
      hasAutoSubmittedRef.current = false
      lastSearchParamsRef.current = currentSearchParams
    }
    const shouldTriggerForOrderMeta = isOrderMetaScenario && hasHistoryLoaded && !isLoading
    const shouldTriggerForNormal = !isOrderMetaScenario && hasHistory === false && !isLoading

    if (currentIdRef.current && (shouldTriggerForOrderMeta || shouldTriggerForNormal) && !hasAutoSubmittedRef.current) {
      // 如果当前会话是由「清空记录」创建的，则不自动调用提问接口
      // 但仍然标记为已处理，避免后续重复触发
      if (createdFromClearRef.current) {
        createdFromClearRef.current = false
        hasAutoSubmittedRef.current = true
        return
      }
      hasAutoSubmittedRef.current = true
      // 进入会话后自动触发一次提交：
      // - 有 orderMeta：传 recordType=A11，使用 orderMeta
      // - 有 userMeta：按 userMeta.numberType 映射 numberType（A03/A05/A07/A09），不传 recordType，仅传 userMeta
      // - 无 userMeta：沿用原逻辑，recordType=A02，携带 tacticsMeta
      if (orderMeta) {
        handleSubmitQuestion(
          undefined,
          undefined,
          undefined,
          {
            busiType: '02',
            recordType: 'A11',
            includeQuestion: false,
            includeTacticsMeta: false,
            includeOrderMeta: true,
          },
        )
      }
      else {
        const hasUserMeta = !!userMeta
        if (hasUserMeta) {
          // place=user 场景下，首次自动调用：
          // - numberType 为 3/4/5/6：映射为 A03/A05/A07/A09
          // - 其它值或不存在：recordType=A01
          const mappedNumberType = getNumberTypeWithUserMeta('A01', true)
          handleSubmitQuestion(
            undefined,
            undefined,
            undefined,
            {
              busiType: '02',
              recordType: mappedNumberType,
              includeQuestion: false,
              includeTacticsMeta: false,
              includeUserMeta: true,
            },
          )
        }
        else {
          handleSubmitQuestion(
            undefined,
            undefined,
            undefined,
            {
              busiType: '03',
              recordType: 'A01',
              includeQuestion: false,
              includeTacticsMeta: true,
            },
          )
        }
      }
    }
  }, [isLoading, handleSubmitQuestion, id, orderMeta, userMeta, getNumberTypeWithUserMeta, hasHistory, location.search])

  // 创建新会话成功后跳转到新会话页面
  useEffect(() => {
    if (shouldNavigateToNewConversation && currentConversationId) {
      logFullUrl('before navigate create new conversation')
      navigate(`/tactics/chat/${currentConversationId}${location.search}`, {
        state: {
          shouldSendQuestion: shouldSendQuestionFromState,
        },
        replace: true,
      })
      dispatch(clearTacticsNavigationFlag())
    }
  }, [shouldNavigateToNewConversation, currentConversationId, navigate, dispatch, shouldSendQuestionFromState, location.search, logFullUrl])

  // 处理shouldSendQuestion的变化 - 自动发送问题
  useEffect(() => {
    console.log('[TacticsChat] autoSend check', {
      shouldSendQuestion,
      currentId: currentIdRef.current,
      isLoading,
      lastSentQuestion: lastSentQuestionRef.current,
    })
    if (
      shouldSendQuestion
      && currentIdRef.current
      && !isLoading
      && shouldSendQuestion !== lastSentQuestionRef.current
    ) {
      lastSentQuestionRef.current = shouldSendQuestion
      dispatch(clearTacticsShouldSendQuestion())
      setTimeout(() => {
        if (orderMeta) {
          handleSubmitQuestion(
            shouldSendQuestion,
            undefined,
            undefined,
            {
              includeQuestion: true,
              includeTacticsMeta: false,
              includeOrderMeta: true,
              recordType: 'A12',
            },
          )
        }
        else {
          const hasUserMeta = !!userMeta
          if (hasUserMeta) {
            const mappedNumberType = getNumberTypeWithUserMeta('A01', false)
            handleSubmitQuestion(
              shouldSendQuestion,
              undefined,
              undefined,
              {
                includeQuestion: true,
                includeTacticsMeta: false,
                includeUserMeta: true,
                recordType: mappedNumberType,
              },
            )
          }
          else {
            // 无 userMeta：保持原有行为，不传任何 extra（不携带 numberType），由内部默认逻辑处理 tacticsMeta
            handleSubmitQuestion(shouldSendQuestion)
          }
        }
      }, 100)
    }
  }, [shouldSendQuestion, isLoading, dispatch, orderMeta, userMeta, handleSubmitQuestion, getNumberTypeWithUserMeta])

  // 提供给 ChatAnswerBox/推荐问题的提交方法，保持与手动提问一致的 recordType 逻辑
  const handleAnswerBoxSubmit = useCallback(
    (question: string, productCode?: string) => {
      if (orderMeta) {
        return handleSubmitQuestion(
          question,
          productCode,
          undefined,
          {
            busiType: '02',
            recordType: 'A12',
            includeQuestion: true,
            includeTacticsMeta: false,
            includeOrderMeta: true,
          },
        )
      }
      const hasUserMeta = !!userMeta
      if (hasUserMeta) {
        const mappedNumberType = getNumberTypeWithUserMeta('A02', false)
        return handleSubmitQuestion(
          question,
          productCode,
          undefined,
          {
            busiType: '02',
            recordType: mappedNumberType,
            includeQuestion: true,
            includeTacticsMeta: false,
            includeUserMeta: true,
          },
        )
      }
      return handleSubmitQuestion(
        question,
        productCode,
        undefined,
        {
          busiType: '03',
          recordType: 'A01',
          includeQuestion: true,
          includeTacticsMeta: true,
        },
      )
    },
    [orderMeta, userMeta, getNumberTypeWithUserMeta, handleSubmitQuestion],
  )

  return (
    <div
      className={styles.scrollView}
      style={{
        height: '100vh',
        display: 'flex',
        flexDirection: 'column',
        marginLeft: 'auto',
        marginRight: 0,
        backgroundColor: '#FFFFFF',
        zIndex: 1000,
      }}
    >
      {/* 右上操作区 */}
      <div className="flex justify-end items-center gap-[16px] px-[16px] pt-[16px] pb-[8px]">
        <button
          type="button"
          onClick={handleOpenClearConfirm}
          className="flex items-center gap-[4px] text-[#4A90E2] text-[14px] hover:opacity-80 transition-opacity cursor-pointer bg-transparent border-none outline-none"
        >
          <DeleteIcon className="w-[16px] h-[16px] text-[#B2B8C1]" />
          <span>清除记录</span>
        </button>
        <button
          type="button"
          onClick={handleReanalyze}
          disabled={!currentIdRef.current || isLoading || isAsking || isRecommendLoading}
          className={`flex items-center gap-[4px] text-[14px] transition-opacity bg-transparent border-none outline-none ${
            !currentIdRef.current || isLoading || isAsking || isRecommendLoading
              ? 'text-[#B2B8C1] cursor-not-allowed opacity-60'
              : 'text-[#4A90E2] hover:opacity-80 cursor-pointer'
          }`}
        >
          <RefreshIcon className="w-[16px] h-[16px] text-[#B2B8C1]" />
          <span>重新分析</span>
        </button>
      </div>
      <div className={`${styles.chatPage} relative flex flex-col h-full`}>
        {showClearConfirm && (
          <div className="fixed inset-0 z-[2000] flex items-center justify-center bg-black/40 px-4">
            <div className="bg-white rounded-[12px] p-6 w-full max-w-[360px] shadow-lg">
              <div className="text-[16px] font-medium text-[#1F2937] mb-4">是否确定清空历史记录？</div>
              <div className="flex justify-end gap-3">
                <button
                  type="button"
                  onClick={handleCancelClear}
                  className="px-4 py-2 rounded-[8px] border border-[#E5E7EB] text-[#4B5563] hover:bg-[#F9FAFB]"
                >
                  取消
                </button>
                <button
                  type="button"
                  onClick={handleClearRecord}
                  className="px-4 py-2 rounded-[8px] bg-[#2563EB] text-white hover:opacity-90"
                >
                  确定
                </button>
              </div>
            </div>
          </div>
        )}
        <div className={`${styles.content} flex-1 overflow-hidden flex flex-col`}>
          {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 flex-1 overflow-y-auto px-[16px] min-h-0`}
            >
              <div className={`${styles.inter} pt-[8px] pb-[24px] min-h-full`}>
                {allItems.map((record, index) => {
                  // 如果 question 为空字符串、null 或不存在则不展示该条记录
                  if (record.role === 'user' && !record.question) {
                    return null
                  }
                  const matchedDivider = historyDividers.find(divider => divider.index === index + 1)
                  const recordId = record.answerList?.[0]?.recordId || record.groupId
                  const uniqueKey = recordId
                    ? `${record.role}-${recordId}`
                    : `${record.role}-${record.question || record.answerList?.[0]?.answer || ''}-${index}`
                  return (
                    <React.Fragment key={uniqueKey}>
                      {matchedDivider && (
                        <div className="flex items-center gap-[12px] my-[16px] text-[#B2B8C1] text-[12px]">
                          <div className="flex-1 h-px bg-[#E4E7EC]" />
                          <span>以上为历史分析数据</span>
                          <span>{matchedDivider.time}</span>
                          <div className="flex-1 h-px bg-[#E4E7EC]" />
                        </div>
                      )}
                      <div
                        className="w-full chatItem mx-auto"
                      >
        {record.role === 'system' && <TacticsWelcome cleared={hasCleared} />}
                        {record.role === 'user' && <ChatItemUser record={record} />}
                        {record.role === 'ai' && (
                          <ChatAnswerBox
                            onSubmitQuestion={handleAnswerBoxSubmit}
                            isLastAnswer={index === allItems.length - 1}
                            showIndex={0}
                            record={record}
                            index={index}
                            // 仅对最后一条 AI 回答的推荐问题 loading 状态进行跟踪
                            onRecommendLoadingChange={index === allItems.length - 1 ? setIsRecommendLoading : undefined}
                            onWorkflowSubmit={handleWorkflowSubmit}
                            isWorkflowSubmitting={isAsking}
                          />
                        )}
                      </div>
                    </React.Fragment>
                  )
                })}
              </div>
            </motion.div>
          )}
        </div>
        <div className="relative box-border px-[16px] mx-auto iptContainer w-full flex-shrink-0 pb-[18px] pt-[12px] bg-white border-t border-gray-100">
          <div className="relative">
            <div className="absolute left-1/2 ml-[-20px] top-[-45px]">
              <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>
            <TacticsChatEditor
              onSubmit={(question) => {
                console.log('[TacticsChat] onSubmit wrapper', {
                  question,
                  currentId: currentIdRef.current,
                  isLoading,
                })
                // 正常问答：
                // - 有 orderMeta：传 recordType=A12，使用 orderMeta
                // - 有 userMeta：按 userMeta.numberType 映射 numberType（A04/A06/A08/A10），不传 recordType，仅传 userMeta
                // - 无 userMeta：保持原有逻辑，recordType=A01、busiType=02、不带 numberType
                if (orderMeta) {
                  return handleSubmitQuestion(
                    question,
                    undefined,
                    undefined,
                    {
                      busiType: '02',
                      recordType: 'A12',
                      includeQuestion: true,
                      includeTacticsMeta: false,
                      includeOrderMeta: true,
                    },
                  )
                }
                const hasUserMeta = !!userMeta
                if (hasUserMeta) {
                  const mappedNumberType = getNumberTypeWithUserMeta('A02', false)
                  return handleSubmitQuestion(
                    question,
                    undefined,
                    undefined,
                    {
                      busiType: '02',
                      recordType: mappedNumberType,
                      includeQuestion: true,
                      includeTacticsMeta: false,
                      includeUserMeta: true,
                    },
                  )
                }
                return handleSubmitQuestion(
                  question,
                  undefined,
                  undefined,
                  {
                    busiType: '03',
                    recordType: 'A01',
                    includeQuestion: true,
                    includeTacticsMeta: true,
                  },
                )
              }}
              placeholders={['']}
            />
            <div className="w-full text-center mt-[12px] text-[#3333334d] text-[12px]">
              内容由AI模型生成，其准确性和完整性无法保证，仅供参考
            </div>
          </div>
        </div>
      </div>
    </div>
  )
}
