<template>
  <section
    class="workbench-ai-mode"
    :class="{ 'has-conversation': conversationStarted }"
    aria-label="小财管家 AI 模式"
  >
    <input
      ref="fileInputRef"
      class="workbench-ai-file-input"
      type="file"
      multiple
      accept=".pdf,.jpg,.jpeg,.png,.webp,.doc,.docx,.xls,.xlsx"
      @change="handleAiModeFilesChange"
    />

    <Transition name="workbench-ai-panel-swap" mode="out-in" appear>
      <div v-if="!conversationStarted" key="welcome" class="workbench-ai-shell workbench-ai-home">
        <div class="workbench-ai-orb" aria-hidden="true">
          <img
            :src="orbIcon"
            class="workbench-ai-orb__image"
            alt=""
          />
        </div>

        <div class="workbench-ai-copy">
          <h2>嗨，{{ displayUserName }}，我是您的小财管家</h2>
          <p>您可以直接向我提问，或选择下方推荐主题，快速完成费用相关事务</p>
        </div>

        <form class="workbench-ai-composer" @submit.prevent="submitAiModePrompt">
          <div class="workbench-ai-composer-field">
            <div v-if="workbenchDateTagLabel" class="workbench-ai-date-chip">
              <i class="mdi mdi-calendar-check-outline"></i>
              <span>{{ workbenchDateTagLabel }}</span>
              <button type="button" aria-label="移除日期" @click="removeWorkbenchDateTag">
                <i class="mdi mdi-close"></i>
              </button>
            </div>

            <textarea
              ref="assistantInputRef"
              v-model="assistantDraft"
              maxlength="1000"
              rows="3"
              placeholder="今天我能帮您做点什么？"
              @keydown.enter.exact.prevent="submitAiModePrompt"
            ></textarea>
          </div>

          <div class="workbench-ai-composer-toolbar">
            <div class="workbench-ai-tool-buttons">
              <div class="workbench-date-anchor workbench-ai-date-anchor">
                <button
                  type="button"
                  class="workbench-ai-icon-btn"
                  :class="{ active: workbenchDatePickerOpen || workbenchDateTagLabel }"
                  title="选择日期"
                  aria-label="选择日期"
                  :aria-expanded="workbenchDatePickerOpen"
                  @click.stop="toggleWorkbenchDatePicker"
                >
                  <i class="mdi mdi-calendar-range"></i>
                </button>

                <div
                  v-if="workbenchDatePickerOpen"
                  class="workbench-ai-date-popover"
                  role="dialog"
                  aria-label="选择业务日期"
                  @click.stop
                >
                  <div class="workbench-ai-date-tabs" role="tablist" aria-label="日期模式">
                    <button
                      type="button"
                      :class="{ active: workbenchDateMode === 'single' }"
                      @click="setWorkbenchDateMode('single')"
                    >
                      单日
                    </button>
                    <button
                      type="button"
                      :class="{ active: workbenchDateMode === 'range' }"
                      @click="setWorkbenchDateMode('range')"
                    >
                      范围
                    </button>
                  </div>

                  <label v-if="workbenchDateMode === 'single'" class="workbench-ai-date-field">
                    <span>业务日期</span>
                    <input
                      v-model="workbenchSingleDate"
                      type="date"
                      @change="handleWorkbenchDateInputChange('single')"
                    />
                  </label>

                  <div v-else class="workbench-ai-date-range">
                    <label class="workbench-ai-date-field">
                      <span>开始日期</span>
                      <input
                        v-model="workbenchRangeStartDate"
                        type="date"
                        @change="handleWorkbenchDateInputChange('range-start')"
                      />
                    </label>
                    <label class="workbench-ai-date-field">
                      <span>结束日期</span>
                      <input
                        v-model="workbenchRangeEndDate"
                        type="date"
                        @change="handleWorkbenchDateInputChange('range-end')"
                      />
                    </label>
                  </div>

                  <div class="workbench-ai-date-actions">
                    <button type="button" class="ghost" @click="clearWorkbenchDateSelection">清除</button>
                    <button
                      type="button"
                      class="primary"
                      :disabled="!workbenchCanApplyDateSelection"
                      @click="applyWorkbenchDateSelection"
                    >
                      完成
                    </button>
                  </div>
                </div>
              </div>
              <button
                type="button"
                class="workbench-ai-icon-btn"
                title="上传附件"
                aria-label="上传附件"
                @click="triggerAiModeFileUpload"
              >
                <i class="mdi mdi-paperclip"></i>
              </button>
              <button
                type="button"
                class="workbench-ai-icon-btn"
                title="语音输入"
                aria-label="语音输入"
                @click="handleVoiceInput"
              >
                <i class="mdi mdi-microphone-outline"></i>
              </button>
            </div>

            <div class="workbench-ai-composer-right">
              <div class="workbench-ai-model-selector" :title="modelSelectorTitle">
                <span>{{ displayModelName }}</span>
                <i class="mdi mdi-chevron-down"></i>
              </div>
              <button
                type="submit"
                class="workbench-ai-send-btn"
                :disabled="!canSubmitAiModePrompt || sending"
                aria-label="发送给小财管家"
              >
                <i class="mdi mdi-arrow-up"></i>
              </button>
            </div>
          </div>
        </form>

        <div v-if="selectedFiles.length" class="workbench-ai-file-strip">
          <span>已选择 {{ selectedFiles.length }} 份附件</span>
          <button type="button" @click="clearAiModeFiles">清空</button>
        </div>

        <div class="workbench-ai-quick-start-section">
          <h3 class="workbench-ai-quick-start-title">快速开始</h3>
          <div class="workbench-ai-action-row" aria-label="推荐主题">
            <button
              v-for="item in aiModeActionItems"
              :key="item.label"
              type="button"
              class="workbench-ai-action"
              @click="runAiModeAction(item)"
            >
              <div class="action-icon-wrapper">
                <i :class="item.icon"></i>
              </div>
              <div class="action-text">
                <strong>{{ item.label }}</strong>
                <p>{{ item.prompt }}</p>
              </div>
            </button>
          </div>
        </div>
      </div>

      <div v-else key="conversation" class="workbench-ai-conversation">
        <div class="workbench-ai-conversation-actions" aria-label="对话操作">
          <button type="button" title="回到对话顶部" aria-label="回到对话顶部" @click="scrollInlineConversationToTop">
            <i class="mdi mdi-arrow-up"></i>
          </button>
          <button
            type="button"
            class="danger"
            title="删除当前对话"
            aria-label="删除当前对话"
            :disabled="!conversationMessages.length"
            @click="requestDeleteCurrentConversation"
          >
            <i class="mdi mdi-trash-can-outline"></i>
          </button>
        </div>

        <div
          ref="conversationScrollRef"
          class="workbench-ai-thread"
          aria-live="polite"
          @scroll.passive="handleInlineConversationScroll"
        >
          <div v-if="conversationMessages.length === 0" class="workbench-ai-empty-thread">
            <strong>{{ activeConversationTitle || '新对话' }}</strong>
            <p>直接输入问题，小财管家会在当前页面内持续回复。</p>
          </div>

          <article
            v-for="message in conversationMessages"
            :key="message.id"
            class="workbench-ai-message"
            :class="`is-${message.role}`"
          >
            <div v-if="message.role === 'user'" class="workbench-ai-user-bubble">
              {{ message.content }}
            </div>
            <div v-if="message.role === 'user'" class="workbench-ai-message-actions">
              <button type="button" title="引用" aria-label="引用" @click="quoteInlineMessage(message)">
                <i class="mdi mdi-format-quote-open"></i>
              </button>
              <button type="button" title="复制" aria-label="复制" @click="copyInlineMessage(message)">
                <i class="mdi mdi-content-copy"></i>
              </button>
              <time class="workbench-ai-message-time">{{ formatMessageTime(message.createdAt) }}</time>
            </div>

            <template v-else>
              <div
                class="workbench-ai-answer-card"
                :class="{ pending: message.pending, 'has-thinking': hasInlineThinking(message) }"
              >
                <div
                  v-if="hasInlineThinking(message)"
                  class="workbench-ai-thinking-panel"
                  :class="{
                    'is-expanded': isInlineThinkingExpanded(message),
                    'is-collapsed': !isInlineThinkingExpanded(message),
                    'is-running': message.pending
                  }"
                >
                  <button
                    v-if="!isInlineThinkingExpanded(message)"
                    type="button"
                    class="workbench-ai-thinking-toggle"
                    aria-expanded="false"
                    @click="toggleInlineThinking(message)"
                  >
                    <span class="workbench-ai-thinking-toggle-left">
                      <span class="workbench-ai-thinking-dot" aria-hidden="true"></span>
                      <strong>小财业务思考</strong>
                      <small>{{ resolveInlineThinkingEvents(message).length }} 条</small>
                    </span>
                    <i class="mdi mdi-chevron-down" aria-hidden="true"></i>
                  </button>

                  <div v-else class="workbench-ai-thinking-expanded">
                    <button
                      type="button"
                      class="workbench-ai-thinking-collapse-btn"
                      aria-label="折叠小财业务思考"
                      @click="toggleInlineThinking(message)"
                    >
                      <i class="mdi mdi-chevron-up" aria-hidden="true"></i>
                    </button>

                    <Transition name="workbench-ai-thinking-collapse" appear>
                      <div
                        class="workbench-ai-thinking-list"
                        aria-label="小财业务思考明细"
                      >
                        <div
                          v-for="event in resolveInlineThinkingEvents(message)"
                          :key="event.eventId || `${message.id}-${event.title}`"
                          class="workbench-ai-thinking-item"
                          :class="`is-${event.status || 'completed'}`"
                        >
                          <span class="workbench-ai-thinking-dot" aria-hidden="true"></span>
                          <div>
                            <strong>{{ event.title || '正在分析' }}</strong>
                            <p v-if="event.content">{{ event.content }}</p>
                          </div>
                        </div>
                      </div>
                    </Transition>
                  </div>
                </div>

                <div
                  v-if="message.content"
                  class="workbench-ai-answer-markdown"
                  v-html="renderInlineMarkdown(message.content)"
                ></div>

                <div v-else-if="message.pending && !hasInlineThinking(message)" class="workbench-ai-pending-line">
                  小财管家正在识别任务、拆解流程并准备下一步建议...
                </div>

                <div v-if="message.suggestedActions?.length" class="workbench-ai-suggested-actions">
                  <button
                    v-for="action in message.suggestedActions"
                    :key="`${message.id}-${action.label}`"
                    type="button"
                    @click="handleInlineSuggestedAction(action)"
                  >
                    <i :class="action.icon || 'mdi mdi-arrow-right-circle-outline'"></i>
                    <span>{{ action.label }}</span>
                  </button>
                </div>
              </div>
              <div v-if="!message.pending" class="workbench-ai-message-actions" aria-label="消息操作">
                <button type="button" title="复制" aria-label="复制" @click="copyInlineMessage(message)">
                  <i class="mdi mdi-content-copy"></i>
                </button>
                <button type="button" title="有帮助" aria-label="有帮助" @click="markInlineMessageFeedback(message, 'up')">
                  <i class="mdi mdi-thumb-up-outline"></i>
                </button>
                <button type="button" title="无帮助" aria-label="无帮助" @click="markInlineMessageFeedback(message, 'down')">
                  <i class="mdi mdi-thumb-down-outline"></i>
                </button>
                <button type="button" title="重新生成" aria-label="重新生成" @click="regenerateLastReply">
                  <i class="mdi mdi-refresh"></i>
                </button>
                <time class="workbench-ai-message-time">{{ formatMessageTime(message.createdAt) }}</time>
              </div>
            </template>
          </article>
        </div>

        <div class="workbench-ai-conversation-bottom">
          <div v-if="selectedFiles.length" class="workbench-ai-file-strip inline">
            <span>已选择 {{ selectedFiles.length }} 份附件</span>
            <button type="button" @click="clearAiModeFiles">清空</button>
          </div>

          <form class="workbench-ai-composer workbench-ai-composer--inline" @submit.prevent="submitAiModePrompt">
            <div class="workbench-ai-composer-field">
              <div v-if="workbenchDateTagLabel" class="workbench-ai-date-chip">
                <i class="mdi mdi-calendar-check-outline"></i>
                <span>{{ workbenchDateTagLabel }}</span>
                <button type="button" aria-label="移除日期" @click="removeWorkbenchDateTag">
                  <i class="mdi mdi-close"></i>
                </button>
              </div>

              <textarea
                ref="assistantInputRef"
                v-model="assistantDraft"
                maxlength="1000"
                rows="3"
                placeholder="继续和小财管家对话..."
                @keydown.enter.exact.prevent="submitAiModePrompt"
              ></textarea>
            </div>

            <div class="workbench-ai-composer-toolbar">
              <div class="workbench-ai-tool-buttons">
                <div class="workbench-date-anchor workbench-ai-date-anchor">
                  <button
                    type="button"
                    class="workbench-ai-icon-btn"
                    :class="{ active: workbenchDatePickerOpen || workbenchDateTagLabel }"
                    title="选择日期"
                    aria-label="选择日期"
                    :aria-expanded="workbenchDatePickerOpen"
                    @click.stop="toggleWorkbenchDatePicker"
                  >
                    <i class="mdi mdi-calendar-range"></i>
                  </button>

                  <div
                    v-if="workbenchDatePickerOpen"
                    class="workbench-ai-date-popover"
                    role="dialog"
                    aria-label="选择业务日期"
                    @click.stop
                  >
                    <div class="workbench-ai-date-tabs" role="tablist" aria-label="日期模式">
                      <button
                        type="button"
                        :class="{ active: workbenchDateMode === 'single' }"
                        @click="setWorkbenchDateMode('single')"
                      >
                        单日
                      </button>
                      <button
                        type="button"
                        :class="{ active: workbenchDateMode === 'range' }"
                        @click="setWorkbenchDateMode('range')"
                      >
                        范围
                      </button>
                    </div>

                    <label v-if="workbenchDateMode === 'single'" class="workbench-ai-date-field">
                      <span>业务日期</span>
                      <input
                        v-model="workbenchSingleDate"
                        type="date"
                        @change="handleWorkbenchDateInputChange('single')"
                      />
                    </label>

                    <div v-else class="workbench-ai-date-range">
                      <label class="workbench-ai-date-field">
                        <span>开始日期</span>
                        <input
                          v-model="workbenchRangeStartDate"
                          type="date"
                          @change="handleWorkbenchDateInputChange('range-start')"
                        />
                      </label>
                      <label class="workbench-ai-date-field">
                        <span>结束日期</span>
                        <input
                          v-model="workbenchRangeEndDate"
                          type="date"
                          @change="handleWorkbenchDateInputChange('range-end')"
                        />
                      </label>
                    </div>

                    <div class="workbench-ai-date-actions">
                      <button type="button" class="ghost" @click="clearWorkbenchDateSelection">清除</button>
                      <button
                        type="button"
                        class="primary"
                        :disabled="!workbenchCanApplyDateSelection"
                        @click="applyWorkbenchDateSelection"
                      >
                        完成
                      </button>
                    </div>
                  </div>
                </div>
                <button
                  type="button"
                  class="workbench-ai-icon-btn"
                  title="上传附件"
                  aria-label="上传附件"
                  @click="triggerAiModeFileUpload"
                >
                  <i class="mdi mdi-paperclip"></i>
                </button>
                <button
                  type="button"
                  class="workbench-ai-icon-btn"
                  title="语音输入"
                  aria-label="语音输入"
                  @click="handleVoiceInput"
                >
                  <i class="mdi mdi-microphone-outline"></i>
                </button>
              </div>

              <div class="workbench-ai-composer-right">
                <div class="workbench-ai-model-selector" :title="modelSelectorTitle">
                  <span>{{ displayModelName }}</span>
                  <i class="mdi mdi-chevron-down"></i>
                </div>
                <button
                  type="submit"
                  class="workbench-ai-send-btn"
                  :disabled="!canSubmitAiModePrompt || sending"
                  aria-label="发送给小财管家"
                >
                  <i class="mdi mdi-arrow-up"></i>
                </button>
              </div>
            </div>
          </form>

          <p class="workbench-ai-disclaimer">小财管家可能会出错，重要费用事项请核对单据、制度与审批结果。</p>
        </div>
      </div>
    </Transition>

    <Transition name="workbench-ai-confirm-fade">
      <div v-if="deleteDialogOpen" class="workbench-ai-confirm-mask" role="presentation" @click.self="cancelDeleteConversation">
        <div
          class="workbench-ai-confirm-dialog"
          role="dialog"
          aria-modal="true"
          aria-labelledby="workbench-ai-delete-title"
        >
          <h3 id="workbench-ai-delete-title">删除当前对话？</h3>
          <p>删除后，左侧最近对话中的这条记录也会被移除。这个操作无法恢复。</p>
          <div class="workbench-ai-confirm-actions">
            <button type="button" class="ghost" @click="cancelDeleteConversation">取消</button>
            <button type="button" class="danger" @click="confirmDeleteConversation">删除对话</button>
          </div>
        </div>
      </div>
    </Transition>
  </section>
</template>

<script setup>
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'

import orbIcon from '../../assets/workbench-ai-mode-orb-icon.gif'
import { useSystemState } from '../../composables/useSystemState.js'
import { useToast } from '../../composables/useToast.js'
import { fetchSettings } from '../../services/settings.js'
import { fetchStewardPlan, fetchStewardPlanStream } from '../../services/steward.js'
import { useWorkbenchComposerDate } from '../../composables/useWorkbenchComposerDate.js'
import {
  deleteAiWorkbenchConversation,
  loadAiWorkbenchConversationHistory,
  saveAiWorkbenchConversation
} from '../../utils/aiWorkbenchConversationStore.js'
import { renderMarkdown } from '../../utils/markdown.js'
import {
  mergeComposerPrefill,
  resolveSuggestedActionPrefill
} from '../../utils/assistantSuggestedActionPrefill.js'
import {
  buildExpenseSceneSelectionActions
} from '../../utils/expenseAssistantActions.js'
import {
  buildStewardPlanMessageText,
  buildStewardPlanRequest,
  buildStewardSuggestedActions,
  normalizeStewardPlan
} from '../../views/scripts/stewardPlanModel.js'
import {
  buildExpenseSceneSelectionMessage,
  SESSION_TYPE_EXPENSE
} from '../../views/scripts/travelReimbursementConversationModel.js'
import {
  applyAiExpenseAnswer,
  buildAiExpenseStepPrompt,
  buildAiExpenseSummary,
  createAiExpenseDraft,
  isAiExpenseDraftComplete
} from '../../utils/aiExpenseDraftModel.js'
import {
  applyAiApplicationAnswer,
  buildAiApplicationStepPrompt,
  buildAiApplicationSummary,
  createAiApplicationDraft,
  isAiApplicationDraftComplete
} from '../../utils/aiApplicationDraftModel.js'
import {
  buildRequiredApplicationActions,
  buildRequiredApplicationMissingText,
  buildRequiredApplicationSelectionText,
  filterRequiredApplicationCandidates
} from '../../views/scripts/travelReimbursementApplicationLinkModel.js'
import { fetchExpenseClaims } from '../../services/reimbursements.js'

const props = defineProps({
  sidebarCommand: { type: Object, default: () => ({ seq: 0, type: '', payload: null }) }
})

const emit = defineEmits(['conversation-change', 'conversation-history-change'])
const { currentUser } = useSystemState()
const { toast } = useToast()
const assistantDraft = ref('')
const assistantInputRef = ref(null)
const fileInputRef = ref(null)
const conversationScrollRef = ref(null)
const inlineConversationAutoScrollPinned = ref(true)
const selectedFiles = ref([])
const systemSettings = ref(null)
const conversationStarted = ref(false)
const conversationMessages = ref([])
const conversationId = ref('')
const activeConversationTitle = ref('')
const sending = ref(false)
const stewardState = ref(null)
const aiExpenseDraft = ref(null)
const aiApplicationDraft = ref(null)
const thinkingExpandedMessageIds = ref(new Set())
const thinkingCollapsedMessageIds = ref(new Set())
const deleteDialogOpen = ref(false)
let messageSeq = 0
const AI_SEARCH_CONVERSATION_ID = 'ai-search'
const INLINE_ANSWER_STREAM_CHUNK_SIZE = 6
const INLINE_ANSWER_STREAM_DELAY_MS = 24
const INLINE_AUTO_SCROLL_THRESHOLD = 96
const INLINE_LAYOUT_SETTLE_SCROLL_DELAY_MS = 260

const {
  workbenchDatePickerOpen,
  workbenchDateMode,
  workbenchSingleDate,
  workbenchRangeStartDate,
  workbenchRangeEndDate,
  workbenchDateTagLabel,
  workbenchCanApplyDateSelection,
  clearWorkbenchDateSelection,
  toggleWorkbenchDatePicker,
  closeWorkbenchDatePicker,
  setWorkbenchDateMode,
  handleWorkbenchDatePickerOutside,
  applyWorkbenchDateSelection,
  handleWorkbenchDateInputChange,
  removeWorkbenchDateTag,
  buildWorkbenchPromptText
} = useWorkbenchComposerDate({ draft: assistantDraft, focusInput: focusAiModeInput })

const aiModeActionItems = [
  {
    label: '发起报销',
    icon: 'mdi mdi-file-document-plus-outline',
    prompt: '帮我发起一笔报销，并检查需要准备哪些票据材料。',
    source: 'workbench',
    sessionType: 'expense'
  },
  {
    label: '查询预算',
    icon: 'mdi mdi-chart-pie-outline',
    prompt: '帮我查询当前预算余额和近期费用占用情况。',
    source: 'budget',
    sessionType: 'budget'
  },
  {
    label: '解释制度',
    icon: 'mdi mdi-book-open-page-variant-outline',
    prompt: '帮我解释公司报销制度，并列出这次需要注意的条款。',
    source: 'workbench',
    sessionType: 'knowledge'
  },
  {
    label: '催办审批',
    icon: 'mdi mdi-bell-ring-outline',
    prompt: '帮我查询待审批单据，并生成一段礼貌的催办说明。',
    source: 'workbench',
    sessionType: 'approval'
  }
]

const displayUserName = computed(() => {
  const user = currentUser.value || {}
  return String(user.name || user.username || '同事').trim() || '同事'
})

const displayModelName = computed(() => {
  const llmForm = systemSettings.value?.llmForm
  if (!llmForm) return 'Axiom Ultra 3.1'
  const model = llmForm.mainModel || ''
  const provider = llmForm.mainProvider || ''
  if (!model) return 'Axiom Ultra 3.1'
  return provider ? `${provider} / ${model}` : model
})

const modelSelectorTitle = computed(() => {
  const llmForm = systemSettings.value?.llmForm
  if (!llmForm) return '当前模型：Axiom Ultra 3.1'
  const model = llmForm.mainModel || 'Axiom Ultra 3.1'
  const provider = llmForm.mainProvider || ''
  return provider ? `当前模型：${provider} / ${model}` : `当前模型：${model}`
})

const canSubmitAiModePrompt = computed(() => (
  Boolean(assistantDraft.value.trim())
    || selectedFiles.value.length > 0
    || Boolean(workbenchDateTagLabel.value)
))

async function loadSystemSettings() {
  try {
    systemSettings.value = await fetchSettings()
  } catch {
    systemSettings.value = { llmForm: {} }
  }
}

function focusAiModeInput() {
  nextTick(() => {
    assistantInputRef.value?.focus()
  })
}

function isInlineConversationNearBottom() {
  const el = conversationScrollRef.value
  if (!el) {
    return true
  }
  return el.scrollHeight - el.clientHeight - el.scrollTop <= INLINE_AUTO_SCROLL_THRESHOLD
}

function handleInlineConversationScroll() {
  inlineConversationAutoScrollPinned.value = isInlineConversationNearBottom()
}

function forceInlineConversationToBottom() {
  const el = conversationScrollRef.value
  if (el) {
    el.scrollTop = el.scrollHeight
    inlineConversationAutoScrollPinned.value = true
  }
}

function scrollInlineConversationToBottom(options = {}) {
  const shouldScroll = options.force !== false
  nextTick(() => {
    if (!shouldScroll) {
      return
    }
    forceInlineConversationToBottom()
    window.requestAnimationFrame(() => {
      forceInlineConversationToBottom()
    })
    window.setTimeout(() => {
      if (inlineConversationAutoScrollPinned.value) {
        forceInlineConversationToBottom()
      }
    }, INLINE_LAYOUT_SETTLE_SCROLL_DELAY_MS)
  })
}

function scrollInlineConversationToTop() {
  nextTick(() => {
    const el = conversationScrollRef.value
    if (el) {
      inlineConversationAutoScrollPinned.value = false
      el.scrollTo({ top: 0, behavior: 'smooth' })
    }
  })
}

function normalizeParagraphs(content) {
  return String(content || '')
    .split(/\n{2,}|\n/)
    .map((item) => item.trim())
    .filter(Boolean)
}

function createInlineMessage(role, content, options = {}) {
  const normalizedContent = String(content || '').trim()
  return {
    id: options.id || `${Date.now()}-${messageSeq += 1}`,
    role,
    content: normalizedContent,
    paragraphs: normalizeParagraphs(normalizedContent),
    pending: Boolean(options.pending),
    feedback: String(options.feedback || ''),
    stewardPlan: options.stewardPlan || null,
    suggestedActions: Array.isArray(options.suggestedActions) ? options.suggestedActions : [],
    createdAt: options.createdAt || Date.now()
  }
}

function formatMessageTime(timestamp) {
  if (!timestamp) return ''
  return new Date(timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
}

function updateInlineMessageContent(message, content) {
  if (!message) {
    return
  }
  message.content = String(content || '')
  message.paragraphs = normalizeParagraphs(message.content)
}

function appendInlineMessageContent(message, delta) {
  const nextDelta = String(delta || '')
  if (!nextDelta) {
    return
  }
  updateInlineMessageContent(message, `${message.content || ''}${nextDelta}`)
}

function waitInlineAnswerStreamFrame() {
  return new Promise((resolve) => {
    window.setTimeout(resolve, INLINE_ANSWER_STREAM_DELAY_MS)
  })
}

async function streamInlineAssistantContent(messageId, content) {
  const targetContent = String(content || '').trim()
  let streamedContent = ''

  for (let index = 0; index < targetContent.length; index += INLINE_ANSWER_STREAM_CHUNK_SIZE) {
    const message = conversationMessages.value.find((item) => item.id === messageId)
    if (!message || !message.pending) {
      return
    }
    const shouldAutoScroll = inlineConversationAutoScrollPinned.value
    streamedContent += targetContent.slice(index, index + INLINE_ANSWER_STREAM_CHUNK_SIZE)
    updateInlineMessageContent(message, streamedContent)
    scrollInlineConversationToBottom({ force: shouldAutoScroll })
    await waitInlineAnswerStreamFrame()
  }
}

function normalizeRuntimeMessage(message = {}) {
  return createInlineMessage(message.role || 'assistant', message.content || '', {
    id: message.id,
    pending: false,
    feedback: message.feedback || '',
    stewardPlan: message.stewardPlan || null,
    suggestedActions: Array.isArray(message.suggestedActions) ? message.suggestedActions : []
  })
}

function serializeRuntimeMessage(message = {}) {
  return {
    id: message.id,
    role: message.role,
    content: message.content,
    feedback: message.feedback || '',
    stewardPlan: message.stewardPlan || null,
    suggestedActions: Array.isArray(message.suggestedActions) ? message.suggestedActions : []
  }
}

function refreshConversationHistory() {
  const history = loadAiWorkbenchConversationHistory(currentUser.value || {})
  emit('conversation-history-change', history)
  return history
}

function isPersistableInlineConversation() {
  return Boolean(
    conversationId.value &&
      conversationId.value !== AI_SEARCH_CONVERSATION_ID &&
      conversationMessages.value.length
  )
}

function persistCurrentConversation() {
  if (!isPersistableInlineConversation()) {
    refreshConversationHistory()
    return []
  }

  const history = saveAiWorkbenchConversation(currentUser.value || {}, {
    id: conversationId.value,
    conversationId: conversationId.value,
    title: activeConversationTitle.value,
    source: 'workbench',
    sessionType: 'steward',
    stewardState: stewardState.value,
    messages: conversationMessages.value.map((message) => serializeRuntimeMessage(message))
  })
  emit('conversation-history-change', history)
  return history
}

function resetInlineConversationState() {
  conversationStarted.value = false
  conversationMessages.value = []
  conversationId.value = ''
  stewardState.value = null
  activeConversationTitle.value = ''
  assistantDraft.value = ''
  thinkingExpandedMessageIds.value = new Set()
  thinkingCollapsedMessageIds.value = new Set()
  deleteDialogOpen.value = false
  clearWorkbenchDateSelection()
  clearAiModeFiles()
}

function replaceInlineMessage(id, nextMessage) {
  const index = conversationMessages.value.findIndex((item) => item.id === id)
  if (index === -1) {
    conversationMessages.value.push(nextMessage)
    return
  }
  conversationMessages.value.splice(index, 1, nextMessage)
}

function activateInlineConversation(options = {}) {
  conversationStarted.value = true
  if (!conversationId.value) {
    conversationId.value = options.id || `inline-${Date.now()}`
  }
  activeConversationTitle.value = options.title || activeConversationTitle.value || '新对话'
  emit('conversation-change', { id: conversationId.value, title: activeConversationTitle.value })
}

function renderInlineMarkdown(content) {
  return renderMarkdown(content)
}

function resolveInlineThinkingEvents(message) {
  return Array.isArray(message?.stewardPlan?.thinkingEvents) ? message.stewardPlan.thinkingEvents : []
}

function hasInlineThinking(message) {
  return resolveInlineThinkingEvents(message).length > 0
}

function isInlineThinkingExpanded(message) {
  if (!message?.id) {
    return Boolean(message?.pending)
  }
  if (thinkingCollapsedMessageIds.value.has(message.id)) {
    return false
  }
  return Boolean(message.pending || thinkingExpandedMessageIds.value.has(message.id))
}

function toggleInlineThinking(message) {
  if (!message?.id) {
    return
  }
  const nextExpandedIds = new Set(thinkingExpandedMessageIds.value)
  const nextCollapsedIds = new Set(thinkingCollapsedMessageIds.value)
  if (isInlineThinkingExpanded(message)) {
    nextExpandedIds.delete(message.id)
    nextCollapsedIds.add(message.id)
  } else {
    nextCollapsedIds.delete(message.id)
    nextExpandedIds.add(message.id)
  }
  thinkingExpandedMessageIds.value = nextExpandedIds
  thinkingCollapsedMessageIds.value = nextCollapsedIds
}

function buildInlinePromptText(rawPrompt, files = []) {
  const prompt = buildWorkbenchPromptText(rawPrompt)
  if (prompt) {
    return prompt
  }
  return files.length ? '请帮我处理已上传的附件。' : ''
}

function shouldCheckAiRequiredApplicationGate(prompt) {
  const compact = String(prompt || '').replace(/\s+/g, '')
  if (!compact || !/(出差|差旅|部署|实施|支撑|支持|协助|拜访|调研|驻场|上线|验收)/.test(compact)) {
    return false
  }
  if (!/\d{1,2}月\d{1,2}|昨天|前天|上周|上月/.test(compact)) {
    return false
  }
  return !/(申请|报销|草稿|提交|审批|保存|发起|创建)/.test(compact)
}

function serializeRequiredApplicationCandidate(candidate = {}) {
  return {
    id: String(candidate.id || '').trim(),
    claim_no: String(candidate.claim_no || '').trim(),
    reason: String(candidate.reason || '').trim(),
    location: String(candidate.location || '').trim(),
    business_time: String(candidate.business_time || '').trim(),
    status_label: String(candidate.status_label || '').trim()
  }
}

async function attachAiRequiredApplicationGate(planRequest, prompt) {
  if (!shouldCheckAiRequiredApplicationGate(prompt)) {
    return planRequest
  }

  try {
    const claims = await fetchExpenseClaims()
    const candidates = filterRequiredApplicationCandidates(claims, 'travel', currentUser.value || {})
    planRequest.context_json = {
      ...(planRequest.context_json || {}),
      required_application_gate: {
        ...((planRequest.context_json || {}).required_application_gate || {}),
        travel: {
          checked: true,
          candidate_count: candidates.length,
          candidates: candidates.slice(0, 5).map((candidate) => serializeRequiredApplicationCandidate(candidate))
        }
      }
    }
  } catch (error) {
    console.warn('AI mode required application lookup failed:', error)
    planRequest.context_json = {
      ...(planRequest.context_json || {}),
      required_application_gate: {
        ...((planRequest.context_json || {}).required_application_gate || {}),
        travel: {
          checked: false,
          query_failed: true
        }
      }
    }
  }
  return planRequest
}

function resolveRequiredApplicationGateContinuationFlow(normalizedPlan) {
  if (String(normalizedPlan?.pendingFlowConfirmation?.status || '').trim() !== 'pending') {
    return null
  }
  const flows = Array.isArray(normalizedPlan?.candidateFlows) ? normalizedPlan.candidateFlows : []
  const applicationFlow = flows.find((flow) => flow.flowId === 'travel_application')
  if (flows.length === 1 && applicationFlow && /先发起出差申请/.test(applicationFlow.label)) {
    return applicationFlow
  }
  return flows.find((flow) => (
    flow.flowId === 'travel_reimbursement' &&
    /关联已有申请单/.test(flow.label)
  )) || null
}

function buildAiRequiredApplicationGateAutoMessage(normalizedPlan, flow) {
  const baseText = buildStewardPlanMessageText({
    planStatus: normalizedPlan?.planStatus,
    nextAction: normalizedPlan?.nextAction,
    summary: normalizedPlan?.summary,
    pendingFlowConfirmation: normalizedPlan?.pendingFlowConfirmation,
    candidateFlows: normalizedPlan?.candidateFlows
  })
  const contextText = String(baseText || '')
    .split(/\n\n1\. \*\*/)[0]
    .trim()
    .replace('### 需要先确认流程方向', '### 我已先查询申请单')
  if (flow?.flowId === 'travel_application') {
    return [
      contextText || baseText,
      '我会继续在当前对话里为你发起出差申请。'
    ].filter(Boolean).join('\n\n')
  }
  if (flow?.flowId === 'travel_reimbursement') {
    return [
      contextText || baseText,
      '我会继续进入申请单关联步骤，请你确认要关联哪张单据。'
    ].filter(Boolean).join('\n\n')
  }
  return baseText
}

function continueAiRequiredApplicationGateFromPlan(normalizedPlan) {
  const flow = resolveRequiredApplicationGateContinuationFlow(normalizedPlan)
  if (!flow) {
    return false
  }
  if (flow.flowId === 'travel_application') {
    aiExpenseDraft.value = null
    startAiApplicationDraft('travel', '差旅费')
    return true
  }
  if (flow.flowId === 'travel_reimbursement') {
    aiApplicationDraft.value = null
    startAiExpenseDraft('travel', '差旅费', true)
    return true
  }
  return false
}

function normalizeStreamThinkingEvent(event = {}) {
  const data = event?.data && typeof event.data === 'object' ? event.data : {}
  const eventId = String(data.event_id || data.eventId || data.stage || `thinking-${Date.now()}`).trim()
  return {
    eventId,
    stage: String(data.stage || '').trim(),
    title: String(data.title || '小财管家正在分析').trim(),
    content: String(data.content || '').trim(),
    status: String(data.status || 'running').trim() || 'running'
  }
}

function handleInlineStewardStreamEvent(messageId, event) {
  const message = conversationMessages.value.find((item) => item.id === messageId)
  if (!message) {
    return
  }

  if (event?.event === 'answer_delta') {
    const data = event?.data && typeof event.data === 'object' ? event.data : {}
    const shouldAutoScroll = inlineConversationAutoScrollPinned.value
    appendInlineMessageContent(message, data.delta || data.content || data.text || '')
    message.stewardPlan = {
      ...(message.stewardPlan || {}),
      streamStatus: 'streaming'
    }
    scrollInlineConversationToBottom({ force: shouldAutoScroll })
    return
  }

  if (event?.event !== 'thinking') {
    return
  }

  const nextEvent = normalizeStreamThinkingEvent(event)
  const shouldAutoScroll = inlineConversationAutoScrollPinned.value
  const currentPlan = message.stewardPlan || {}
  const currentEvents = Array.isArray(currentPlan.thinkingEvents) ? currentPlan.thinkingEvents : []
  const eventIndex = currentEvents.findIndex((item) => item.eventId && item.eventId === nextEvent.eventId)
  const nextEvents = eventIndex >= 0
    ? currentEvents.map((item, index) => (index === eventIndex ? { ...item, ...nextEvent } : item))
    : [...currentEvents, nextEvent]

  message.stewardPlan = {
    ...currentPlan,
    thinkingEvents: nextEvents,
    streamStatus: 'streaming'
  }
  scrollInlineConversationToBottom({ force: shouldAutoScroll })
}

async function fetchInlineStewardPlan(messageId, payload) {
  try {
    return await fetchStewardPlanStream(
      payload,
      {
        onEvent: (event) => handleInlineStewardStreamEvent(messageId, event)
      },
      {
        idleTimeoutMs: 90000,
        timeoutMessage: '小财管家仍在规划任务，已停止等待。你可以稍后继续追问。'
      }
    )
  } catch (error) {
    if (String(error?.message || '').includes('流式服务')) {
      return fetchStewardPlan(payload, {
        timeoutMs: 75000,
        timeoutMessage: '小财管家仍在规划任务，已停止等待。你可以稍后继续追问。'
      })
    }
    throw error
  }
}

async function requestInlineAssistantReply(prompt, entry = {}, files = []) {
  let shouldAutoScrollOnFinish = true
  const pendingMessage = createInlineMessage('assistant', '', {
    pending: true,
    stewardPlan: {
      streamStatus: 'streaming',
      thinkingEvents: [
        {
          eventId: 'init',
          title: '小财管家正在接入业务流程',
          content: '正在识别你的意图、上下文和附件信息。',
          status: 'running'
        }
      ]
    }
  })
  conversationMessages.value.push(pendingMessage)
  scrollInlineConversationToBottom()

  try {
    const planRequest = buildStewardPlanRequest({
      rawText: prompt,
      files,
      currentUser: currentUser.value || {},
      conversationId: conversationId.value,
      stewardState: stewardState.value
    })
    planRequest.context_json = {
      ...planRequest.context_json,
      entry_source: 'workbench_ai_inline',
      source: entry.source || 'workbench'
    }
    await attachAiRequiredApplicationGate(planRequest, prompt)

    const plan = await fetchInlineStewardPlan(pendingMessage.id, planRequest)
    const normalizedPlan = normalizeStewardPlan(plan, {
      visibleThinkingEventCount: Number.MAX_SAFE_INTEGER,
      initialSummaryOnly: true
    })
    const previousThinkingEvents = resolveInlineThinkingEvents(pendingMessage)
    const nextThinkingEvents = normalizedPlan.thinkingEvents.length
      ? normalizedPlan.thinkingEvents
      : previousThinkingEvents.map((item) => ({ ...item, status: 'completed' }))
    const previousConversationId = conversationId.value
    const nextConversationId = String(normalizedPlan.conversationId || '').trim()
    if (nextConversationId) {
      conversationId.value = nextConversationId
      emit('conversation-change', { id: conversationId.value, title: activeConversationTitle.value })
      if (previousConversationId && previousConversationId !== nextConversationId) {
        deleteAiWorkbenchConversation(currentUser.value || {}, previousConversationId)
      }
    }
    if (normalizedPlan.stewardState) {
      stewardState.value = normalizedPlan.stewardState
    }
    const requiredApplicationContinuationFlow = resolveRequiredApplicationGateContinuationFlow(normalizedPlan)
    const finalMessageText = requiredApplicationContinuationFlow
      ? buildAiRequiredApplicationGateAutoMessage(normalizedPlan, requiredApplicationContinuationFlow)
      : buildStewardPlanMessageText(plan)
    const hasServerStreamedContent = Boolean(String(pendingMessage.content || '').trim())
    if (!hasServerStreamedContent) {
      await streamInlineAssistantContent(pendingMessage.id, finalMessageText)
    }
    shouldAutoScrollOnFinish = inlineConversationAutoScrollPinned.value
    replaceInlineMessage(
      pendingMessage.id,
      createInlineMessage('assistant', finalMessageText, {
        id: pendingMessage.id,
        stewardPlan: {
          ...normalizedPlan,
          thinkingEvents: nextThinkingEvents,
          streamStatus: 'completed'
        },
        suggestedActions: requiredApplicationContinuationFlow ? [] : buildStewardSuggestedActions(plan)
      })
    )
    if (continueAiRequiredApplicationGateFromPlan(normalizedPlan)) {
      shouldAutoScrollOnFinish = inlineConversationAutoScrollPinned.value
    }
    persistCurrentConversation()
  } catch (error) {
    shouldAutoScrollOnFinish = inlineConversationAutoScrollPinned.value
    replaceInlineMessage(
      pendingMessage.id,
      createInlineMessage(
        'assistant',
        error?.message || '小财管家暂时无法完成规划，请稍后再试。',
        {
          id: pendingMessage.id,
          stewardPlan: {
            streamStatus: 'failed',
            thinkingEvents: resolveInlineThinkingEvents(pendingMessage).map((item) => ({
              ...item,
              status: 'failed'
            }))
          }
        }
      )
    )
    toast(error?.message || '小财管家暂时无法完成规划。')
    persistCurrentConversation()
  } finally {
    sending.value = false
    scrollInlineConversationToBottom({ force: shouldAutoScrollOnFinish })
  }
}

function startInlineConversation(prompt, entry = {}, files = []) {
  const cleanPrompt = buildInlinePromptText(prompt, files)
  if (!cleanPrompt || sending.value) {
    return
  }

  if (aiExpenseDraft.value && !isAiExpenseDraftComplete(aiExpenseDraft.value)) {
    advanceAiExpenseDraft(cleanPrompt, files)
    return
  }

  if (aiApplicationDraft.value && !isAiApplicationDraftComplete(aiApplicationDraft.value)) {
    advanceAiApplicationDraft(cleanPrompt, files)
    return
  }

  if (conversationId.value === AI_SEARCH_CONVERSATION_ID) {
    conversationId.value = ''
    conversationMessages.value = []
    activeConversationTitle.value = ''
  }

  sending.value = true
  activateInlineConversation({
    title: entry.label || cleanPrompt.slice(0, 18) || '新对话'
  })
  inlineConversationAutoScrollPinned.value = true
  conversationMessages.value.push(createInlineMessage('user', cleanPrompt))
  assistantDraft.value = ''
  removeWorkbenchDateTag()
  closeWorkbenchDatePicker()
  clearAiModeFiles()
  scrollInlineConversationToBottom()
  persistCurrentConversation()
  void requestInlineAssistantReply(cleanPrompt, entry, files)
}

function submitAiModePrompt() {
  if (!canSubmitAiModePrompt.value) {
    toast('请输入需求后再发送。')
    focusAiModeInput()
    return
  }

  startInlineConversation(assistantDraft.value, { source: 'workbench', sessionType: 'steward' }, Array.from(selectedFiles.value))
}

function runAiModeAction(item) {
  if (String(item?.label || '').trim() === '发起报销') {
    pushInlineExpenseSceneSelectionPrompt(item.prompt, item.label)
    return
  }
  startInlineConversation(item.prompt, item, Array.from(selectedFiles.value))
}

function startNewInlineConversation() {
  resetInlineConversationState()
  emit('conversation-change', { id: '', title: '' })
  refreshConversationHistory()
  focusAiModeInput()
}

function openInlineSearchConversation() {
  conversationMessages.value = [
    createInlineMessage('assistant', '你可以输入关键词搜索历史对话，也可以直接描述要继续处理的费用事项。')
  ]
  stewardState.value = null
  thinkingExpandedMessageIds.value = new Set()
  thinkingCollapsedMessageIds.value = new Set()
  conversationId.value = AI_SEARCH_CONVERSATION_ID
  activateInlineConversation({ id: AI_SEARCH_CONVERSATION_ID, title: '查询对话' })
  focusAiModeInput()
  scrollInlineConversationToBottom()
}

function openInlineRecentConversation(item = {}) {
  const title = String(item.title || '最近对话').trim()
  conversationId.value = String(item.id || `recent-${Date.now()}`).trim()
  activeConversationTitle.value = title
  stewardState.value = item.stewardState || null
  thinkingExpandedMessageIds.value = new Set()
  thinkingCollapsedMessageIds.value = new Set()
  inlineConversationAutoScrollPinned.value = true
  conversationMessages.value = Array.isArray(item.messages) && item.messages.length
    ? item.messages.map((message) => normalizeRuntimeMessage(message))
    : [
        createInlineMessage(
          'assistant',
          '这条历史对话没有保存完整消息。你可以继续输入新的问题，小财管家会接着处理。'
        )
      ]
  conversationStarted.value = true
  emit('conversation-change', { id: conversationId.value, title })
  focusAiModeInput()
  scrollInlineConversationToBottom()
}

function regenerateLastReply() {
  const lastUserMessage = [...conversationMessages.value].reverse().find((message) => message.role === 'user')
  if (!lastUserMessage || sending.value) {
    return
  }
  const lastAssistantIndex = conversationMessages.value.map((message) => message.role).lastIndexOf('assistant')
  if (lastAssistantIndex >= 0) {
    conversationMessages.value.splice(lastAssistantIndex, 1)
  }
  sending.value = true
  void requestInlineAssistantReply(lastUserMessage.content, { source: 'workbench', sessionType: 'steward' }, [])
}

function handleInlineSuggestedAction(action = {}) {
  const prefillText = resolveSuggestedActionPrefill(action)
  if (prefillText) {
    assistantDraft.value = mergeComposerPrefill(assistantDraft.value, prefillText)
    focusAiModeInput()
    return
  }

  const actionType = String(action?.action_type || '').trim()
  const actionPayload = action?.payload && typeof action.payload === 'object' ? action.payload : {}
  if (actionPayload.steward_confirm_flow && actionPayload.flow_id === 'travel_reimbursement') {
    const expenseType = String(actionPayload.expense_type || 'travel').trim() || 'travel'
    const expenseTypeLabel = String(actionPayload.expense_type_label || '差旅费').trim() || '差旅费'
    startAiExpenseDraft(expenseType, expenseTypeLabel, true)
    return
  }
  if (actionPayload.steward_confirm_flow && actionPayload.flow_id === 'travel_application') {
    aiExpenseDraft.value = null
    const expenseType = String(actionPayload.expense_type || 'travel').trim() || 'travel'
    const expenseTypeLabel = String(actionPayload.expense_type_label || '差旅费').trim() || '差旅费'
    startAiApplicationDraft(expenseType, expenseTypeLabel)
    return
  }
  if (actionType === 'select_expense_type') {
    const expenseType = String(action?.payload?.expense_type || '').trim()
    const expenseTypeLabel = String(action?.payload?.expense_type_label || action?.label || '').trim()
    const requiresApplicationBeforeReimbursement = Boolean(action?.payload?.requires_application_before_reimbursement)
    startAiExpenseDraft(expenseType, expenseTypeLabel, requiresApplicationBeforeReimbursement)
    return
  }

  if (actionType === 'select_required_application') {
    linkAiExpenseApplication(action?.payload || {})
    return
  }

  if (actionType === 'ai_application_start_inline') {
    aiExpenseDraft.value = null
    const expenseType = String(action?.payload?.expense_type || '').trim()
    const expenseTypeLabel = String(action?.payload?.expense_type_label || action?.label || '').trim()
    startAiApplicationDraft(expenseType, expenseTypeLabel)
    return
  }

  const carryText = String(action?.payload?.carry_text || action?.label || '').trim()
  if (!carryText) {
    return
  }
  if (String(action?.payload?.session_type || '').trim() === SESSION_TYPE_EXPENSE && carryText === '我要报销') {
    pushInlineExpenseSceneSelectionPrompt(carryText, action.label)
    return
  }
  startInlineConversation(carryText, {
    label: action.label,
    source: 'steward-action',
    sessionType: action?.payload?.session_type || 'steward'
  }, action?.payload?.carry_files ? Array.from(selectedFiles.value) : [])
}

function pushInlineExpenseSceneSelectionPrompt(originalMessage, selectedLabel = '') {
  const sourceText = String(originalMessage || '我要报销').trim()
  if (!conversationStarted.value) {
    activateInlineConversation({
      title: String(selectedLabel || sourceText || '报销').trim().slice(0, 18) || '报销'
    })
  }
  assistantDraft.value = ''
  removeWorkbenchDateTag()
  closeWorkbenchDatePicker()
  conversationMessages.value.push(createInlineMessage('user', String(selectedLabel || sourceText).trim()))
  conversationMessages.value.push(createInlineMessage('assistant', buildExpenseSceneSelectionMessage(sourceText), {
    suggestedActions: buildExpenseSceneSelectionActions(sourceText)
  }))
  persistCurrentConversation()
  scrollInlineConversationToBottom()
}

function pushInlineUserMessage(text) {
  conversationMessages.value.push(createInlineMessage('user', String(text || '').trim()))
}

// 选定报销类型后，在当前对话页内启动逐项收集流程；
// 差旅/招待需先查申请单，其余类型直接进入字段填写。
function startAiExpenseDraft(expenseType, expenseTypeLabel, requiresApplicationBeforeReimbursement) {
  if (!conversationStarted.value) {
    activateInlineConversation({ title: String(expenseTypeLabel || '报销').trim().slice(0, 18) || '报销' })
  }
  assistantDraft.value = ''
  removeWorkbenchDateTag()
  closeWorkbenchDatePicker()
  clearAiModeFiles()
  pushInlineUserMessage(`选择${expenseTypeLabel || expenseType || '报销'}`)

  if (requiresApplicationBeforeReimbursement) {
    void resolveAiExpenseApplicationLink(expenseType, expenseTypeLabel)
    return
  }

  const draft = createAiExpenseDraft(expenseType, expenseTypeLabel)
  aiExpenseDraft.value = draft
  conversationMessages.value.push(createInlineMessage('assistant', buildAiExpenseStepPrompt(draft)))
  persistCurrentConversation()
  scrollInlineConversationToBottom()
}

function advanceAiExpenseDraft(answer, files = []) {
  const fileNames = Array.from(files || [])
  pushInlineUserMessage(answer || (fileNames.length ? `上传 ${fileNames.length} 份附件` : ''))
  assistantDraft.value = ''
  clearAiModeFiles()

  const next = applyAiExpenseAnswer(aiExpenseDraft.value, answer, fileNames)
  aiExpenseDraft.value = next

  if (isAiExpenseDraftComplete(next)) {
    conversationMessages.value.push(createInlineMessage('assistant', `${buildAiExpenseSummary(next)}\n\n如果哪一项需要修改，直接告诉我；确认无误后我再帮你生成报销草稿。`))
    aiExpenseDraft.value = null
  } else {
    conversationMessages.value.push(createInlineMessage('assistant', buildAiExpenseStepPrompt(next)))
  }
  persistCurrentConversation()
  scrollInlineConversationToBottom()
}

async function resolveAiExpenseApplicationLink(expenseType, expenseTypeLabel) {
  let claims = null
  try {
    claims = await fetchExpenseClaims()
  } catch {
    aiExpenseDraft.value = null
    conversationMessages.value.push(createInlineMessage('assistant', '查询可关联申请单时出现异常，请稍后再试，我先暂停这次报销流程。'))
    persistCurrentConversation()
    scrollInlineConversationToBottom()
    return
  }

  const candidates = filterRequiredApplicationCandidates(claims, expenseType, currentUser.value || {})
  aiExpenseDraft.value = createAiExpenseDraft(expenseType, expenseTypeLabel)

  if (!candidates.length) {
    conversationMessages.value.push(createInlineMessage('assistant', buildRequiredApplicationMissingText(expenseType), {
      suggestedActions: [{
        label: '在当前对话里发起申请',
        description: '逐项收集出差申请要点，整理后你可以提交到申请助手',
        icon: 'mdi mdi-file-plus-outline',
        action_type: 'ai_application_start_inline',
        payload: {
          expense_type: expenseType,
          expense_type_label: expenseTypeLabel
        }
      }]
    }))
    persistCurrentConversation()
    scrollInlineConversationToBottom()
    return
  }

  conversationMessages.value.push(createInlineMessage('assistant', buildRequiredApplicationSelectionText(expenseType, candidates), {
    suggestedActions: buildRequiredApplicationActions(candidates, 'select_required_application')
  }))
  persistCurrentConversation()
  scrollInlineConversationToBottom()
}

function linkAiExpenseApplication(application = {}) {
  const draft = aiExpenseDraft.value
  if (!draft) {
    return
  }
  const claimNo = String(application.application_claim_no || '').trim()
  pushInlineUserMessage(`关联申请单 ${claimNo}`.trim())

  const linked = {
    ...draft,
    applicationClaim: application,
    values: {
      ...draft.values,
      reason: String(application.application_reason || '').trim(),
      location: String(application.application_location || '').trim(),
      time_range: String(application.application_business_time || '').trim(),
      amount: String(application.application_amount_label || application.application_amount || '').trim()
    },
    // 申请单已带出事由/时间/地点/金额，直接进入票据确认
    stepKey: 'attachments'
  }
  aiExpenseDraft.value = linked
  conversationMessages.value.push(createInlineMessage('assistant', [
    `已关联申请单${claimNo ? ` ${claimNo}` : ''}，事由、时间、地点、金额我先用申请单的内容预填了。`,
    '',
    '再确认一下票据：可以现在上传，或回复“稍后上传”。'
  ].join('\n')))
  persistCurrentConversation()
  scrollInlineConversationToBottom()
}

// 进入申请草稿：在当前 AI 对话页内逐项收集出差申请要点，
// 不跳工作台、不调用旧 applyGuided 流程。
function startAiApplicationDraft(expenseType, expenseTypeLabel) {
  pushInlineUserMessage('在当前对话里先发起申请')
  const draft = createAiApplicationDraft(expenseType, expenseTypeLabel)
  aiApplicationDraft.value = draft
  conversationMessages.value.push(createInlineMessage('assistant', buildAiApplicationStepPrompt(draft)))
  persistCurrentConversation()
  scrollInlineConversationToBottom()
}

function advanceAiApplicationDraft(answer, files = []) {
  const fileNames = Array.from(files || [])
  pushInlineUserMessage(answer || (fileNames.length ? `上传 ${fileNames.length} 份附件` : ''))
  assistantDraft.value = ''
  clearAiModeFiles()

  const next = applyAiApplicationAnswer(aiApplicationDraft.value, answer, fileNames)
  aiApplicationDraft.value = next

  if (isAiApplicationDraftComplete(next)) {
    conversationMessages.value.push(createInlineMessage('assistant', buildAiApplicationSummary(next)))
    aiApplicationDraft.value = null
  } else {
    conversationMessages.value.push(createInlineMessage('assistant', buildAiApplicationStepPrompt(next)))
  }
  persistCurrentConversation()
  scrollInlineConversationToBottom()
}

function requestDeleteCurrentConversation() {
  if (!conversationMessages.value.length) {
    return
  }
  deleteDialogOpen.value = true
}

function cancelDeleteConversation() {
  deleteDialogOpen.value = false
}

function confirmDeleteConversation() {
  const nextHistory = conversationId.value
    ? deleteAiWorkbenchConversation(currentUser.value || {}, conversationId.value)
    : refreshConversationHistory()
  emit('conversation-history-change', nextHistory)
  resetInlineConversationState()
  emit('conversation-change', { id: '', title: '' })
  toast('已删除当前对话。')
  focusAiModeInput()
}

async function copyInlineMessage(message) {
  try {
    await navigator.clipboard?.writeText(message.content)
    toast('已复制内容。')
  } catch {
    toast('当前浏览器暂不支持自动复制。')
  }
}

function quoteInlineMessage(message) {
  const quote = `> ${message.content}\n\n`
  assistantDraft.value = assistantDraft.value ? assistantDraft.value + '\n' + quote : quote
  focusAiModeInput()
}

function markInlineMessageFeedback(message, feedback) {
  message.feedback = feedback
  persistCurrentConversation()
  toast(feedback === 'up' ? '已记录有帮助反馈。' : '已记录需要改进反馈。')
}

function triggerAiModeFileUpload() {
  fileInputRef.value?.click()
}

function handleAiModeFilesChange(event) {
  selectedFiles.value = Array.from(event.target.files || []).slice(0, 10)
  if (fileInputRef.value) {
    fileInputRef.value.value = ''
  }
  focusAiModeInput()
}

function clearAiModeFiles() {
  selectedFiles.value = []
  if (fileInputRef.value) {
    fileInputRef.value.value = ''
  }
}

function handleVoiceInput() {
  toast('语音输入正在准备中，您可以先输入文字需求。')
  focusAiModeInput()
}

watch(
  () => props.sidebarCommand?.seq,
  () => {
    const command = props.sidebarCommand || {}
    if (command.type === 'new-chat') {
      startNewInlineConversation()
      return
    }
    if (command.type === 'search-chat') {
      openInlineSearchConversation()
      return
    }
    if (command.type === 'open-recent') {
      openInlineRecentConversation(command.payload || {})
    }
  }
)

onMounted(() => {
  loadSystemSettings()
  refreshConversationHistory()
  document.addEventListener('click', handleWorkbenchDatePickerOutside)
})

onBeforeUnmount(() => {
  document.removeEventListener('click', handleWorkbenchDatePickerOutside)
})
</script>

<style scoped src="../../assets/styles/components/personal-workbench-ai-mode.css"></style>
