import { computed, ref } from 'vue'
import {
  FLOW_RUN_DETAIL_REFRESH_TIMEOUT_MS,
  formatFlowDuration,
  resolveFinishedTimestamp,
  resolveSemanticPhaseDurations,
  resolveStartedTimestamp,
  resolveToolCallDurationMs
} from './travelReimbursementFlowTiming.js'
import {
  buildApplicationDuplicateDetail,
  buildApplicationSubmitSuccessDetail,
  isDuplicateApplicationPayload as isDuplicateApplicationPayloadModel,
  isSavedReimbursementDraftPayload,
  isSubmittedApplicationPayload,
  resolveToolCallFlowMeta as resolveToolCallFlowMetaModel,
  summarizeDraftRiskReviewDetail,
  summarizeFlowToolCall as summarizeFlowToolCallModel
} from './travelReimbursementFlowToolModel.js'

export function useTravelReimbursementFlow({
  activeSessionType,
  reviewDrawerMode,
  insightPanelCollapsed,
  isKnowledgeSession,
  fetchAgentRunDetail,
  buildLocalIntentPreview,
  buildLocalExtractionProgressMessages,
  summarizeSemanticIntentDetail,
  summarizeSemanticParseDetail,
  SCENARIO_LABELS,
  INTENT_LABELS,
  EXPENSE_TYPE_LABELS,
  FLOW_STEP_FALLBACKS,
  REVIEW_DRAWER_MODE_FLOW,
  REVIEW_DRAWER_MODE_REVIEW,
  FLOW_STEP_STATUS_PENDING,
  FLOW_STEP_STATUS_RUNNING,
  FLOW_STEP_STATUS_COMPLETED,
  FLOW_STEP_STATUS_FAILED
}) {
  const flowRunId = ref('')
  const flowSessionType = ref('')
  const flowStartedAt = ref(0)
  const flowFinishedAt = ref(0)
  const flowSteps = ref([])
  const flowRefreshBusy = ref(false)
  const flowTick = ref(Date.now())
  let flowTickTimer = 0
  const flowSimulationTimers = []

  const activeFlowSteps = computed(() => (
    flowSessionType.value === resolveCurrentFlowSessionType()
      ? flowSteps.value
      : []
  ))
  const completedFlowStepCount = computed(
    () => activeFlowSteps.value.filter((step) => step.status === FLOW_STEP_STATUS_COMPLETED).length
  )
  const rawRunningFlowStep = computed(
    () => flowSteps.value.find((step) => step.status === FLOW_STEP_STATUS_RUNNING) || null
  )
  const runningFlowStep = computed(
    () => activeFlowSteps.value.find((step) => step.status === FLOW_STEP_STATUS_RUNNING) || null
  )
  const visibleFlowSteps = computed(() => {
    const visibleSteps = []
    for (const step of activeFlowSteps.value) {
      visibleSteps.push(step)
      if (step.status !== FLOW_STEP_STATUS_COMPLETED) {
        break
      }
    }
    return visibleSteps
  })
  const flowOverallStatusTone = computed(() => {
    if (activeFlowSteps.value.some((step) => step.status === FLOW_STEP_STATUS_FAILED)) {
      return 'failed'
    }
    if (runningFlowStep.value) {
      return 'running'
    }
    if (
      activeFlowSteps.value.length
        && completedFlowStepCount.value === activeFlowSteps.value.length
        && flowStartedAt.value
    ) {
      return 'completed'
    }
    return 'pending'
  })
  const flowOverallStatusText = computed(() => {
    const total = activeFlowSteps.value.length
    const completed = completedFlowStepCount.value
    if (flowOverallStatusTone.value === 'failed') {
      return `异常 ${completed}/${total}`
    }
    if (flowOverallStatusTone.value === 'completed') {
      return `已完成 ${total}/${total}`
    }
    if (flowOverallStatusTone.value === 'running') {
      return `执行中 ${completed}/${total}`
    }
    return total ? `待执行 0/${total}` : '暂无流程'
  })
  const flowTotalDurationText = computed(() => {
    if (!flowStartedAt.value) {
      return '--'
    }

    const finishedAt = flowFinishedAt.value || (runningFlowStep.value ? flowTick.value : 0)
    if (finishedAt > flowStartedAt.value) {
      return formatFlowDuration(finishedAt - flowStartedAt.value)
    }

    const measuredDuration = activeFlowSteps.value.reduce((total, step) => {
      const duration = Number(step.durationMs)
      return total + (Number.isFinite(duration) && duration > 0 ? duration : 0)
    }, 0)
    return measuredDuration ? formatFlowDuration(measuredDuration) : '--'
  })

  function resolveCurrentFlowSessionType() {
    return String(activeSessionType?.value || '').trim()
  }

  function startFlowTick() {
    if (flowTickTimer) {
      return
    }
    flowTickTimer = window.setInterval(() => {
      flowTick.value = Date.now()
    }, 250)
  }

  function stopFlowRuntime() {
    if (flowTickTimer) {
      window.clearInterval(flowTickTimer)
      flowTickTimer = 0
    }
    clearFlowSimulationTimers()
  }

  function clearFlowSimulationTimers() {
    while (flowSimulationTimers.length) {
      const timerId = flowSimulationTimers.pop()
      window.clearTimeout(timerId)
      window.clearInterval(timerId)
    }
  }

  function resetFlowRun(options = {}) {
    clearFlowSimulationTimers()
    const shouldOpenDrawer = options.openDrawer !== false
    const startedAt = Number(options.startedAt)
    flowRunId.value = ''
    flowSessionType.value = String(options.sessionType || resolveCurrentFlowSessionType()).trim()
    flowStartedAt.value = Number.isFinite(startedAt) && startedAt >= 0 ? startedAt : Date.now()
    flowFinishedAt.value = 0
    if (shouldOpenDrawer) {
      reviewDrawerMode.value = REVIEW_DRAWER_MODE_FLOW
      insightPanelCollapsed.value = false
    }
    flowSteps.value = []
  }

  function findFlowDefinition(key) {
    return FLOW_STEP_FALLBACKS[key] || null
  }

  function normalizeFlowStepPatch(key, patch = {}) {
    const definition = findFlowDefinition(key) || {}
    const normalizedPatch = typeof patch === 'string' ? { detail: patch } : { ...patch }
    return {
      title: normalizedPatch.title || definition.title || '智能体工具调用',
      tool: normalizedPatch.tool || definition.tool || 'AgentTool',
      detail: normalizedPatch.detail || definition.runningText || '',
      ...normalizedPatch
    }
  }

  function createFlowStep(key, patch = {}) {
    const normalizedPatch = normalizeFlowStepPatch(key, patch)
    return {
      key,
      index: flowSteps.value.length + 1,
      title: normalizedPatch.title,
      tool: normalizedPatch.tool,
      status: normalizedPatch.status || FLOW_STEP_STATUS_PENDING,
      detail: normalizedPatch.detail || '',
      durationMs: normalizedPatch.durationMs ?? null,
      startedAt: normalizedPatch.startedAt || 0,
      finishedAt: normalizedPatch.finishedAt || 0,
      error: normalizedPatch.error || '',
      deferredCompletion: Boolean(normalizedPatch.deferredCompletion),
      syntheticTiming: Boolean(normalizedPatch.syntheticTiming)
    }
  }

  function normalizeFlowStepIndexes(steps) {
    return steps.map((step, index) => ({ ...step, index: index + 1 }))
  }

  function upsertFlowStep(key, patch) {
    const existingStep = flowSteps.value.find((step) => step.key === key)
    if (!existingStep) {
      const nextStep = createFlowStep(key, patch)
      flowSteps.value = normalizeFlowStepIndexes([...flowSteps.value, nextStep])
      return
    }
    const normalizedPatch = normalizeFlowStepPatch(key, patch)
    flowSteps.value = flowSteps.value.map((step) => (
      step.key === key ? { ...step, ...normalizedPatch } : step
    ))
  }

  function startFlowStep(key, patch = {}) {
    if (!flowSessionType.value) {
      flowSessionType.value = resolveCurrentFlowSessionType()
    }
    const normalizedPatch = normalizeFlowStepPatch(key, patch)
    const explicitStartedAt = Number(normalizedPatch.startedAt)
    const startedAt = Number.isFinite(explicitStartedAt) && explicitStartedAt > 0
      ? explicitStartedAt
      : Date.now()
    flowFinishedAt.value = 0
    upsertFlowStep(key, {
      ...normalizedPatch,
      status: FLOW_STEP_STATUS_RUNNING,
      detail: normalizedPatch.detail,
      startedAt,
      finishedAt: 0,
      durationMs: null,
      error: '',
      syntheticTiming: Boolean(normalizedPatch.syntheticTiming)
    })
  }

  function completeFlowStep(key, detail = '', durationMs = null, patch = {}) {
    const now = Date.now()
    const definition = findFlowDefinition(key)
    const currentStep = flowSteps.value.find((step) => step.key === key)
    const explicitDuration = Number(durationMs)
    const hasExplicitDuration = Number.isFinite(explicitDuration) && explicitDuration >= 0
    const startedAt = currentStep?.startedAt || (hasExplicitDuration ? Math.max(0, now - explicitDuration) : 0)
    const measuredDuration = hasExplicitDuration
      ? explicitDuration
      : startedAt && !currentStep?.syntheticTiming
        ? Math.max(0, now - startedAt)
        : null
    upsertFlowStep(key, {
      ...patch,
      status: FLOW_STEP_STATUS_COMPLETED,
      detail: detail || definition?.completedText || '',
      startedAt,
      finishedAt: now,
      durationMs: measuredDuration,
      error: '',
      deferredCompletion: false,
      syntheticTiming: false
    })
    if (
      flowSteps.value.length
        && flowSteps.value.every((step) => [FLOW_STEP_STATUS_COMPLETED, FLOW_STEP_STATUS_FAILED].includes(step.status))
    ) {
      flowFinishedAt.value = now
    }
  }

  function failFlowStep(key, detail = '', error = '', patch = {}) {
    const now = Date.now()
    const definition = findFlowDefinition(key)
    const currentStep = flowSteps.value.find((step) => step.key === key)
    const startedAt = currentStep?.startedAt || now
    upsertFlowStep(key, {
      ...patch,
      status: FLOW_STEP_STATUS_FAILED,
      detail: detail || error || '调用失败',
      startedAt,
      finishedAt: now,
      durationMs: now - startedAt,
      error: String(error || definition?.title || '').trim()
    })
    flowFinishedAt.value = now
  }

  function completePendingFlowStep(key, detail = '', durationMs = null, patch = {}) {
    const patchObject = patch && typeof patch === 'object' ? { ...patch } : {}
    const refreshCompleted = Boolean(patchObject.refreshCompleted)
    delete patchObject.refreshCompleted
    const currentStep = flowSteps.value.find((step) => step.key === key)
    const normalizedDuration = Number(durationMs)
    const hasMeasuredDuration = Number.isFinite(normalizedDuration) && normalizedDuration > 0
    if (currentStep?.status === FLOW_STEP_STATUS_COMPLETED) {
      if (refreshCompleted && hasMeasuredDuration) {
        completeFlowStep(key, detail, normalizedDuration, patchObject)
      }
      return
    }
    if (!currentStep || currentStep.status === FLOW_STEP_STATUS_PENDING) {
      const revealOrder = flowSteps.value.length
      startFlowStep(key, { ...patchObject, deferredCompletion: true, syntheticTiming: !hasMeasuredDuration })
      const completionTimer = window.setTimeout(() => {
        completeFlowStep(
          key,
          detail || findFlowDefinition(key)?.completedText || '',
          hasMeasuredDuration ? normalizedDuration : null,
          { deferredCompletion: false }
        )
      }, 280 + Math.min(revealOrder, 4) * 180)
      flowSimulationTimers.push(completionTimer)
      return
    }
    completeFlowStep(key, detail, hasMeasuredDuration ? normalizedDuration : null, patchObject)
  }

  function failCurrentFlowStep(error) {
    clearFlowSimulationTimers()
    const currentStep = rawRunningFlowStep.value || flowSteps.value.find((step) => step.status === FLOW_STEP_STATUS_PENDING)
    failFlowStep(
      currentStep?.key || 'orchestrator-error',
      error?.message || '智能体调用失败',
      error?.message || '',
      currentStep ? {} : { title: '流程调用', tool: 'Orchestrator' }
    )
  }

  function startSemanticFlowPreview(rawText, options = {}) {
    clearFlowSimulationTimers()
    const intentPreview = buildLocalIntentPreview(rawText, activeSessionType.value, { intentLabels: INTENT_LABELS })
    const extractionMessages = buildLocalExtractionProgressMessages(rawText, options)

    const completeIntentTimer = window.setTimeout(() => {
      const currentStep = flowSteps.value.find((step) => step.key === 'intent')
      if (currentStep?.status === FLOW_STEP_STATUS_COMPLETED || currentStep?.status === FLOW_STEP_STATUS_FAILED) {
        return
      }
      completePendingFlowStep('intent', intentPreview, null)
    }, 260)
    flowSimulationTimers.push(completeIntentTimer)

    const startExtractionTimer = window.setTimeout(() => {
      const currentStep = flowSteps.value.find((step) => step.key === 'extraction')
      if (currentStep?.status === FLOW_STEP_STATUS_COMPLETED || currentStep?.status === FLOW_STEP_STATUS_FAILED) {
        return
      }
      startFlowStep('extraction', extractionMessages[0] || FLOW_STEP_FALLBACKS.extraction.runningText)

      if (extractionMessages.length <= 1) {
        return
      }

      let index = 1
      const detailTimer = window.setInterval(() => {
        const runningStep = flowSteps.value.find((step) => step.key === 'extraction')
        if (!runningStep || runningStep.status !== FLOW_STEP_STATUS_RUNNING) {
          window.clearInterval(detailTimer)
          return
        }
        upsertFlowStep('extraction', {
          detail: extractionMessages[index] || extractionMessages[extractionMessages.length - 1]
        })
        index = Math.min(index + 1, extractionMessages.length - 1)
      }, 650)
      flowSimulationTimers.push(detailTimer)
    }, 420)
    flowSimulationTimers.push(startExtractionTimer)
  }

  function startExpenseSceneSelectionFlowPreview(rawText) {
    clearFlowSimulationTimers()
    const intentPreview = buildLocalIntentPreview(rawText, activeSessionType.value, { intentLabels: INTENT_LABELS })
    const completeIntentTimer = window.setTimeout(() => {
      completePendingFlowStep('intent', intentPreview, null)
    }, 220)
    flowSimulationTimers.push(completeIntentTimer)

    const startSelectionTimer = window.setTimeout(() => {
      startFlowStep('expense-scene-selection', {
        detail: '报销意图已确认，但费用场景还不明确；暂停信息抽取，等待用户先选择报销场景。'
      })
    }, 320)
    flowSimulationTimers.push(startSelectionTimer)
  }

  function startExpenseIntentConfirmationFlowPreview(rawText) {
    clearFlowSimulationTimers()
    const intentPreview = buildLocalIntentPreview(rawText, activeSessionType.value, { intentLabels: INTENT_LABELS })
    const completeIntentTimer = window.setTimeout(() => {
      completePendingFlowStep('intent', intentPreview, null)
    }, 220)
    flowSimulationTimers.push(completeIntentTimer)

    const startConfirmationTimer = window.setTimeout(() => {
      startFlowStep('expense-intent-confirmation', {
        detail: '识别到业务事项描述，但是否发起报销还不明确；暂停信息抽取，等待用户确认。'
      })
    }, 320)
    flowSimulationTimers.push(startConfirmationTimer)
  }

  function startExpenseSceneSelectionAfterIntentConfirmation(rawText) {
    clearFlowSimulationTimers()
    completePendingFlowStep('expense-intent-confirmation', '用户已确认要发起报销', null)
    startFlowStep('expense-scene-selection', {
      detail: '报销意图已确认，但费用场景还不明确；暂停信息抽取，等待用户先选择报销场景。'
    })
    if (reviewDrawerMode.value !== REVIEW_DRAWER_MODE_FLOW) {
      reviewDrawerMode.value = REVIEW_DRAWER_MODE_FLOW
    }
  }

  function isExpenseSceneSelectionResult(payload) {
    const result = payload?.result && typeof payload.result === 'object' ? payload.result : {}
    if (result.review_payload) {
      return false
    }
    return (Array.isArray(result.suggested_actions) ? result.suggested_actions : []).some(
      (item) => String(item?.action_type || '').trim() === 'select_expense_type'
    )
  }

  function startReviewActionFlowStep(reviewAction) {
    if (reviewAction !== 'next_step') {
      return
    }

    startFlowStep('pre-submit-review', {
      title: '自动检测与风险识别',
      tool: 'ExpenseClaimService.submit_claim',
      detail: '正在校验财务规则、风险规则和审批路径...'
    })
  }

  function startExpenseClaimDraftFlowStep(reviewAction, options = {}) {
    if (isKnowledgeSession.value) {
      return
    }
    if (options.waitForSceneSelection) {
      return
    }
    if (reviewAction === 'next_step') {
      startReviewActionFlowStep(reviewAction)
      return
    }

    const attachmentCount = Math.max(0, Number(options.attachmentCount || 0))
    const configs = {
      save_draft: {
        key: 'expense-claim-draft',
        title: '保存报销草稿',
        tool: 'database.expense_claims.save_or_submit',
        detail: '正在把已确认信息保存为草稿...'
      },
      link_to_existing_draft: {
        key: 'attachment-association',
        title: '票据关联草稿',
        tool: 'database.expense_claims.save_or_submit',
        detail: '正在把本次票据关联到现有草稿...'
      },
      create_new_claim_from_documents: {
        key: 'expense-claim-draft',
        title: '新建报销草稿',
        tool: 'database.expense_claims.save_or_submit',
        detail: '正在根据当前票据新建报销草稿...'
      }
    }
    const defaultConfigBySessionType = {
      application: {
        key: 'application-review-preview',
        title: '申请信息核对',
        tool: 'user_agent.application_review_preview',
        detail: '正在整理申请事项和待补充信息...'
      },
      approval: {
        key: 'approval-review-preview',
        title: '审核信息核对',
        tool: 'user_agent.approval_review_preview',
        detail: '正在整理待审核单据、风险点和审核建议...'
      }
    }
    const config = configs[reviewAction] || defaultConfigBySessionType[String(activeSessionType.value || '').trim()] || {
      key: 'expense-review-preview',
      title: '报销信息核对',
      tool: 'user_agent.expense_review_preview',
      detail: attachmentCount
        ? '正在根据 OCR 结果整理核对信息...'
        : '正在整理识别结果和右侧核对信息...'
    }

    startFlowStep(config.key, {
      title: config.title,
      tool: config.tool,
      detail: config.detail
    })

    if (['save_draft', 'link_to_existing_draft', 'create_new_claim_from_documents'].includes(reviewAction)) {
      startFlowStep('draft-risk-review', {
        title: '草稿风险识别',
        tool: 'RuleEngine',
        detail: '正在校验申请单关联、票据完整性、金额口径和行程一致性...'
      })
    }
  }

  function isApplicationSessionActive() {
    return String(activeSessionType?.value || '').trim() === 'application'
  }

  function isDuplicateApplicationPayload(payload) {
    return isDuplicateApplicationPayloadModel(payload, {
      applicationSessionActive: isApplicationSessionActive()
    })
  }

  function resolveToolCallFlowMeta(toolCall, index) {
    return resolveToolCallFlowMetaModel(toolCall, index, {
      applicationSessionActive: isApplicationSessionActive()
    })
  }

  function summarizeFlowToolCall(toolCall) {
    return summarizeFlowToolCallModel(toolCall, {
      applicationSessionActive: isApplicationSessionActive()
    })
  }

  function mergeFlowRunDetail(run) {
    const runStartedAt = resolveStartedTimestamp(run)
    const runFinishedAt = resolveFinishedTimestamp(run)
    if (runStartedAt) {
      flowStartedAt.value = runStartedAt
    }
    const toolCalls = Array.isArray(run?.tool_calls) ? run.tool_calls : []
    if (run?.semantic_parse && flowSteps.value.some((step) => step.key === 'intent')) {
      clearFlowSimulationTimers()
      const semanticDurations = resolveSemanticPhaseDurations(run)
      completePendingFlowStep(
        'intent',
        summarizeSemanticIntentDetail(run.semantic_parse, {
          scenarioLabels: SCENARIO_LABELS,
          intentLabels: INTENT_LABELS,
          expenseTypeLabels: EXPENSE_TYPE_LABELS,
          fallbackText: FLOW_STEP_FALLBACKS.intent.completedText
        }),
        semanticDurations.intentMs,
        { refreshCompleted: true }
      )
      completePendingFlowStep(
        'extraction',
        summarizeSemanticParseDetail(run.semantic_parse, run?.ontology_json || {}),
        semanticDurations.extractionMs,
        { refreshCompleted: true }
      )
    }

    const hasApplicationSubmitSuccess = flowSteps.value.some((step) => step.key === 'application-submit-success')
    toolCalls.forEach((toolCall, index) => {
      const meta = resolveToolCallFlowMeta(toolCall, index)
      if (!meta) {
        return
      }
      if (hasApplicationSubmitSuccess && isApplicationSessionActive() && meta.key !== 'application-submit-success') {
        return
      }
      const failed = String(toolCall?.status || '').toLowerCase() === 'failed'
      if (failed) {
        failFlowStep(meta.key, toolCall?.error_message || summarizeFlowToolCall(toolCall), toolCall?.error_message || '', meta)
      } else {
        const toolDurationMs = resolveToolCallDurationMs(toolCall, index, toolCalls, run)
        completePendingFlowStep(
          meta.key,
          summarizeFlowToolCall(toolCall),
          toolDurationMs,
          { ...meta, refreshCompleted: true }
        )
      }
    })

    if (String(run?.status || '').toLowerCase() === 'failed') {
      failCurrentFlowStep({ message: run?.error_message || '智能体调用失败' })
      return
    }
    if (
      runFinishedAt
        && flowSteps.value.length
        && flowSteps.value.every((step) => [FLOW_STEP_STATUS_COMPLETED, FLOW_STEP_STATUS_FAILED].includes(step.status))
    ) {
      flowFinishedAt.value = runFinishedAt
    }
  }

  function completeFlowResult(payload, run = null) {
    const answer = String(payload?.result?.answer || payload?.result?.message || '').trim()
    if (!answer && !payload?.result) {
      return
    }
    if (isSubmittedApplicationPayload(payload)) {
      completePendingFlowStep(
        'application-submit-success',
        buildApplicationSubmitSuccessDetail(payload),
        null,
        { title: '申请单提交成功', tool: 'ApplicationSubmit' }
      )
    }
    if (isDuplicateApplicationPayload(payload)) {
      completePendingFlowStep(
        'application-submit-success',
        buildApplicationDuplicateDetail(payload),
        null,
        { title: '重复申请已拦截', tool: 'ApplicationSubmit' }
      )
    }
    if (isSavedReimbursementDraftPayload(payload)) {
      completePendingFlowStep(
        'draft-risk-review',
        summarizeDraftRiskReviewDetail(payload),
        null,
        { title: '草稿风险识别', tool: 'RuleEngine' }
      )
    }
    const sceneSelectionPending = isExpenseSceneSelectionResult(payload)
    flowSteps.value
      .filter((step) => ![FLOW_STEP_STATUS_COMPLETED, FLOW_STEP_STATUS_FAILED].includes(step.status))
      .filter((step) => !step.deferredCompletion)
      .forEach((step) => {
        const detail = sceneSelectionPending && step.key === 'expense-scene-selection'
          ? '已暂停后续识别，请先在主对话中选择报销场景。'
          : resolveFlowStepDetail({ ...step, status: FLOW_STEP_STATUS_COMPLETED })
        completeFlowStep(step.key, detail)
      })
    const runFinishedAt = resolveFinishedTimestamp(run)
    flowFinishedAt.value = flowSteps.value.some(
      (step) => ![FLOW_STEP_STATUS_COMPLETED, FLOW_STEP_STATUS_FAILED].includes(step.status)
    )
      ? 0
      : runFinishedAt || Date.now()
  }

  async function refreshFlowRunDetail() {
    if (!flowRunId.value || flowRefreshBusy.value) {
      return null
    }
    flowRefreshBusy.value = true
    try {
      const run = await Promise.race([
        fetchAgentRunDetail(flowRunId.value),
        new Promise((resolve) => {
          globalThis.setTimeout(() => resolve(null), FLOW_RUN_DETAIL_REFRESH_TIMEOUT_MS)
        })
      ])
      if (!run) {
        return null
      }
      mergeFlowRunDetail(run)
      return run
    } catch (error) {
      console.warn('Failed to refresh agent run detail:', error)
      return null
    } finally {
      flowRefreshBusy.value = false
    }
  }

  function formatFlowStepDuration(step) {
    if (step?.status === FLOW_STEP_STATUS_RUNNING && step.startedAt) {
      return formatFlowDuration(flowTick.value - step.startedAt)
    }
    return formatFlowDuration(step?.durationMs)
  }

  function resolveFlowStepStatusLabel(step) {
    const status = String(step?.status || '').trim()
    if (status === FLOW_STEP_STATUS_COMPLETED) {
      return '完成'
    }
    if (status === FLOW_STEP_STATUS_RUNNING) {
      return '执行中'
    }
    if (status === FLOW_STEP_STATUS_FAILED) {
      return '异常'
    }
    return '待执行'
  }

  function resolveFlowStepDetail(step) {
    const detail = String(step?.detail || '').trim()
    if (detail) {
      return detail
    }
    const definition = findFlowDefinition(step?.key)
    if (step?.status === FLOW_STEP_STATUS_COMPLETED) {
      return definition?.completedText || '步骤已完成'
    }
    if (step?.status === FLOW_STEP_STATUS_RUNNING) {
      return definition?.runningText || '正在执行当前步骤...'
    }
    if (step?.status === FLOW_STEP_STATUS_FAILED) {
      return step?.error || '步骤执行异常'
    }
    return definition?.runningText ? `等待${definition.title || '当前步骤'}...` : '等待智能体调度...'
  }

  return {
    flowRunId,
    flowSessionType,
    flowStartedAt,
    flowFinishedAt,
    flowSteps,
    activeFlowSteps,
    visibleFlowSteps,
    flowRefreshBusy,
    flowTick,
    completedFlowStepCount,
    runningFlowStep,
    flowOverallStatusTone,
    flowOverallStatusText,
    flowTotalDurationText,
    clearFlowSimulationTimers,
    resetFlowRun,
    findFlowDefinition,
    normalizeFlowStepPatch,
    createFlowStep,
    normalizeFlowStepIndexes,
    upsertFlowStep,
    startFlowTick,
    stopFlowRuntime,
    startFlowStep,
    completeFlowStep,
    failFlowStep,
    completePendingFlowStep,
    failCurrentFlowStep,
    startSemanticFlowPreview,
    startExpenseSceneSelectionFlowPreview,
    startExpenseIntentConfirmationFlowPreview,
    startExpenseSceneSelectionAfterIntentConfirmation,
    isExpenseSceneSelectionResult,
    startReviewActionFlowStep,
    startExpenseClaimDraftFlowStep,
    resolveToolCallFlowMeta,
    summarizeFlowToolCall,
    mergeFlowRunDetail,
    completeFlowResult,
    refreshFlowRunDetail,
    formatFlowStepDuration,
    resolveFlowStepStatusLabel,
    resolveFlowStepDetail
  }
}
