import { buildSuggestedActionKey } from '../../utils/suggestedActionKey.js'
import { normalizeExpenseQueryPayload } from './travelReimbursementExpenseQueryModel.js'
import { buildAgentInsight, buildReviewFilePreviewsFromReviewPayload } from './travelReimbursementAttachmentModel.js'
import { resolveExpenseTypeLabel } from './travelReimbursementReviewModel.js'
import { isBudgetMonitorUser, isExecutiveUser, isPlatformAdminUser } from '../../utils/accessControl.js'
import {
  GUIDED_ACTION_OPEN_TRAVEL_CALCULATOR,
  GUIDED_ACTION_START_APPLICATION,
  GUIDED_ACTION_START_REIMBURSEMENT,
  GUIDED_ACTION_START_STATUS_QUERY
} from './travelReimbursementGuidedFlowModel.js'

export const SESSION_TYPE_EXPENSE = 'expense'
export const SESSION_TYPE_APPLICATION = 'application'
export const SESSION_TYPE_APPROVAL = 'approval'
export const SESSION_TYPE_KNOWLEDGE = 'knowledge'
export const SESSION_TYPE_BUDGET = 'budget'

export const ASSISTANT_SESSION_TYPES = [
  SESSION_TYPE_APPLICATION,
  SESSION_TYPE_EXPENSE,
  SESSION_TYPE_APPROVAL,
  SESSION_TYPE_KNOWLEDGE,
  SESSION_TYPE_BUDGET
]

export const ASSISTANT_SESSION_MODE_OPTIONS = [
  {
    key: SESSION_TYPE_APPLICATION,
    label: '申请助手',
    icon: 'mdi mdi-file-plus-outline',
    description: '只处理费用申请、事前审批、申请材料和申请状态'
  },
  {
    key: SESSION_TYPE_EXPENSE,
    label: '报销助手',
    icon: 'mdi mdi-receipt-text-plus-outline',
    description: '只处理报销发起、票据识别、草稿归集和报销状态'
  },
  {
    key: SESSION_TYPE_APPROVAL,
    label: '审核助手',
    icon: 'mdi mdi-clipboard-check-outline',
    description: '只处理待审单据、风险解释、审批动作和审核意见'
  },
  {
    key: SESSION_TYPE_KNOWLEDGE,
    label: '财务知识助手',
    icon: 'mdi mdi-book-open-page-variant-outline',
    description: '只处理财务制度、标准规则、票据要求和政策解释'
  },
  {
    key: SESSION_TYPE_BUDGET,
    label: '预算编制助手',
    icon: 'mdi mdi-calculator-variant-outline',
    description: '帮助你进行预算编制与预算相关问题的整理'
  }
]

export function canUseBudgetAssistantSession(user = null) {
  return Boolean(isPlatformAdminUser(user) || isBudgetMonitorUser(user) || isExecutiveUser(user))
}

function canUseAssistantSessionType(sessionType, user = null) {
  const normalized = String(sessionType || '').trim()
  if (normalized === SESSION_TYPE_BUDGET) {
    return canUseBudgetAssistantSession(user)
  }
  return true
}

export function filterAssistantSessionModes(sessionModes = [], user = null) {
  return Array.isArray(sessionModes)
    ? sessionModes.filter((mode) => canUseAssistantSessionType(mode?.key, user))
    : []
}

export function filterAssistantSessionTypes(sessionTypes = [], user = null) {
  return Array.isArray(sessionTypes)
    ? sessionTypes.filter((sessionType) => canUseAssistantSessionType(String(sessionType || '').trim(), user))
    : []
}

export function normalizeAssistantSessionType(sessionType, fallback = SESSION_TYPE_EXPENSE) {
  const normalized = String(sessionType || '').trim()
  if (ASSISTANT_SESSION_TYPES.includes(normalized)) {
    return normalized
  }
  const fallbackType = String(fallback || '').trim()
  return ASSISTANT_SESSION_TYPES.includes(fallbackType) ? fallbackType : SESSION_TYPE_EXPENSE
}

export function resolveAssistantSessionMode(sessionType) {
  const normalized = normalizeAssistantSessionType(sessionType)
  return ASSISTANT_SESSION_MODE_OPTIONS.find((item) => item.key === normalized) || ASSISTANT_SESSION_MODE_OPTIONS[1]
}

export const aiAvatar = '/assets/header.png'
export const userAvatar = '/assets/person.png'

export const SOURCE_LABELS = {
  workbench: '来自个人工作台',
  topbar: '来自发起报销',
  application: '来自发起申请',
  budget: '来自预算中心',
  detail: '来自智能录入',
  upload: '来自附件上传',
  requests: '来自报销列表'
}

export const SCENARIO_LABELS = {
  expense: '报销',
  accounts_receivable: '应收',
  accounts_payable: '应付',
  budget: '预算',
  knowledge: '知识',
  unknown: '通用'
}

export const INTENT_LABELS = {
  query: '查询',
  explain: '解释',
  compare: '对比',
  risk_check: '风险检查',
  draft: '信息核对',
  operate: '动作请求'
}

export const FLOW_STEP_FALLBACKS = {
  intent: {
    title: '意图识别',
    tool: 'IntentRecognizer',
    runningText: '正在识别业务意图...',
    completedText: '意图识别完成'
  },
  extraction: {
    title: '信息提取',
    tool: 'SemanticExtractor',
    runningText: '正在提取时间、金额、费用类型和待补项...',
    completedText: '信息提取完成'
  },
  ocr: {
    title: '票据/OCR识别',
    tool: 'OCRService',
    runningText: '正在识别票据附件...',
    completedText: '票据识别完成'
  },
  'expense-review-preview': {
    title: '报销信息核对',
    tool: 'user_agent.expense_review_preview',
    runningText: '正在整理识别结果和右侧核对信息...',
    completedText: '核对信息已整理'
  },
  'expense-claim-draft': {
    title: '保存报销草稿',
    tool: 'database.expense_claims.save_or_submit',
    runningText: '正在把已确认信息保存为草稿...',
    completedText: '草稿已保存'
  },
  'attachment-association': {
    title: '票据关联草稿',
    tool: 'database.expense_claims.save_or_submit',
    runningText: '正在把本次票据关联到已保存草稿...',
    completedText: '票据已归集到草稿'
  },
  'expense-scene-selection': {
    title: '报销场景确认',
    tool: 'UserConfirmation',
    runningText: '等待用户选择报销场景...',
    completedText: '已进入场景选择，等待用户确认'
  },
  'expense-intent-confirmation': {
    title: '报销意图确认',
    tool: 'UserConfirmation',
    runningText: '等待用户确认是否发起报销...',
    completedText: '用户已确认报销意图'
  }
}
export const ASSISTANT_DISPLAY_NAME = '财务助手'

export const EXPENSE_WELCOME_QUICK_ACTIONS = [
  {
    label: '快速发起报销',
    action: GUIDED_ACTION_START_REIMBURSEMENT,
    icon: 'mdi mdi-receipt-text-plus-outline'
  },
  {
    label: '查询单据状态',
    action: GUIDED_ACTION_START_STATUS_QUERY,
    icon: 'mdi mdi-file-search-outline'
  },
  {
    label: '差旅计算器',
    action: GUIDED_ACTION_OPEN_TRAVEL_CALCULATOR,
    icon: 'mdi mdi-calculator-variant-outline'
  }
]

export const APPLICATION_WELCOME_QUICK_ACTIONS = [
  {
    label: '快速发起申请',
    action: GUIDED_ACTION_START_APPLICATION,
    icon: 'mdi mdi-file-plus-outline'
  },
  {
    label: '查询申请状态',
    prompt: '帮我查询我的费用申请单状态，筛选最近的 5 条记录。',
    icon: 'mdi mdi-file-search-outline'
  },
  {
    label: '申请材料清单',
    prompt: '请告诉我发起费用申请通常需要准备哪些关键信息和附件。',
    icon: 'mdi mdi-clipboard-text-search-outline'
  }
]

export const APPROVAL_WELCOME_QUICK_ACTIONS = [
  {
    label: '待我审核',
    prompt: '帮我查询当前待我审核的单据，筛选最近的 5 条记录。',
    icon: 'mdi mdi-clipboard-list-outline'
  },
  {
    label: '审核风险说明',
    prompt: '帮我梳理待审核单据中需要重点关注的风险，并按高、中、低风险分类说明。',
    icon: 'mdi mdi-alert-circle-outline'
  },
  {
    label: '生成审核意见',
    prompt: '请根据当前待审核单据的风险点，帮我生成一段专业、克制的审核意见草稿。',
    icon: 'mdi mdi-text-box-edit-outline'
  }
]

export const BUDGET_WELCOME_QUICK_ACTIONS = [
  {
    label: '预算编制查询',
    prompt: '帮我查询当前部门本季度预算编制情况，重点看差旅、通信、招待费和办公用品。',
    icon: 'mdi mdi-calculator-variant-outline'
  },
  {
    label: '阈值风险检查',
    prompt: '帮我检查当前预算的提醒阈值、告警阈值和风险阈值设置是否合理，并指出需要关注的费用类型。',
    icon: 'mdi mdi-alert-decagram-outline'
  },
  {
    label: '预算调整建议',
    prompt: '请根据已发生、已占用和剩余预算，帮我整理下一轮预算调整建议。',
    icon: 'mdi mdi-chart-box-plus-outline'
  }
]

export const HOT_KNOWLEDGE_QUESTIONS = [
  '差旅住宿标准按什么规则执行？',
  '酒店超标后如何申请例外报销？',
  '招待费报销需要哪些凭证？',
  '发票抬头不一致还能报销吗？',
  '电子发票验真失败怎么处理？',
  '借款多久内需要冲销？',
  '预算不足还能先提交报销吗？',
  '会议费和招待费如何区分？',
  '跨部门项目费用应该怎么归集？',
  '员工退票手续费是否可以报销？'
]
export const FLOW_MISSING_SLOT_LABELS = {
  expense_type: '报销类型',
  customer_name: '客户名称',
  time_range: '发生时间',
  location: '地点',
  merchant_name: '酒店/商户',
  amount: '金额',
  reason: '事由说明',
  participants: '参与人员',
  attachments: '票据附件'
}

let messageSeed = 0

export function nowTime() {
  return new Date().toLocaleTimeString('zh-CN', {
    hour: '2-digit',
    minute: '2-digit',
    hour12: false
  })
}

export function createMessage(role, text, attachments = [], extras = {}) {
  messageSeed += 1
  return {
    id: `msg-${messageSeed}`,
    role,
    text,
    attachments,
    time: nowTime(),
    meta: [],
    citations: [],
    suggestedActions: [],
    suggestedActionsLocked: false,
    selectedSuggestedActionKey: '',
    selectedSuggestedActionLabel: '',
    querySelectionLocked: false,
    selectedQueryRecordId: '',
    queryPayload: null,
    draftPayload: null,
    reviewPayload: null,
    reviewPanelScope: '',
    riskFlags: [],
    pendingAttachmentAssociation: null,
    applicationPreview: null,
    budgetReport: null,
    ...extras
  }
}

export function buildExpenseIntentConfirmationMessage(rawText) {
  const text = String(rawText || '').trim()
  return [
    text
      ? `我看到了「${text}」这类业务事项描述。`
      : '我看到了这类业务事项描述。',
    '但现在还不能确定你是要发起报销，还是要处理其他事项，所以我先暂停后续识别。',
    '如果你是想报销，请点击下面的“我要报销”，我再继续引导你选择具体报销场景。'
  ].join('\n')
}

export function buildExpenseSceneSelectionMessage(rawText) {
  const text = String(rawText || '').trim()
  const hasBusinessTime = /业务发生时间|发生时间|20\d{2}[-年\/.]\d{1,2}/.test(text)
  const prefix = hasBusinessTime
    ? '我已看到你提供了业务发生时间和报销意图。'
    : '我已识别到这是报销申请。'

  return [
    `${prefix}但现在还不能确定具体报销场景，所以我先暂停信息抽取。`,
    '请先选择本次要发起的报销场景，选择后我再按对应规则继续识别并整理核对信息。'
  ].join('\n')
}

export function formatMessageTime(value) {
  if (!value) {
    return nowTime()
  }

  const parsed = new Date(value)
  if (Number.isNaN(parsed.getTime())) {
    return nowTime()
  }

  return parsed.toLocaleTimeString('zh-CN', {
    hour: '2-digit',
    minute: '2-digit',
    hour12: false
  })
}

export function formatSemanticEntityValue(entity) {
  const normalizedValue = String(entity?.normalized_value || '').trim()
  const rawValue = String(entity?.value || '').trim()
  const entityType = String(entity?.type || '').trim()

  if (entityType === 'amount') {
    const numericValue = Number(normalizedValue || rawValue)
    if (Number.isFinite(numericValue) && numericValue > 0) {
      return Number.isInteger(numericValue) ? `${numericValue}元` : `${numericValue.toFixed(2)}元`
    }
  }

  return rawValue || normalizedValue
}

export function summarizeSemanticParseDetail(semanticParse, ontologyJson = {}) {
  if (!semanticParse || typeof semanticParse !== 'object') {
    return FLOW_STEP_FALLBACKS.extraction.completedText
  }

  const entities = Array.isArray(semanticParse.entities_json) ? semanticParse.entities_json : []
  const entityMap = new Map()
  for (const item of entities) {
    const entityType = String(item?.type || '').trim()
    if (!entityType || entityMap.has(entityType)) continue
    entityMap.set(entityType, item)
  }

  const extractedParts = []
  const timeRange = semanticParse.time_range_json && typeof semanticParse.time_range_json === 'object'
    ? semanticParse.time_range_json
    : {}
  const startDate = String(timeRange.start_date || '').trim()
  const endDate = String(timeRange.end_date || '').trim()
  if (startDate) {
    extractedParts.push(`时间 ${startDate}${endDate && endDate !== startDate ? ` 至 ${endDate}` : ''}`)
  }

  const amountEntity = entityMap.get('amount')
  if (amountEntity) {
    const amountValue = formatSemanticEntityValue(amountEntity)
    if (amountValue) {
      extractedParts.push(`金额 ${amountValue}`)
    }
  }

  const expenseTypeEntity = entityMap.get('expense_type')
  if (expenseTypeEntity) {
    const expenseTypeLabel = resolveExpenseTypeLabel(
      String(expenseTypeEntity?.normalized_value || '').trim(),
      String(expenseTypeEntity?.value || '').trim()
    )
    if (expenseTypeLabel) {
      extractedParts.push(`费用类型 ${expenseTypeLabel}`)
    }
  }

  const customerEntity = entityMap.get('customer')
  if (customerEntity) {
    const customerValue = formatSemanticEntityValue(customerEntity)
    if (customerValue) {
      extractedParts.push(`客户 ${customerValue}`)
    }
  }

  const missingSlots = Array.isArray(ontologyJson?.missing_slots) ? ontologyJson.missing_slots : []
  const missingLabels = missingSlots
    .map((item) => FLOW_MISSING_SLOT_LABELS[String(item || '').trim()] || String(item || '').trim())
    .filter(Boolean)

  if (extractedParts.length && missingLabels.length) {
    return `已提取${extractedParts.join('、')}；待补充 ${missingLabels.join('、')}`
  }
  if (extractedParts.length) {
    return `已提取${extractedParts.join('、')}`
  }
  if (missingLabels.length) {
    return `已完成信息提取；待补充 ${missingLabels.join('、')}`
  }
  return FLOW_STEP_FALLBACKS.extraction.completedText
}

export function sanitizeRequest(request) {
  if (!request || typeof request !== 'object') return null

  const normalized = {
    claimId: String(request.claimId || request.claim_id || '').trim(),
    id: String(request.id || '').trim(),
    typeLabel: String(request.typeLabel || request.category || '').trim(),
    reason: String(request.reason || request.title || '').trim(),
    entity: String(request.entity || '').trim(),
    city: String(request.city || request.location || '').trim(),
    period: String(request.period || '').trim(),
    applyTime: String(request.applyTime || request.occurredAt || '').trim(),
    amount: String(request.amount || '').trim(),
    node: String(request.node || '').trim(),
    approval: String(request.approval || '').trim(),
    travel: String(request.travel || '').trim()
  }

  return Object.values(normalized).some(Boolean) ? normalized : null
}

export function resolveStatusLabel(status) {
  if (status === 'succeeded') return '已完成'
  if (status === 'blocked') return '已阻断'
  return '失败'
}

export function resolveStatusTone(status) {
  if (status === 'succeeded') return 'success'
  if (status === 'blocked') return 'warning'
  return 'note'
}

export function buildMessageMeta(payload, fileNames = []) {
  const items = []

  if (payload?.selected_agent) {
    items.push(`Agent: ${payload.selected_agent}`)
  }

  if (payload?.permission_level) {
    items.push(`权限: ${payload.permission_level}`)
  }

  if (payload?.trace_summary?.tool_count) {
    items.push(`工具: ${payload.trace_summary.tool_count}`)
  }

  if (payload?.trace_summary?.degraded) {
    items.push('已降级')
  }

  if (payload?.requires_confirmation) {
    items.push('待确认')
  }

  if (payload?.run_id) {
    items.push(`Run: ${payload.run_id}`)
  }

  if (fileNames.length) {
    items.push(`附件: ${fileNames.length}`)
  }

  return items
}

export function buildStoredMessageMeta(messageJson, attachmentNames = []) {
  const payload = messageJson?.orchestrator_payload
  if (payload) {
    return buildMessageMeta(payload, attachmentNames)
  }

  const items = []
  if (messageJson?.status) {
    items.push(`状态: ${messageJson.status}`)
  }
  if (attachmentNames.length) {
    items.push(`附件: ${attachmentNames.length}`)
  }
  return items
}

export function buildWelcomeUserContext(user = {}) {
  const username = String(user.username || '').trim()
  const name = String(user.name || username || '同事').trim()
  const grade = String(user.grade || '').trim()
  const position = String(user.position || '').trim()
  const role = String(user.role || '').trim()
  const roleCodes = Array.isArray(user.roleCodes) ? user.roleCodes : []
  const isAdmin =
    Boolean(user.isAdmin)
    || username.toLowerCase() === 'admin'
    || roleCodes.some((item) => /admin|manager/i.test(String(item || '')))
    || /管理员|系统管理/.test(position)
    || /管理员|系统管理/.test(role)

  const now = new Date()
  const dateLine = now.toLocaleDateString('zh-CN', {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
    weekday: 'long'
  })

  let honorific = name
  if (isAdmin) {
    honorific = name && !/^admin$/i.test(name) ? `${name} 管理员` : '管理员'
  } else {
    const prefix = [grade, position].filter(Boolean).join(' ')
    honorific = prefix ? `${prefix} ${name}`.trim() : name
  }

  return {
    name,
    username,
    grade,
    position,
    role,
    isAdmin,
    honorific,
    dateLine
  }
}

export function buildWelcomeQuickActions(sessionType, user, entrySource, linkedRequest) {
  const normalizedSessionType = normalizeAssistantSessionType(sessionType)
  if (normalizedSessionType === SESSION_TYPE_KNOWLEDGE) {
    return HOT_KNOWLEDGE_QUESTIONS.slice(0, 6).map((question) => ({
      label: question.length > 20 ? `${question.slice(0, 20)}…` : question,
      prompt: question,
      icon: 'mdi mdi-comment-question-outline'
    }))
  }

  if (normalizedSessionType === SESSION_TYPE_APPLICATION) {
    return APPLICATION_WELCOME_QUICK_ACTIONS
  }

  if (normalizedSessionType === SESSION_TYPE_APPROVAL) {
    return APPROVAL_WELCOME_QUICK_ACTIONS
  }

  if (normalizedSessionType === SESSION_TYPE_BUDGET) {
    return BUDGET_WELCOME_QUICK_ACTIONS
  }

  return EXPENSE_WELCOME_QUICK_ACTIONS
}

export function buildWelcomeMessage(entrySource, linkedRequest, sessionType = SESSION_TYPE_EXPENSE, user = null) {
  const normalizedSessionType = normalizeAssistantSessionType(sessionType)
  const ctx = buildWelcomeUserContext(user || {})
  const greeting = ctx.isAdmin ? `${ctx.honorific}，您好` : `您好，${ctx.honorific}`

  if (normalizedSessionType === SESSION_TYPE_KNOWLEDGE) {
    return [
      `${greeting}！今日是 **${ctx.dateLine}**。`,
      '',
      '**欢迎来到个人财务中心 · 财务知识助手。** 我可以帮您查制度、报销标准、票据要求和常见财务问题，并保持知识问答对话独立记录。',
      '',
      '业务范围：财务制度、标准规则、票据要求和政策口径解释。发起申请、报销处理或审核动作请切换到对应助手。',
      '',
      '您可以直接输入问题，或点击下方「猜你想问」快速开始。'
    ].join('\n')
  }

  if (normalizedSessionType === SESSION_TYPE_APPLICATION) {
    return [
      `${greeting}！今日是 **${ctx.dateLine}**。`,
      '',
      '**欢迎来到个人财务中心 · 申请助手。** 我会先判断您要处理的是费用申请、报销申请还是其他财务事项，再按对应流程引导补充信息。',
      '',
      '业务范围：费用申请、事前审批、申请材料清单和申请单状态。报销票据、审核处理和制度问答请切换到对应助手。',
      '',
      '您可以直接描述申请事项，或点击下方快捷操作开始发起申请。'
    ].join('\n')
  }

  if (normalizedSessionType === SESSION_TYPE_APPROVAL) {
    return [
      `${greeting}！今日是 **${ctx.dateLine}**。`,
      '',
      '**欢迎来到个人财务中心 · 审核助手。** 我可以帮您查询待审单据、解释风险点、整理审核意见，并保持审核对话独立记录。',
      '',
      '业务范围：待审单据查询、审批动作、风险解释和审核意见草稿。申请、报销和制度问答请切换到对应助手。',
      '',
      '您可以直接输入要审核或查询的内容，或点击下方快捷操作快速开始。'
    ].join('\n')
  }

  if (normalizedSessionType === SESSION_TYPE_BUDGET) {
    return [
      `${greeting}！今日是 **${ctx.dateLine}**。`,
      '',
      '**欢迎来到个人财务中心 · 预算编制助手。** 我可以帮您查询预算编制情况、整理费用类型预算、检查提醒/告警/风险阈值，并保持预算对话独立记录。',
      '',
      '业务范围：预算编制查询、部门预算检查、费用类型额度梳理、预算占用说明和阈值风险分析。报销发起、审核动作和制度问答请切换到对应助手。',
      '',
      '您可以直接输入预算问题，或点击下方快捷操作快速开始。'
    ].join('\n')
  }

  if (entrySource === 'detail' && linkedRequest?.id) {
    return [
      `${greeting}！今日是 **${ctx.dateLine}**。`,
      '',
      `我已为您打开关联单据 **${linkedRequest.id}**。您可以继续补充票据、核对识别结果，或让我解释待补项与风险。`,
      '',
      '如需新建其他报销，也可以直接告诉我费用场景，或上传发票、行程单开始识别。'
    ].join('\n')
  }

  return [
    `${greeting}！今日是 **${ctx.dateLine}**。`,
    '',
    '**欢迎来到个人财务中心 · 报销助手。** 我可以陪您完成报销发起、票据识别、草稿归集、报销信息核对、待补项提醒和风险说明，并保持报销对话独立记录。',
    '',
    '业务范围：发起报销、票据识别、草稿归集、报销状态查询和报销信息核对。申请、审核和制度问答请切换到对应助手。',
    '',
    '您可以描述一笔费用、上传票据，或点击下方快捷操作直接开始。'
  ].join('\n')
}

export function buildWelcomeInsight(entrySource, linkedRequest, sessionType = SESSION_TYPE_EXPENSE, user = null) {
  const normalizedSessionType = normalizeAssistantSessionType(sessionType)
  const ctx = buildWelcomeUserContext(user || {})

  if (normalizedSessionType === SESSION_TYPE_KNOWLEDGE) {
    return {
      intent: 'welcome',
      metricLabel: '今日',
      metricValue: ctx.dateLine.split(' ')[0] || '—',
      title: '财务知识问答',
      summary: `${ctx.honorific}，右侧整理了热门制度问题，点选即可追问；左侧也可直接输入您关心的问题。`,
      agent: null
    }
  }

  if (normalizedSessionType === SESSION_TYPE_APPLICATION) {
    return {
      intent: 'welcome',
      metricLabel: '当前助手',
      metricValue: '申请助手',
      title: '申请助手',
      summary: `${ctx.honorific}，这里会单独保存费用申请相关对话，不会混入报销、审核或知识问答记录。`,
      agent: null
    }
  }

  if (normalizedSessionType === SESSION_TYPE_APPROVAL) {
    return {
      intent: 'welcome',
      metricLabel: '当前助手',
      metricValue: '审核助手',
      title: '审核助手',
      summary: `${ctx.honorific}，这里会单独保存审核相关对话，适合查询待审单据、风险点和审核意见。`,
      agent: null
    }
  }

  if (normalizedSessionType === SESSION_TYPE_BUDGET) {
    return {
      intent: 'welcome',
      metricLabel: '当前助手',
      metricValue: '预算编制助手',
      title: '预算编制助手',
      summary: `${ctx.honorific}，这里会单独保存预算相关对话，适合查询预算编制、预算占用和阈值风险。`,
      agent: null
    }
  }

  return {
    intent: 'welcome',
    metricLabel: '当前助手',
    metricValue: '报销助手',
    title:
      entrySource === 'detail' && linkedRequest?.id
        ? `已关联 ${linkedRequest.id}`
        : '报销助手',
    summary:
      entrySource === 'detail' && linkedRequest?.id
        ? `${ctx.honorific}，发送消息或上传附件后，我会结合当前单据继续识别并提示待补项。`
        : `${ctx.honorific}，描述费用场景或上传票据后，我会在右侧展示识别结果，并在对话中提示待补信息与风险。`,
    agent: null
  }
}

export function createWelcomeAssistantMessage(entrySource, linkedRequest, sessionType = SESSION_TYPE_EXPENSE, user = null) {
  return createMessage('assistant', buildWelcomeMessage(entrySource, linkedRequest, sessionType, user), [], {
    assistantName: ASSISTANT_DISPLAY_NAME,
    isWelcome: true,
    welcomeQuickActions: buildWelcomeQuickActions(sessionType, user, entrySource, linkedRequest)
  })
}

export function resolveInitialSessionType(conversation, fallback = SESSION_TYPE_EXPENSE) {
  const stateJson = conversation?.state_json || conversation?.stateJson || {}
  const sessionType = String(stateJson?.session_type || '').trim()
  return normalizeAssistantSessionType(sessionType, fallback)
}

export function buildInitialInsightFromConversation(conversation) {
  const rawMessages = Array.isArray(conversation?.messages) ? conversation.messages : []
  for (let index = rawMessages.length - 1; index >= 0; index -= 1) {
    const item = rawMessages[index]
    const messageJson = item?.message_json || item?.messageJson || {}
    const orchestratorPayload = messageJson?.orchestrator_payload || null
    if (!orchestratorPayload) continue
    const attachmentNames = Array.isArray(messageJson?.attachment_names)
      ? messageJson.attachment_names.filter(Boolean)
      : []
    return buildAgentInsight(
      orchestratorPayload,
      attachmentNames,
      buildReviewFilePreviewsFromReviewPayload(orchestratorPayload?.result?.review_payload)
    )
  }
  return null
}

export function resolveInitialConversationId(conversation) {
  return String(conversation?.conversation_id || conversation?.conversationId || '').trim()
}

export function resolveInitialDraftClaimId(conversation) {
  return String(conversation?.draft_claim_id || conversation?.draftClaimId || '').trim()
}

export function resolveKnowledgeRankLabel(index) {
  return String(index + 1)
}

export function resolveKnowledgeRankTone(index) {
  if (index === 0) return 'gold'
  if (index === 1) return 'silver'
  if (index === 2) return 'bronze'
  return 'default'
}

export function parseConversationMessageSequence(message) {
  const messageJson = message?.message_json || message?.messageJson || {}
  const sequence = Number.parseInt(messageJson?.sequence, 10)
  return Number.isFinite(sequence) && sequence > 0 ? sequence : null
}

export function parseConversationMessageTime(message) {
  const rawValue = message?.created_at || message?.createdAt || ''
  const timestamp = new Date(rawValue).getTime()
  return Number.isFinite(timestamp) ? timestamp : Number.MAX_SAFE_INTEGER
}

export function resolveConversationMessageRolePriority(message) {
  return String(message?.role || '').trim() === 'user' ? 0 : 1
}

export function sortConversationMessages(messages) {
  return [...(Array.isArray(messages) ? messages : [])].sort((left, right) => {
    const leftSequence = parseConversationMessageSequence(left)
    const rightSequence = parseConversationMessageSequence(right)
    if (leftSequence !== null && rightSequence !== null && leftSequence !== rightSequence) {
      return leftSequence - rightSequence
    }

    const timeDiff = parseConversationMessageTime(left) - parseConversationMessageTime(right)
    if (timeDiff !== 0) {
      return timeDiff
    }

    const leftRunId = String(left?.run_id || left?.runId || '').trim()
    const rightRunId = String(right?.run_id || right?.runId || '').trim()
    if (leftRunId && rightRunId && leftRunId === rightRunId) {
      const roleDiff = resolveConversationMessageRolePriority(left) - resolveConversationMessageRolePriority(right)
      if (roleDiff !== 0) {
        return roleDiff
      }
    }

    return String(left?.id || '').localeCompare(String(right?.id || ''))
  })
}

export function normalizeInitialConversationMessages(conversation) {
  const rawMessages = sortConversationMessages(conversation?.messages)

  const restoredMessages = rawMessages.map((item) => {
    const messageJson = item?.message_json || item?.messageJson || {}
    const attachmentNames = Array.isArray(messageJson?.attachment_names)
      ? messageJson.attachment_names.filter(Boolean)
      : []
    const orchestratorPayload = messageJson?.orchestrator_payload || null
    const result = orchestratorPayload?.result || {}

    return createMessage(item.role, item.content, attachmentNames, {
      id: `restored-${item.id || ++messageSeed}`,
      time: formatMessageTime(item.created_at || item.createdAt),
      meta: item.role === 'assistant' ? buildStoredMessageMeta(messageJson, attachmentNames) : [],
      citations: item.role === 'assistant' && Array.isArray(result?.citations) ? result.citations : [],
      suggestedActions:
        item.role === 'assistant' && Array.isArray(result?.suggested_actions)
          ? result.suggested_actions
          : [],
      queryPayload: item.role === 'assistant' ? normalizeExpenseQueryPayload(result?.query_payload) : null,
      draftPayload: item.role === 'assistant' ? result?.draft_payload || messageJson?.draft_payload || null : null,
      reviewPayload: item.role === 'assistant' ? result?.review_payload || null : null,
      riskFlags: item.role === 'assistant' && Array.isArray(result?.risk_flags) ? result.risk_flags : []
    })
  })
  return markResolvedSuggestedActionMessages(restoredMessages)
}

export function normalizeSnapshotMessage(message) {
  const extras = message && typeof message === 'object' ? { ...message } : {}
  const role = String(extras.role || 'assistant').trim() || 'assistant'
  const text = String(extras.text || '')
  const attachments = Array.isArray(extras.attachments) ? extras.attachments.filter(Boolean) : []
  delete extras.role
  delete extras.text
  delete extras.attachments
  return createMessage(role, text, attachments, extras)
}

export function normalizeSnapshotMessages(messages) {
  return Array.isArray(messages)
    ? markResolvedSuggestedActionMessages(messages.map(normalizeSnapshotMessage))
    : []
}

export function serializeSessionMessages(messages) {
  return (Array.isArray(messages) ? messages : []).map((message) => ({
    id: message.id,
    role: message.role,
    text: message.text,
    attachments: Array.isArray(message.attachments) ? message.attachments.filter(Boolean) : [],
    time: message.time,
    meta: Array.isArray(message.meta) ? message.meta.filter(Boolean) : [],
    metaTone: message.metaTone || '',
    citations: Array.isArray(message.citations) ? message.citations : [],
    suggestedActions: Array.isArray(message.suggestedActions) ? message.suggestedActions : [],
    suggestedActionsLocked: Boolean(message.suggestedActionsLocked),
    selectedSuggestedActionKey: String(message.selectedSuggestedActionKey || ''),
    selectedSuggestedActionLabel: String(message.selectedSuggestedActionLabel || ''),
    querySelectionLocked: Boolean(message.querySelectionLocked),
    selectedQueryRecordId: String(message.selectedQueryRecordId || ''),
    queryPayload: message.queryPayload || null,
    draftPayload: message.draftPayload || null,
    reviewPayload: message.reviewPayload || null,
    riskFlags: Array.isArray(message.riskFlags) ? message.riskFlags : [],
    pendingAttachmentAssociation: message.pendingAttachmentAssociation || null,
    applicationPreview: message.applicationPreview || null,
    budgetReport: message.budgetReport || null,
    assistantName: message.assistantName || '',
    isWelcome: Boolean(message.isWelcome),
    welcomeQuickActions: Array.isArray(message.welcomeQuickActions) ? message.welcomeQuickActions : []
  }))
}

export function hasMeaningfulSessionMessages(messages) {
  return (Array.isArray(messages) ? messages : []).some((message) => {
    if (!message || message.isWelcome) {
      return false
    }
    if (message.role === 'user') {
      return true
    }
    return Boolean(
      String(message.text || '').trim()
        || (Array.isArray(message.suggestedActions) && message.suggestedActions.length)
        || message.reviewPayload
        || message.queryPayload
        || message.draftPayload
        || message.applicationPreview
        || message.budgetReport
        || message.pendingAttachmentAssociation
        || (Array.isArray(message.riskFlags) && message.riskFlags.length)
    )
  })
}

export function hasActiveSuggestedActionMessage(messages) {
  return (Array.isArray(messages) ? messages : []).some(
    (message) =>
      message?.role === 'assistant'
        && Array.isArray(message.suggestedActions)
        && message.suggestedActions.length > 0
        && !message.suggestedActionsLocked
  )
}

export function resolveConversationUpdatedAt(conversation) {
  const timestamp = new Date(conversation?.updated_at || conversation?.updatedAt || 0).getTime()
  return Number.isFinite(timestamp) ? timestamp : 0
}

export function shouldPreferPersistedSessionState(persistedState, snapshot, conversation) {
  if (!persistedState) {
    return false
  }
  if (!conversation) {
    return true
  }
  if (hasActiveSuggestedActionMessage(persistedState.messages)) {
    return true
  }
  const snapshotUpdatedAt = Number(snapshot?.updatedAt || 0)
  return snapshotUpdatedAt >= resolveConversationUpdatedAt(conversation)
}

export function markResolvedSuggestedActionMessages(messages) {
  const items = Array.isArray(messages) ? messages : []
  const selectedLabels = new Set()

  for (const message of items) {
    if (message?.role !== 'user') {
      continue
    }
    const text = String(message.text || '').trim()
    const selectedMatch = text.match(/^选择(.+)$/) || text.match(/用户选择报销场景[:：]\s*([^\n\r]+)/)
    if (selectedMatch?.[1]) {
      selectedLabels.add(selectedMatch[1].trim())
    } else if (text === '我要报销') {
      selectedLabels.add(text)
    }
  }

  if (!selectedLabels.size) {
    return items
  }

  return items.map((message) => {
    if (
      message?.role !== 'assistant'
        || message.suggestedActionsLocked
        || !Array.isArray(message.suggestedActions)
        || !message.suggestedActions.length
    ) {
      return message
    }

    const selectedAction = message.suggestedActions.find((action) =>
      selectedLabels.has(String(action?.label || action?.payload?.expense_type_label || '').trim())
    )
    if (!selectedAction) {
      return message
    }

    return {
      ...message,
      suggestedActionsLocked: true,
      selectedSuggestedActionKey: buildSuggestedActionKey(selectedAction),
      selectedSuggestedActionLabel: String(selectedAction.label || selectedAction?.payload?.expense_type_label || '').trim()
    }
  })
}
