import {
  APPLICATION_TRANSPORT_MODE_OPTIONS,
  normalizeApplicationPreview,
  normalizeTransportModeOption
} from '../../utils/expenseApplicationPreview.js'
import { fetchStewardSlotDecision } from '../../services/steward.js'
import { resolveStewardTypewriterNextIndex } from './stewardTypewriter.js'
import {
  APPLICATION_MISSING_LABEL_ONTOLOGY_FIELD_MAP,
  APPLICATION_NON_BLOCKING_ONTOLOGY_FIELDS,
  APPLICATION_PREVIEW_FIELD_ACTION_SET,
  APPLICATION_PREVIEW_ONTOLOGY_FIELD_MAP,
  ONTOLOGY_FIELD_APPLICATION_PREVIEW_KEY_MAP,
  ONTOLOGY_FIELD_DISPLAY_LABEL_MAP,
  STEWARD_ASSISTANT_NAME,
  STEWARD_DELEGATED_THINKING_CHUNK_SIZE,
  STEWARD_DELEGATED_THINKING_INTERVAL_MS,
  STEWARD_DELEGATED_TYPEWRITER_CHUNK_SIZE,
  STEWARD_DELEGATED_TYPEWRITER_INTERVAL_MS
} from './travelReimbursementSubmitConstants.js'

export function createStewardDelegationHelpers({
  currentInsight,
  insightPanelCollapsed,
  messages,
  nextTick,
  persistSessionState,
  resetFlowRun,
  scrollToBottom
}) {
    function isStewardDelegatedRun(options = {}) {
      return Boolean(options?.stewardContinuation && typeof options.stewardContinuation === 'object')
    }

    function resolveStewardDelegatedActionLabel(sessionType = '') {
      return String(sessionType || '').trim() === 'application'
        ? '申请单核对'
        : '报销单核对'
    }

    function buildStewardDelegatedPlan(continuation = null, thinkingEvents = [], streamStatus = 'streaming') {
      return {
        planId: String(continuation?.planId || continuation?.plan_id || 'steward_delegation').trim(),
        planStatus: 'delegating',
        summary: '',
        visibleThinkingEventCount: Number.MAX_SAFE_INTEGER,
        initialSummaryOnly: true,
        thinkingEvents,
        tasks: [],
        attachmentGroups: [],
        confirmationGroups: [],
        streamStatus
      }
    }

    function resolveApplicationPreviewMissingFieldsForSteward(preview = {}) {
      const normalized = normalizeApplicationPreview(preview)
      return Array.isArray(normalized.missingFields) ? normalized.missingFields : []
    }

    function isBlockingApplicationOntologyField(key = '') {
      const normalizedKey = String(key || '').trim()
      return Boolean(normalizedKey && !APPLICATION_NON_BLOCKING_ONTOLOGY_FIELDS.has(normalizedKey))
    }

    function resolveBlockingApplicationMissingFieldsForSteward(preview = {}) {
      return resolveApplicationPreviewMissingFieldsForSteward(preview).filter((label) => {
        const ontologyKey = APPLICATION_MISSING_LABEL_ONTOLOGY_FIELD_MAP[String(label || '').trim()] || ''
        return !ontologyKey || isBlockingApplicationOntologyField(ontologyKey)
      })
    }

    function buildStewardApplicationPreviewSuggestedActions(preview = {}) {
      const normalized = normalizeApplicationPreview(preview)
      const missingFields = resolveApplicationPreviewMissingFieldsForSteward(normalized)
      if (!missingFields.includes('出行方式')) {
        return []
      }
      const iconMap = {
        火车: 'mdi mdi-train',
        飞机: 'mdi mdi-airplane',
        轮船: 'mdi mdi-ferry'
      }
      return APPLICATION_TRANSPORT_MODE_OPTIONS.map((mode) => ({
        action_type: APPLICATION_PREVIEW_FIELD_ACTION_SET,
        label: mode,
        description: `选择${mode}后，由小财管家继续查询票价并测算费用。`,
        icon: iconMap[mode] || 'mdi mdi-map-marker-path',
        payload: {
          field_key: 'transportMode',
          field_label: '出行方式',
          value: mode,
          applicationPreview: normalized,
          steward_delegated_field_completion: true
        }
      }))
    }

    function resolveStewardContinuationCurrentTask(continuation = null) {
      const task = continuation?.currentTask || continuation?.current_task || null
      return task && typeof task === 'object' ? task : null
    }

    function normalizeCanonicalFieldList(fields = []) {
      const normalized = []
      if (!Array.isArray(fields)) {
        return normalized
      }
      fields.forEach((field) => {
        const key = String(field || '').trim()
        if (key && !normalized.includes(key)) {
          normalized.push(key)
        }
      })
      return normalized
    }

    function buildStewardSlotDecisionOntologyFields(preview = {}, continuation = null) {
      const normalizedPreview = normalizeApplicationPreview(preview)
      const previewFields = normalizedPreview.fields || {}
      const task = resolveStewardContinuationCurrentTask(continuation)
      const taskFields = task?.ontology_fields || task?.ontologyFields || {}
      const fields = {}
      Object.entries(taskFields || {}).forEach(([key, value]) => {
        const normalizedKey = String(key || '').trim()
        const normalizedValue = String(value || '').trim()
        if (normalizedKey && normalizedValue) {
          fields[normalizedKey] = normalizedValue
        }
      })
      Object.entries(APPLICATION_PREVIEW_ONTOLOGY_FIELD_MAP).forEach(([previewKey, ontologyKey]) => {
        const value = String(previewFields[previewKey] || '').trim()
        if (value && value !== '待补充' && !fields[ontologyKey]) {
          fields[ontologyKey] = value
        }
      })
      return fields
    }

    function buildStewardSlotDecisionMissingFields(preview = {}, continuation = null, ontologyFields = {}) {
      const task = resolveStewardContinuationCurrentTask(continuation)
      const taskMissingFields = normalizeCanonicalFieldList(task?.missing_fields || task?.missingFields || [])
        .filter((key) => isBlockingApplicationOntologyField(key) && !String(ontologyFields[key] || '').trim())
      if (taskMissingFields.length) {
        return taskMissingFields
      }
      return resolveApplicationPreviewMissingFieldsForSteward(preview)
        .map((label) => APPLICATION_MISSING_LABEL_ONTOLOGY_FIELD_MAP[String(label || '').trim()] || '')
        .filter((key, index, list) =>
          key &&
          isBlockingApplicationOntologyField(key) &&
          !String(ontologyFields[key] || '').trim() &&
          list.indexOf(key) === index
        )
    }

    async function fetchStewardApplicationSlotDecision(preview = {}, rawText = '', continuation = null) {
      const ontologyFields = buildStewardSlotDecisionOntologyFields(preview, continuation)
      const missingFields = buildStewardSlotDecisionMissingFields(preview, continuation, ontologyFields)
      try {
        return await fetchStewardSlotDecision({
          task_type: 'expense_application',
          user_message: String(rawText || '').trim(),
          ontology_fields: ontologyFields,
          missing_fields: missingFields,
          task_context: {
            steward_continuation: continuation || null,
            application_preview: normalizeApplicationPreview(preview)
          }
        }, {
          timeoutMs: 45000,
          timeoutMessage: '小财管家字段决策超时，已按当前本体缺口继续追问。'
        })
      } catch (error) {
        console.warn('Steward slot decision failed:', error)
        return null
      }
    }

    function formatStewardDecisionUserText(text = '') {
      let formatted = String(text || '').trim()
      Object.entries(ONTOLOGY_FIELD_DISPLAY_LABEL_MAP).forEach(([fieldKey, label]) => {
        const escapedKey = fieldKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
        formatted = formatted
          .replace(new RegExp(`（\\s*${escapedKey}\\s*）`, 'g'), '')
          .replace(new RegExp(`\\(\\s*${escapedKey}\\s*\\)`, 'g'), '')
          .replace(new RegExp(`\\b${escapedKey}\\b`, 'g'), label)
      })
      return formatted.replace(/\s{2,}/g, ' ').trim()
    }

    function buildStewardSlotDecisionMessage(decision = null, preview = {}, fallbackText = '') {
      if (!decision || String(decision.next_action || '').trim() !== 'ask_user') {
        return fallbackText
      }
      const question = formatStewardDecisionUserText(decision.question || '')
      const rationale = formatStewardDecisionUserText(decision.rationale || '')
      const parts = [
        '我已经识别出这一步要先处理申请单，但当前还不能直接生成可提交的申请核对表。',
        '',
        rationale ? `**原因是：${rationale}**` : '',
        '',
        question || buildStewardApplicationPreviewMessage(preview, fallbackText)
      ].filter((item) => item !== '')
      return parts.join('\n')
    }

    function buildStewardSlotDecisionSuggestedActions(decision = null, preview = {}) {
      if (!decision || String(decision.next_action || '').trim() !== 'ask_user') {
        return []
      }
      const normalizedPreview = normalizeApplicationPreview(preview)
      const iconMap = {
        火车: 'mdi mdi-train',
        飞机: 'mdi mdi-airplane',
        轮船: 'mdi mdi-ferry'
      }
      const actions = Array.isArray(decision.options) ? decision.options : []
      return actions.map((option) => {
        const canonicalField = String(option?.field_key || option?.fieldKey || '').trim()
        if (canonicalField && !isBlockingApplicationOntologyField(canonicalField)) {
          return null
        }
        const fieldKey = ONTOLOGY_FIELD_APPLICATION_PREVIEW_KEY_MAP[canonicalField] || canonicalField
        const value = String(option?.value || option?.label || '').trim()
        const label = String(option?.label || value).trim()
        const normalizedValue = fieldKey === 'transportMode'
          ? normalizeTransportModeOption(value || label, '')
          : value
        if (!fieldKey || !value || !label) {
          return null
        }
        if (fieldKey === 'transportMode' && !normalizedValue) {
          return null
        }
        return {
          action_type: APPLICATION_PREVIEW_FIELD_ACTION_SET,
          label,
          description: String(option?.description || '').trim() || `选择${label}后，由小财管家继续测算并生成核对结果。`,
          icon: iconMap[label] || iconMap[value] || 'mdi mdi-form-select',
          payload: {
            field_key: fieldKey,
            field_label: ONTOLOGY_FIELD_DISPLAY_LABEL_MAP[canonicalField] || label,
            value: normalizedValue,
            applicationPreview: normalizedPreview,
            steward_delegated_field_completion: true
          }
        }
      }).filter(Boolean)
    }

    function buildStewardApplicationPreviewMessage(preview = {}, fallbackText = '') {
      const normalized = normalizeApplicationPreview(preview)
      const fields = normalized.fields || {}
      const missingFields = resolveBlockingApplicationMissingFieldsForSteward(normalized)
      if (!missingFields.length) {
        return fallbackText
      }

      if (missingFields.includes('出行方式')) {
        return [
          '我已经识别出这一步要先处理出差申请，但现在还不能生成可提交的申请核对表。',
          '',
          '**原因是：还缺少“出行方式”。**',
          '',
          `本次申请是前往${fields.location || '目的地'}的差旅事项，出行方式会影响交通费用口径和系统预估金额。`,
          '',
          '请先告诉我您打算怎么出行：**火车、飞机或轮船**。我会根据您的选择生成申请核对表，并同步费用测算，再继续判断是否可以提交申请。'
        ].join('\n')
      }

      return [
        '我已经识别出这一步要先处理申请单，但现在还不能生成可提交的申请核对表。',
        '',
        `**还需要您补充：${missingFields.join('、')}。**`,
        '',
        `请先补充 **${missingFields[0]}**。补齐后我再生成申请核对表，并继续推进下一步。`
      ].join('\n')
    }

    function shouldPauseStewardApplicationPreview(preview = {}) {
      const missingFields = resolveBlockingApplicationMissingFieldsForSteward(preview)
      // 仅缺出行方式时先交给认证签发端尝试应用个人记忆；其他缺失仍由 Steward 追问。
      return missingFields.some((field) => String(field || '').trim() !== '出行方式')
    }

    function extractStewardCarryLine(text = '', label = '') {
      const escapedLabel = String(label || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
      const match = String(text || '').match(new RegExp(`(?:^|\\n)${escapedLabel}[：:]([^\\n]+)`, 'u'))
      return match ? match[1].trim() : ''
    }

    function extractStewardDelegatedTaskTitle(text = '', sessionType = '') {
      const taskMatch = String(text || '').match(/请(?:先)?(?:创建申请单|填写报销单|继续填写报销单)[：:]([^。\n]+)/u)
      if (taskMatch?.[1]) {
        return taskMatch[1].trim()
      }
      return String(sessionType || '').trim() === 'application' ? '本次出差申请' : '本次费用报销'
    }

    function sanitizeStewardDelegatedTaskSummary(summary = '', sessionType = '') {
      const text = String(summary || '').trim()
      if (String(sessionType || '').trim() !== 'application') {
        return text
      }
      return text
        .replace(/交通方式和(?:预算|预计)?金额待补充/g, '交通方式待补充')
        .replace(/出行方式和(?:预算|预计)?金额待补充/g, '出行方式待补充')
        .replace(/交通方式及(?:预估|预计|预算)?费用/g, '交通方式')
        .replace(/出行方式及(?:预估|预计|预算)?费用/g, '出行方式')
        .replace(/(?:费用预算|预算费用|出差费用预算)(?:待补充|待确认|待填写|需补充|需要补充|未补充|缺失)?[，,、；;\s]*/g, '')
        .replace(/[，,、；;\s]*(?:预估|预计|预算)费用(?:待补充|待确认|待填写|需补充|需要补充|需确认|需要确认|未补充|缺失)?/g, '')
        .replace(/[，,、；;\s]*(?:预算|预计)?金额(?:待补充|待确认|待填写|需补充|需要补充|未补充|缺失)/g, '')
        .replace(/([，,、；;。])\1+/g, '$1')
        .replace(/[，,、；;\s]+。/g, '。')
        .replace(/[，,、；;\s]+$/g, '')
        .trim()
    }

    function summarizeApplicationPreviewForSteward(preview = {}) {
      const normalized = normalizeApplicationPreview(preview)
      const fields = normalized.fields || {}
      return [
        fields.time ? `时间：${fields.time}` : '',
        fields.location ? `地点：${fields.location}` : '',
        fields.reason ? `事由：${fields.reason}` : '',
        fields.applicationType ? `类型：${fields.applicationType}` : ''
      ].filter(Boolean).join('；')
    }

    function buildStewardDelegatedThinkingEvents(sessionType = '', continuation = null, context = {}) {
      const actionLabel = resolveStewardDelegatedActionLabel(sessionType)
      const eventPrefix = `steward-delegated-${Date.now()}-${Math.random().toString(16).slice(2)}`
      const rawText = String(context.rawText || '').trim()
      const taskTitle = extractStewardDelegatedTaskTitle(rawText, sessionType)
      const taskSummary = sanitizeStewardDelegatedTaskSummary(
        extractStewardCarryLine(rawText, '任务摘要'),
        sessionType
      )
      const identifiedInfo = summarizeApplicationPreviewForSteward(context.applicationPreview)
        || extractStewardCarryLine(rawText, '已识别信息')
      const carryMissingInfo = extractStewardCarryLine(rawText, '还需要补充')
      const applicationMissingFields = context.applicationPreview
        ? resolveBlockingApplicationMissingFieldsForSteward(context.applicationPreview)
        : []
      const missingInfo = applicationMissingFields.length
        ? applicationMissingFields.join('、')
        : carryMissingInfo
      const events = [
        {
          eventId: `${eventPrefix}-intent`,
          title: '理解当前任务',
          content: taskSummary
            ? `您确认先处理“${taskTitle}”。我把这一步理解为：${taskSummary}。`
            : `您确认先处理“${taskTitle}”，我会先生成${actionLabel}结果。`
        },
        {
          eventId: `${eventPrefix}-known`,
          title: '核对已知信息',
          content: identifiedInfo
            ? `当前已识别到：${identifiedInfo}。`
            : `当前先围绕“${taskTitle}”生成可核对内容，具体缺口会在核对结果里继续判断。`
        }
      ]
      if (missingInfo) {
        const transportMissing = /出行方式/.test(missingInfo)
        events.push({
          eventId: `${eventPrefix}-gap`,
          title: '判断待补充信息',
          content: transportMissing
            ? '这一步还没有说明出行方式。出行方式会影响交通费用测算，所以我会先请您选择火车、飞机或轮船，不会直接推进提交。'
            : `这一步还缺少${missingInfo}，我会先向您确认这些信息，不直接推进提交。`
        })
      } else {
        events.push({
          eventId: `${eventPrefix}-ready`,
          title: '判断下一步动作',
          content: `这一步的关键业务信息已形成核对结果。我会先请您检查${actionLabel}，确认后再继续入库、生成草稿或处理后续任务。`
        })
      }
      return events
    }

    function resolveStewardDelegatedFinalMeta(finalExtras = {}) {
      const sourceMeta = Array.isArray(finalExtras.meta) ? finalExtras.meta : []
      const sourceLabel = sourceMeta.find((item) =>
        String(item || '').trim() && String(item || '').trim() !== STEWARD_ASSISTANT_NAME
      )
      const requiresConfirmation = Boolean(
        finalExtras.applicationPreview ||
        finalExtras.reviewPayload ||
        (Array.isArray(finalExtras.suggestedActions) && finalExtras.suggestedActions.length)
      )
      return [
        STEWARD_ASSISTANT_NAME,
        requiresConfirmation ? '等待用户确认' : '已完成',
        sourceLabel || ''
      ].filter(Boolean).slice(0, 3)
    }

    function waitStewardDelegatedTick(intervalMs) {
      return new Promise((resolve) => {
        globalThis.setTimeout(resolve, intervalMs)
      })
    }

    async function typeStewardDelegatedMessage(messageId, finalText, finalExtras = {}, context = {}) {
      const continuation = finalExtras.stewardContinuation || context.stewardContinuation || null
      const pendingSuggestedActions = Array.isArray(finalExtras.suggestedActions)
        ? finalExtras.suggestedActions
        : []
      const message = messages.value.find((item) => item.id === messageId)
      if (!message) {
        return
      }

      message.text = ''
      message.assistantName = STEWARD_ASSISTANT_NAME
      message.meta = [STEWARD_ASSISTANT_NAME, '思考中']
      message.suggestedActions = []
      message.stewardContinuation = continuation
      message.stewardPlan = buildStewardDelegatedPlan(continuation, [], 'streaming')
      persistSessionState()
      nextTick(scrollToBottom)

      const typedEvents = []
      const thinkingEvents = buildStewardDelegatedThinkingEvents(context.sessionType, continuation, context)
      for (const eventData of thinkingEvents) {
        const event = {
          eventId: eventData.eventId,
          stage: 'delegated_action',
          title: eventData.title,
          content: '',
          status: 'running'
        }
        typedEvents.push(event)
        message.stewardPlan = buildStewardDelegatedPlan(continuation, [...typedEvents], 'streaming')
        persistSessionState()
        nextTick(scrollToBottom)

        const chars = Array.from(String(eventData.content || ''))
        for (let index = 0; index < chars.length;) {
          await waitStewardDelegatedTick(STEWARD_DELEGATED_THINKING_INTERVAL_MS)
          index = resolveStewardTypewriterNextIndex(chars, index, STEWARD_DELEGATED_THINKING_CHUNK_SIZE)
          event.content = chars.slice(0, index).join('')
          message.stewardPlan = buildStewardDelegatedPlan(continuation, [...typedEvents], 'streaming')
          if (index % STEWARD_DELEGATED_THINKING_CHUNK_SIZE === 0 || index === chars.length) {
            nextTick(scrollToBottom)
          }
        }
        event.content = String(eventData.content || '')
        event.status = 'completed'
        message.stewardPlan = buildStewardDelegatedPlan(continuation, [...typedEvents], 'streaming')
        persistSessionState()
      }

      const text = String(finalText || '')
      message.text = ''
      message.meta = [STEWARD_ASSISTANT_NAME, '输出中']
      message.suggestedActions = pendingSuggestedActions
      message.stewardPlan = buildStewardDelegatedPlan(continuation, [...typedEvents], 'typing')
      const chars = Array.from(text)
      for (let index = 0; index < chars.length;) {
        await waitStewardDelegatedTick(STEWARD_DELEGATED_TYPEWRITER_INTERVAL_MS)
        index = resolveStewardTypewriterNextIndex(chars, index, STEWARD_DELEGATED_TYPEWRITER_CHUNK_SIZE)
        message.text = chars.slice(0, index).join('')
        message.meta = [STEWARD_ASSISTANT_NAME, '输出中']
        message.stewardPlan = buildStewardDelegatedPlan(continuation, [...typedEvents], 'typing')
        if (index % STEWARD_DELEGATED_TYPEWRITER_CHUNK_SIZE === 0 || index === chars.length) {
          nextTick(scrollToBottom)
        }
      }

      Object.assign(message, finalExtras, {
        id: messageId,
        text,
        assistantName: STEWARD_ASSISTANT_NAME,
        meta: resolveStewardDelegatedFinalMeta(finalExtras),
        stewardContinuation: continuation,
        stewardPlan: buildStewardDelegatedPlan(continuation, [...typedEvents], 'completed')
      })
      persistSessionState()
      nextTick(scrollToBottom)
    }

    function resetStewardDelegatedInsightState() {
      resetFlowRun({ startedAt: 0, openDrawer: false })
      insightPanelCollapsed.value = true
      currentInsight.value = {
        intent: 'welcome',
        agent: null
      }
    }

  return {
    buildStewardApplicationPreviewMessage,
    buildStewardApplicationPreviewSuggestedActions,
    buildStewardDelegatedPlan,
    buildStewardSlotDecisionMessage,
    buildStewardSlotDecisionSuggestedActions,
    fetchStewardApplicationSlotDecision,
    isStewardDelegatedRun,
    resetStewardDelegatedInsightState,
    shouldPauseStewardApplicationPreview,
    typeStewardDelegatedMessage
  }
}
