<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"
      :disabled="isAiModeInputLocked"
      @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="移除日期" :disabled="isAiModeInputLocked" @click="removeWorkbenchDateTag">
                <i class="mdi mdi-close"></i>
              </button>
            </div>

            <textarea
              ref="assistantInputRef"
              v-model="assistantDraft"
              maxlength="1000"
              rows="3"
              :placeholder="isAiModeInputLocked ? '费用测算中，请稍等...' : '今天我能帮您做点什么？'"
              :disabled="isAiModeInputLocked"
              @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"
                  :disabled="isAiModeInputLocked"
                  @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' }"
                      :disabled="isAiModeInputLocked"
                      @click="setWorkbenchDateMode('single')"
                    >
                      单日
                    </button>
                    <button
                      type="button"
                      :class="{ active: workbenchDateMode === 'range' }"
                      :disabled="isAiModeInputLocked"
                      @click="setWorkbenchDateMode('range')"
                    >
                      范围
                    </button>
                  </div>

                  <label v-if="workbenchDateMode === 'single'" class="workbench-ai-date-field">
                    <span>业务日期</span>
                    <input
                      v-model="workbenchSingleDate"
                      type="date"
                      :disabled="isAiModeInputLocked"
                      @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"
                        :disabled="isAiModeInputLocked"
                        @change="handleWorkbenchDateInputChange('range-start')"
                      />
                    </label>
                    <label class="workbench-ai-date-field">
                      <span>结束日期</span>
                      <input
                        v-model="workbenchRangeEndDate"
                        type="date"
                        :disabled="isAiModeInputLocked"
                        @change="handleWorkbenchDateInputChange('range-end')"
                      />
                    </label>
                  </div>

                  <div class="workbench-ai-date-actions">
                    <button type="button" class="ghost" :disabled="isAiModeInputLocked" @click="clearWorkbenchDateSelection">清除</button>
                    <button
                      type="button"
                      class="primary"
                      :disabled="!workbenchCanApplyDateSelection || isAiModeInputLocked"
                      @click="applyWorkbenchDateSelection"
                    >
                      完成
                    </button>
                  </div>
                </div>
              </div>
              <button
                type="button"
                class="workbench-ai-icon-btn"
                title="上传附件"
                aria-label="上传附件"
                :disabled="isAiModeInputLocked"
                @click="triggerAiModeFileUpload"
              >
                <i class="mdi mdi-paperclip"></i>
              </button>
              <button
                type="button"
                class="workbench-ai-icon-btn"
                title="语音输入"
                aria-label="语音输入"
                :disabled="isAiModeInputLocked"
                @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 || isAiModeInputLocked"
                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" :disabled="isAiModeInputLocked" @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"
              :disabled="isAiModeInputLocked"
              @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"
                  @click.capture="handleAiAnswerMarkdownClick($event)"
                  v-html="renderInlineConversationHtml(message.content)"
                ></div>

                <Transition name="structured-card-reveal" appear>
                  <div
                    v-if="message.applicationPreview"
                    class="workbench-ai-application-preview application-preview-shell"
                    aria-label="申请信息核对结果"
                  >
                    <div
                      class="application-preview-table"
                      role="table"
                      aria-label="申请信息核对表"
                    >
                      <div class="application-preview-row head" role="row">
                        <span role="columnheader">字段</span>
                        <span role="columnheader">内容</span>
                      </div>
                      <div
                        v-for="row in resolveInlineApplicationPreviewRows(message)"
                        :key="`${message.id}-${row.key}`"
                        class="application-preview-row"
                        :class="{
                          missing: row.missing,
                          editable: row.editable,
                          highlight: row.highlight,
                          'is-disabled': isApplicationPreviewEstimatePending(message)
                        }"
                        role="row"
                        :tabindex="row.editable && !isApplicationPreviewEstimatePending(message) ? 0 : -1"
                        :aria-label="row.editable ? `编辑${row.label}` : row.label"
                        @click.stop="row.editable && !isApplicationPreviewEstimatePending(message) && !isApplicationPreviewEditing(message, row.key) && openApplicationPreviewEditor(message, row.key, row.value)"
                        @keydown.enter.prevent="row.editable && !isApplicationPreviewEstimatePending(message) && !isApplicationPreviewEditing(message, row.key) && openApplicationPreviewEditor(message, row.key, row.value)"
                        @keydown.space.prevent="row.editable && !isApplicationPreviewEstimatePending(message) && !isApplicationPreviewEditing(message, row.key) && openApplicationPreviewEditor(message, row.key, row.value)"
                      >
                        <span class="application-preview-label" role="cell">{{ row.label }}</span>
                        <span class="application-preview-value" role="cell">
                          <input
                            v-if="isApplicationPreviewEditing(message, row.key) && resolveInlineApplicationPreviewEditorControl(row.key) === 'text'"
                            v-model="applicationPreviewEditor.draftValue"
                            class="application-preview-input"
                            type="text"
                            autofocus
                            :disabled="isApplicationPreviewEstimatePending(message)"
                            @click.stop
                            @keydown.stop="handleInlineApplicationPreviewEditorKeydown($event, message)"
                            @blur="commitInlineApplicationPreviewEditor(message)"
                          />
                          <select
                            v-else-if="isApplicationPreviewEditing(message, row.key) && resolveInlineApplicationPreviewEditorControl(row.key) === 'select'"
                            v-model="applicationPreviewEditor.draftValue"
                            class="application-preview-input application-preview-select"
                            autofocus
                            :disabled="isApplicationPreviewEstimatePending(message)"
                            @click.stop
                            @change="commitInlineApplicationPreviewEditor(message)"
                            @keydown.stop="handleInlineApplicationPreviewEditorKeydown($event, message)"
                            @blur="commitInlineApplicationPreviewEditor(message)"
                          >
                            <option value="">请选择</option>
                            <option
                              v-for="option in resolveApplicationPreviewEditorOptions(row.key)"
                              :key="`${message.id}-${row.key}-${option}`"
                              :value="option"
                            >
                              {{ option }}
                            </option>
                          </select>
                          <template v-else>
                            <span class="application-preview-text">{{ row.value }}</span>
                            <button
                              v-if="row.editable"
                              type="button"
                              class="application-preview-edit-btn"
                              title="修改内容"
                              aria-label="修改内容"
                              :disabled="isApplicationPreviewEstimatePending(message)"
                              @click.stop="openApplicationPreviewEditor(message, row.key, row.value)"
                            >
                              <i class="mdi mdi-pencil-outline"></i>
                            </button>
                          </template>
                        </span>
                      </div>
                    </div>

                    <div
                      v-if="resolveInlineApplicationPreviewMissingFields(message).length"
                      class="application-preview-footer application-preview-footer-missing"
                      aria-live="polite"
                    >
                      <span class="application-preview-missing-prefix">当前还需要补充：</span>
                      <span class="application-preview-missing-list">
                        <template
                          v-for="(field, index) in resolveInlineApplicationPreviewMissingFields(message)"
                          :key="`${message.id}-missing-${field}`"
                        >
                          <span class="application-preview-missing-chip">{{ field }}</span>
                          <span
                            v-if="index < resolveInlineApplicationPreviewMissingFields(message).length - 1"
                            class="application-preview-missing-separator"
                          >、</span>
                        </template>
                      </span>
                      <span class="application-preview-missing-suffix">。点击表格字段补齐后，费用测算会自动刷新。</span>
                    </div>
                    <div
                      v-else-if="buildInlineApplicationPreviewFooterText(message)"
                      class="application-preview-footer workbench-ai-answer-markdown"
                      v-html="renderInlineConversationHtml(buildInlineApplicationPreviewFooterText(message))"
                    ></div>
                  </div>
                </Transition>

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

                <div v-if="canShowInlineSuggestedActions(message)" class="workbench-ai-suggested-actions">
                  <button
                    v-for="action in message.suggestedActions"
                    :key="`${message.id}-${action.label}`"
                    type="button"
                    :disabled="isInlineSuggestedActionDisabled(action, message)"
                    @click="handleInlineSuggestedAction(action, message)"
                  >
                    <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" :disabled="isAiModeInputLocked" @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="移除日期" :disabled="isAiModeInputLocked" @click="removeWorkbenchDateTag">
                  <i class="mdi mdi-close"></i>
                </button>
              </div>

              <textarea
                ref="assistantInputRef"
                v-model="assistantDraft"
                maxlength="1000"
                rows="3"
                :placeholder="isAiModeInputLocked ? '费用测算中，请稍等...' : '继续和小财管家对话...'"
                :disabled="isAiModeInputLocked"
                @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"
                    :disabled="isAiModeInputLocked"
                    @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' }"
                        :disabled="isAiModeInputLocked"
                        @click="setWorkbenchDateMode('single')"
                      >
                        单日
                      </button>
                      <button
                        type="button"
                        :class="{ active: workbenchDateMode === 'range' }"
                        :disabled="isAiModeInputLocked"
                        @click="setWorkbenchDateMode('range')"
                      >
                        范围
                      </button>
                    </div>

                    <label v-if="workbenchDateMode === 'single'" class="workbench-ai-date-field">
                      <span>业务日期</span>
                      <input
                        v-model="workbenchSingleDate"
                        type="date"
                        :disabled="isAiModeInputLocked"
                        @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"
                          :disabled="isAiModeInputLocked"
                          @change="handleWorkbenchDateInputChange('range-start')"
                        />
                      </label>
                      <label class="workbench-ai-date-field">
                        <span>结束日期</span>
                        <input
                          v-model="workbenchRangeEndDate"
                          type="date"
                          :disabled="isAiModeInputLocked"
                          @change="handleWorkbenchDateInputChange('range-end')"
                        />
                      </label>
                    </div>

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

    <Transition name="workbench-ai-confirm-fade">
      <div
        v-if="applicationSubmitConfirmOpen"
        class="workbench-ai-confirm-mask"
        role="presentation"
        @click.self="cancelInlineApplicationSubmitConfirm"
      >
        <div
          class="workbench-ai-confirm-dialog"
          role="dialog"
          aria-modal="true"
          aria-labelledby="workbench-ai-submit-confirm-title"
        >
          <h3 id="workbench-ai-submit-confirm-title">确认直接提交申请？</h3>
          <p>确认后系统会先查询你名下相同日期的申请单；若发现重复或重叠日期，会停止提交并列出已有单据供你查看。</p>
          <p>若核查通过，申请单会直接进入审批流程。</p>
          <div class="workbench-ai-confirm-actions">
            <button type="button" class="ghost" @click="cancelInlineApplicationSubmitConfirm">取消</button>
            <button type="button" class="primary" :disabled="sending" @click="confirmInlineApplicationSubmit">确认直接提交</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 { renderAiConversationHtml } from '../../utils/aiConversationHtmlRenderer.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 {
  buildApplicationPreviewFooterMessage,
  buildApplicationPreviewRows,
  buildApplicationTemplatePreview,
  buildLocalApplicationPreview,
  buildLocalApplicationPreviewMessage,
  normalizeApplicationPreview
} from '../../utils/expenseApplicationPreview.js'
import {
  buildAiApplicationPrecheck,
  buildAiApplicationPrecheckThinkingEvents,
  buildAiApplicationSubmitConflictMessage,
  isAiApplicationPrecheckBlocking
} from '../../utils/aiApplicationPrecheckModel.js'
import { useApplicationPreviewEditor } from '../../views/scripts/useApplicationPreviewEditor.js'
import {
  buildAiDocumentQueryConditionSummary,
  buildAiDocumentQueryMessage,
  filterAiDocumentQueryRecords,
  resolveAiDocumentQueryIntent
} from '../../utils/aiDocumentQueryModel.js'
import {
  buildRequiredApplicationActions,
  buildRequiredApplicationMissingText,
  buildRequiredApplicationSelectionText,
  filterRequiredApplicationCandidates
} from '../../views/scripts/travelReimbursementApplicationLinkModel.js'
import {
  calculateTravelReimbursement,
  extractExpenseClaimItems,
  fetchApprovalExpenseClaims,
  fetchExpenseClaims
} from '../../services/reimbursements.js'
import {
  AI_APPLICATION_ACTION_SAVE_DRAFT,
  AI_APPLICATION_ACTION_SUBMIT,
  runAiApplicationPreviewAction
} from '../../services/aiApplicationPreviewActions.js'

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

const emit = defineEmits(['conversation-change', 'conversation-history-change', 'open-document'])
const AI_DOCUMENT_QUERY_STEP_DELAY_MS = 320
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 thinkingExpandedMessageIds = ref(new Set())
const thinkingCollapsedMessageIds = ref(new Set())
const deleteDialogOpen = ref(false)
const applicationSubmitConfirmOpen = ref(false)
const applicationSubmitConfirmContext = ref(null)
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 AI_DOCUMENT_DETAIL_HREF_PREFIX = '#ai-open-document-detail:'
const AI_APPLICATION_DETAIL_HREF_PREFIX = '#ai-open-application-detail:'

const {
  applicationPreviewEditor,
  resolveApplicationPreviewEditorControl,
  resolveApplicationPreviewEditorOptions,
  refreshApplicationPreviewEstimate,
  isApplicationPreviewEditing,
  openApplicationPreviewEditor,
  commitApplicationPreviewEditor,
  cancelApplicationPreviewEditor,
  handleApplicationPreviewEditorKeydown
} = useApplicationPreviewEditor({
  persistSessionState: () => persistCurrentConversation(),
  toast,
  calculateTravelReimbursement,
  currentUser
})

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 applicationPreviewEstimatePending = computed(() => (
  conversationMessages.value.some((message) => isApplicationPreviewEstimatePending(message))
))

const isAiModeInputLocked = computed(() => applicationPreviewEstimatePending.value)

const canSubmitAiModePrompt = computed(() => (
  !isAiModeInputLocked.value && (
    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 : [],
    applicationPreview: options.applicationPreview || null,
    draftPayload: options.draftPayload || null,
    text: options.text || normalizedContent,
    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 : [],
    applicationPreview: message.applicationPreview || null,
    draftPayload: message.draftPayload || null,
    text: message.text || message.content || ''
  })
}

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

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
  applicationSubmitConfirmOpen.value = false
  applicationSubmitConfirmContext.value = null
  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 renderInlineConversationHtml(content) {
  return renderAiConversationHtml(content)
}

function isApplicationPreviewEstimatePendingPreview(applicationPreview = {}) {
  const fields = normalizeApplicationPreview(applicationPreview).fields || {}
  return [
    fields.transportPolicy,
    fields.policyEstimate,
    fields.transportEstimatedAmount,
    fields.amount
  ].some((value) => /正在|查询中/.test(String(value || '')))
}

function isApplicationPreviewEstimatePending(message = {}) {
  return Boolean(message?.applicationPreview && isApplicationPreviewEstimatePendingPreview(message.applicationPreview))
}

function shouldRefreshInlineApplicationPreviewEstimate(fieldKey = '') {
  return ['transportMode', 'time', 'location', 'days'].includes(String(fieldKey || '').trim())
}

function canShowInlineSuggestedActions(message = {}) {
  return Boolean(message?.suggestedActions?.length) && !isApplicationPreviewEstimatePending(message)
}

function isInlineSuggestedActionDisabled(action = {}, message = {}) {
  const actionType = String(action?.action_type || '').trim()
  return (
    [AI_APPLICATION_ACTION_SAVE_DRAFT, AI_APPLICATION_ACTION_SUBMIT].includes(actionType) &&
    isApplicationPreviewEstimatePending(message)
  )
}

function resolveInlineApplicationPreviewRows(message) {
  return buildApplicationPreviewRows(message?.applicationPreview || {})
}

function resolveInlineApplicationPreviewMissingFields(message) {
  return normalizeApplicationPreview(message?.applicationPreview || {}).missingFields || []
}

function resolveInlineApplicationPreviewEditorControl(fieldKey) {
  const control = resolveApplicationPreviewEditorControl(fieldKey)
  return control === 'date' ? 'text' : control
}

function syncInlineApplicationPreviewMessageContent(message) {
  if (!message?.applicationPreview) {
    return
  }
  const nextContent = buildLocalApplicationPreviewMessage(message.applicationPreview)
  message.content = nextContent
  message.text = nextContent
  message.suggestedActions = buildInlineApplicationPreviewSuggestedActions(message.applicationPreview, message.draftPayload)
}

async function commitInlineApplicationPreviewEditor(message) {
  const shouldLockForEstimate = shouldRefreshInlineApplicationPreviewEstimate(applicationPreviewEditor.value.fieldKey)
  if (shouldLockForEstimate) {
    message.suggestedActions = []
    persistCurrentConversation()
  }
  const committed = await commitApplicationPreviewEditor(message)
  syncInlineApplicationPreviewMessageContent(message)
  persistCurrentConversation()
  return committed
}

function handleInlineApplicationPreviewEditorKeydown(event, message) {
  if (event.key === 'Enter') {
    event.preventDefault()
    void commitInlineApplicationPreviewEditor(message)
    return
  }
  if (event.key === 'Escape') {
    event.preventDefault()
    cancelApplicationPreviewEditor()
    return
  }
  handleApplicationPreviewEditorKeydown(event, message)
}

function buildInlineApplicationPreviewFooterText(message) {
  const normalized = normalizeApplicationPreview(message?.applicationPreview || {})
  if (isApplicationPreviewEstimatePending(message)) {
    return '费用测算正在同步，请稍等，完成后才能保存草稿或直接提交。'
  }
  if (normalized.validationIssues?.length || normalized.missingFields?.length) {
    return buildApplicationPreviewFooterMessage(normalized)
  }
  return '申请核对表已补齐，费用测算已同步。你仍可点击表格继续修改；确认无误后，可以点击下方按钮保存草稿或直接提交，也可以直接回复“保存草稿”或“提交”。'
}

function buildInlineApplicationPreviewSuggestedActions(applicationPreview = {}, draftPayload = null) {
  if (isApplicationPreviewEstimatePendingPreview(applicationPreview)) {
    return []
  }
  const normalized = normalizeApplicationPreview(applicationPreview)
  const actions = [{
    label: '保存草稿',
    description: '先保存当前申请表，后续可以继续补充或提交。',
    icon: 'mdi mdi-content-save-outline',
    action_type: AI_APPLICATION_ACTION_SAVE_DRAFT,
    payload: { draftPayload }
  }]
  if (normalized.readyToSubmit) {
    actions.push({
      label: '直接提交',
      description: '提交前先核查相同日期申请单，确认通过后进入审批流程。',
      icon: 'mdi mdi-send-check-outline',
      action_type: AI_APPLICATION_ACTION_SUBMIT,
      payload: { draftPayload }
    })
  }
  return actions
}

function resolveLatestApplicationPreviewMessage() {
  return [...conversationMessages.value]
    .reverse()
    .find((message) => message.role === 'assistant' && message.applicationPreview)
}

function resolveInlineApplicationPreviewActionFromText(text = '') {
  const normalized = String(text || '').replace(/\s+/g, '').trim()
  if (!normalized) {
    return ''
  }
  if (/^(保存草稿|保存|存草稿|先保存)$/.test(normalized)) {
    return AI_APPLICATION_ACTION_SAVE_DRAFT
  }
  if (/^(提交|提交申请|确认提交|提交审批|直接提交)$/.test(normalized)) {
    return AI_APPLICATION_ACTION_SUBMIT
  }
  return ''
}

function extractInlineApplicationDraftPayload(payload = {}) {
  const result = payload?.result && typeof payload.result === 'object' ? payload.result : {}
  return result.draft_payload && typeof result.draft_payload === 'object'
    ? result.draft_payload
    : payload?.draft_payload && typeof payload.draft_payload === 'object'
      ? payload.draft_payload
      : null
}

function normalizeInlineApplicationResultTableCell(value, fallback = '-') {
  const text = String(value || '')
    .replace(/\s*\n+\s*/g, ' ')
    .replace(/\|/g, '｜')
    .trim()
  return text || fallback
}

function buildInlineApplicationActionDetailHref(reference = '') {
  const value = String(reference || '').trim()
  return value ? `${AI_APPLICATION_DETAIL_HREF_PREFIX}${encodeURIComponent(value)}` : ''
}

function resolveInlineApplicationActionDocumentInfo(draftPayload = {}) {
  const source = draftPayload && typeof draftPayload === 'object' ? draftPayload : {}
  return {
    claimNo: String(source.claim_no || source.claimNo || source.document_no || source.documentNo || '').trim(),
    claimId: String(source.claim_id || source.claimId || source.id || '').trim(),
    statusLabel: String(source.status_label || source.statusLabel || source.status || '').trim(),
    approvalStage: String(source.approval_stage || source.approvalStage || '').trim(),
    documentTypeLabel: String(
      source.document_type_label ||
        source.documentTypeLabel ||
        source.application_type_label ||
        source.applicationTypeLabel ||
        source.expense_type_label ||
        source.expenseTypeLabel ||
        ''
    ).trim()
  }
}

function buildInlineApplicationResultTable(draftPayload = {}, options = {}) {
  const info = resolveInlineApplicationActionDocumentInfo(draftPayload)
  const reference = info.claimNo || info.claimId
  const href = buildInlineApplicationActionDetailHref(reference)
  const actionText = href ? `[查看](${href})` : '-'
  return [
    '| 单据类型 | 单据编号 | 单据状态 | 当前节点 | 操作 |',
    '| --- | --- | --- | --- | --- |',
    `| ${normalizeInlineApplicationResultTableCell(info.documentTypeLabel || options.documentTypeLabel, '出差申请')} | ${normalizeInlineApplicationResultTableCell(reference)} | ${normalizeInlineApplicationResultTableCell(info.statusLabel || options.statusLabel)} | ${normalizeInlineApplicationResultTableCell(info.approvalStage || options.stageLabel)} | ${actionText} |`
  ].join('\n')
}

function buildInlineApplicationPreviewActionResultText(actionType, payload = {}) {
  const draftPayload = extractInlineApplicationDraftPayload(payload) || {}
  const claimNo = String(draftPayload.claim_no || draftPayload.claimNo || '').trim()
  const approvalStage = String(draftPayload.approval_stage || draftPayload.approvalStage || '').trim()
  if (actionType === AI_APPLICATION_ACTION_SUBMIT) {
    return [
      '### 申请单据已生成，并已进入审批流程',
      approvalStage ? `系统已推送到 **${approvalStage}**，当前节点：${approvalStage}。` : '系统已推送到审批流程，当前节点：审批中。',
      buildInlineApplicationResultTable(draftPayload, {
        statusLabel: '审批中',
        stageLabel: approvalStage || '直属领导审批',
        documentTypeLabel: '出差申请'
      }),
      '需要查看完整详情时，请点击列表最后一列的“查看”进入单据详情。'
    ].filter(Boolean).join('\n\n')
  }
  return [
    '### 申请草稿已保存',
    claimNo ? `系统已保存当前申请草稿，草稿单号：**${claimNo}**。` : '系统已保存当前申请草稿。',
    buildInlineApplicationResultTable(draftPayload, {
      statusLabel: '草稿',
      stageLabel: '待提交',
      documentTypeLabel: '出差申请'
    }),
    '后续请点击表格最后一列的“查看”进入详情页继续核对。'
  ].filter(Boolean).join('\n\n')
}

function buildInlineApplicationDetailAction(draftPayload = {}) {
  const claimNo = String(draftPayload?.claim_no || draftPayload?.claimNo || '').trim()
  if (!claimNo) {
    return []
  }
  return [{
    label: '查看单据详情',
    description: '打开刚生成的申请单详情。',
    icon: 'mdi mdi-open-in-new',
    action_type: 'open_application_detail',
    payload: {
      claim_no: claimNo,
      claim_id: String(draftPayload.claim_id || draftPayload.claimId || '').trim(),
      document_type: 'application'
    }
  }]
}

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 buildAiRequiredApplicationGateSuggestedActions(flow, prompt = '') {
  if (flow.flowId === 'travel_application') {
    return [{
      label: '确认发起出差申请',
      description: '确认后生成完整申请表，并预填已识别的时间、地点和事由。',
      icon: 'mdi mdi-file-plus-outline',
      action_type: 'ai_application_start_inline',
      payload: {
        expense_type: 'travel',
        expense_type_label: '差旅费',
        carry_text: prompt
      }
    }]
  }
  if (flow.flowId === 'travel_reimbursement') {
    return [{
      label: '确认关联已有申请单',
      description: '确认后查询你名下可关联的差旅申请单，并进入关联步骤。',
      icon: 'mdi mdi-link-variant',
      action_type: 'steward_confirm_flow',
      payload: {
        steward_confirm_flow: true,
        flow_id: 'travel_reimbursement',
        expense_type: 'travel',
        expense_type_label: '差旅费',
        carry_text: prompt
      }
    }]
  }
  return []
}

function resolveInlineApplicationDraftIdentity(payload = {}) {
  const source = payload && typeof payload === 'object' ? payload : {}
  return {
    claimId: String(source.claim_id || source.claimId || source.id || '').trim(),
    claimNo: String(source.claim_no || source.claimNo || source.document_no || source.documentNo || '').trim()
  }
}

function isSameInlineApplicationDraftClaim(claim = {}, draftPayload = {}) {
  const draftIdentity = resolveInlineApplicationDraftIdentity(draftPayload)
  if (!draftIdentity.claimId && !draftIdentity.claimNo) {
    return false
  }
  const claimIdentity = resolveInlineApplicationDraftIdentity(claim)
  return Boolean(
    (draftIdentity.claimId && claimIdentity.claimId && draftIdentity.claimId === claimIdentity.claimId) ||
      (draftIdentity.claimNo && claimIdentity.claimNo && draftIdentity.claimNo === claimIdentity.claimNo)
  )
}

function buildInlineApplicationSubmitPrecheckPayload(claimsPayload, draftPayload = {}) {
  const items = extractExpenseClaimItems(claimsPayload)
    .filter((claim) => !isSameInlineApplicationDraftClaim(claim, draftPayload))
  return { items }
}

function completeInlineThinkingEvents(events = []) {
  return events.map((event) => ({
    ...event,
    status: event.status === 'failed' ? 'failed' : 'completed'
  }))
}

function buildInitialInlineApplicationSubmitThinkingEvents() {
  return [
    {
      eventId: 'application-precheck-overlap',
      title: '核查同时间段申请单',
      content: '正在查询你名下可见申请单，检查是否存在相同或重叠日期。',
      status: 'running'
    },
    {
      eventId: 'application-precheck-budget',
      title: '评估预算与审批影响',
      content: '等待单据重叠核查完成后，继续评估预算占用和审批影响。',
      status: 'pending'
    },
    {
      eventId: 'application-submit',
      title: '提交申请单据',
      content: '等待提交前核查完成。',
      status: 'pending'
    }
  ]
}

function buildInlineApplicationSubmitThinkingEvents(precheck = {}) {
  const blocked = isAiApplicationPrecheckBlocking(precheck)
  return buildAiApplicationPrecheckThinkingEvents(precheck).map((event) => {
    if (event.eventId !== 'application-precheck-form') {
      return event
    }
    return {
      eventId: 'application-submit',
      title: blocked ? '暂停提交申请' : '提交申请单据',
      content: blocked
        ? '发现相同或重叠日期已有申请单，已暂停本次提交。'
        : '提交前核查通过，正在生成申请单据并推送审批流程。',
      status: blocked ? 'completed' : 'running'
    }
  })
}

function buildFailedInlineApplicationSubmitThinkingEvents(error) {
  return [
    {
      eventId: 'application-precheck-overlap',
      title: '核查同时间段申请单',
      content: `查询已有申请单失败：${String(error?.message || error || '未知错误')}`,
      status: 'failed'
    },
    {
      eventId: 'application-submit',
      title: '暂停提交申请',
      content: '因为未能完成提交前重复日期核查，系统没有提交本次申请。',
      status: 'failed'
    }
  ]
}

function requestInlineApplicationSubmitConfirmation(targetMessage, options = {}) {
  applicationSubmitConfirmContext.value = {
    messageId: String(targetMessage?.id || '').trim(),
    draftPayload: targetMessage?.draftPayload || options.draftPayload || null,
    userText: String(options.userText || '直接提交').trim() || '直接提交'
  }
  applicationSubmitConfirmOpen.value = true
  persistCurrentConversation()
}

function cancelInlineApplicationSubmitConfirm() {
  applicationSubmitConfirmOpen.value = false
  applicationSubmitConfirmContext.value = null
  focusAiModeInput()
}

function confirmInlineApplicationSubmit() {
  const context = applicationSubmitConfirmContext.value || {}
  applicationSubmitConfirmOpen.value = false
  applicationSubmitConfirmContext.value = null
  const sourceMessage = conversationMessages.value.find((message) => message.id === context.messageId)
  if (!sourceMessage?.applicationPreview) {
    toast('当前申请表已变化，请重新点击直接提交。')
    return
  }
  void executeInlineApplicationPreviewAction(AI_APPLICATION_ACTION_SUBMIT, sourceMessage, {
    confirmed: true,
    skipUserMessage: false,
    draftPayload: context.draftPayload || null,
    userText: context.userText || '直接提交'
  })
}

async function runInlineApplicationSubmitPrecheck(targetMessage, pendingMessage, normalizedPreview, options = {}) {
  try {
    const claimsPayload = await fetchExpenseClaims({ page: 1, pageSize: 100 })
    const precheck = buildAiApplicationPrecheck(normalizedPreview, {
      claimsPayload: buildInlineApplicationSubmitPrecheckPayload(
        claimsPayload,
        targetMessage.draftPayload || options.draftPayload || null
      ),
      currentUser: currentUser.value || {},
      expenseType: 'travel'
    })
    const thinkingEvents = buildInlineApplicationSubmitThinkingEvents(precheck)
    const blocked = isAiApplicationPrecheckBlocking(precheck)

    if (blocked) {
      replaceInlineMessage(
        pendingMessage.id,
        createInlineMessage('assistant', buildAiApplicationSubmitConflictMessage(normalizedPreview, precheck), {
          id: pendingMessage.id,
          stewardPlan: {
            streamStatus: 'completed',
            thinkingEvents
          }
        })
      )
      persistCurrentConversation()
      scrollInlineConversationToBottom({ force: inlineConversationAutoScrollPinned.value })
      return false
    }

    const message = conversationMessages.value.find((item) => item.id === pendingMessage.id) || pendingMessage
    updateInlineMessageContent(message, '提交前核查通过，正在提交申请并进入审批流程...')
    message.stewardPlan = {
      ...(message.stewardPlan || {}),
      streamStatus: 'streaming',
      thinkingEvents
    }
    await nextTick()
    scrollInlineConversationToBottom({ force: inlineConversationAutoScrollPinned.value })
    return true
  } catch (error) {
    replaceInlineMessage(
      pendingMessage.id,
      createInlineMessage('assistant', [
        '### 提交前核查失败',
        '系统未能完成相同日期申请单查询，所以本次申请没有提交。',
        '请稍后重试；如果仍然失败，请先到单据中心核对是否已有同日期申请单。'
      ].join('\n\n'), {
        id: pendingMessage.id,
        stewardPlan: {
          streamStatus: 'failed',
          thinkingEvents: buildFailedInlineApplicationSubmitThinkingEvents(error)
        }
      })
    )
    toast('提交前核查失败，已暂停提交。')
    persistCurrentConversation()
    return false
  }
}

async function executeInlineApplicationPreviewAction(actionType, sourceMessage = null, options = {}) {
  const targetMessage = sourceMessage?.applicationPreview ? sourceMessage : resolveLatestApplicationPreviewMessage()
  if (!targetMessage?.applicationPreview) {
    toast('当前没有可提交的申请表。')
    return false
  }

  const normalizedPreview = normalizeApplicationPreview(targetMessage.applicationPreview)
  const isSubmit = actionType === AI_APPLICATION_ACTION_SUBMIT
  const userText = String(options.userText || (isSubmit ? '直接提交' : '保存草稿')).trim()

  if (isSubmit && !normalizedPreview.readyToSubmit) {
    if (!options.skipUserMessage) {
      pushInlineApplicationActionUserMessage(userText)
    }
    const missingText = normalizedPreview.missingFields?.length
      ? `当前还缺少：${normalizedPreview.missingFields.join('、')}。`
      : ''
    const validationText = normalizedPreview.validationIssues?.length
      ? normalizedPreview.validationIssues.map((item) => item.message).join('；')
      : ''
    conversationMessages.value.push(createInlineMessage('assistant', [
      '### 暂不能提交申请',
      missingText || validationText || '当前申请表还未通过提交校验。',
      '请先点击表格中的字段补充或修正，补齐后我会开放“直接提交”入口。'
    ].filter(Boolean).join('\n\n')))
    persistCurrentConversation()
    scrollInlineConversationToBottom()
    return true
  }

  if (isSubmit && !options.confirmed) {
    requestInlineApplicationSubmitConfirmation(targetMessage, { ...options, userText })
    return true
  }

  if (!options.skipUserMessage) {
    pushInlineApplicationActionUserMessage(userText)
  }

  sending.value = true
  const pendingMessage = createInlineMessage(
    'assistant',
    isSubmit ? '正在提交前核查相同日期申请单...' : '正在保存申请草稿...',
    {
      pending: true,
      stewardPlan: {
        streamStatus: 'streaming',
        thinkingEvents: isSubmit
          ? buildInitialInlineApplicationSubmitThinkingEvents()
          : [
              {
                eventId: 'application-save-draft',
                title: '保存申请草稿',
                content: '正在按当前申请表内容保存草稿。',
                status: 'running'
              }
            ]
      }
    }
  )
  conversationMessages.value.push(pendingMessage)
  scrollInlineConversationToBottom()

  try {
    if (isSubmit) {
      const precheckPassed = await runInlineApplicationSubmitPrecheck(
        targetMessage,
        pendingMessage,
        normalizedPreview,
        options
      )
      if (!precheckPassed) {
        return true
      }
    }

    const payload = await runAiApplicationPreviewAction({
      actionType,
      applicationPreview: normalizedPreview,
      currentUser: currentUser.value || {},
      conversationId: conversationId.value,
      draftPayload: targetMessage.draftPayload || options.draftPayload || null
    })
    const draftPayload = extractInlineApplicationDraftPayload(payload)
    if (draftPayload) {
      targetMessage.draftPayload = draftPayload
    }
    targetMessage.suggestedActions = []
    replaceInlineMessage(
      pendingMessage.id,
      createInlineMessage('assistant', buildInlineApplicationPreviewActionResultText(actionType, payload), {
        id: pendingMessage.id,
        stewardPlan: {
          streamStatus: 'completed',
          thinkingEvents: completeInlineThinkingEvents(resolveInlineThinkingEvents(pendingMessage))
        },
        suggestedActions: isSubmit ? buildInlineApplicationDetailAction(draftPayload) : []
      })
    )
    persistCurrentConversation()
    scrollInlineConversationToBottom({ force: inlineConversationAutoScrollPinned.value })
    return true
  } catch (error) {
    replaceInlineMessage(
      pendingMessage.id,
      createInlineMessage('assistant', error?.message || (isSubmit ? '申请提交失败，请稍后重试。' : '申请草稿保存失败，请稍后重试。'), {
        id: pendingMessage.id,
        stewardPlan: {
          streamStatus: 'failed',
          thinkingEvents: resolveInlineThinkingEvents(pendingMessage).map((item) => ({
            ...item,
            status: 'failed'
          }))
        }
      })
    )
    toast(error?.message || (isSubmit ? '申请提交失败。' : '申请草稿保存失败。'))
    persistCurrentConversation()
    return true
  } finally {
    sending.value = false
    scrollInlineConversationToBottom({ force: inlineConversationAutoScrollPinned.value })
  }
}

function handleInlineApplicationPreviewTextAction(prompt) {
  if (applicationPreviewEstimatePending.value) {
    toast('请等待费用测算完成后再继续操作。')
    return true
  }
  const actionType = resolveInlineApplicationPreviewActionFromText(prompt)
  if (!actionType || !resolveLatestApplicationPreviewMessage()) {
    return false
  }
  void executeInlineApplicationPreviewAction(actionType, null, { userText: prompt })
  return true
}

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
  }
}

function parseAiDocumentDetailHref(href = '') {
  const value = String(href || '').trim()
  if (!value.startsWith(AI_DOCUMENT_DETAIL_HREF_PREFIX)) {
    return null
  }
  const encodedReference = value.slice(AI_DOCUMENT_DETAIL_HREF_PREFIX.length)
  if (!encodedReference) {
    return null
  }
  try {
    const reference = decodeURIComponent(encodedReference).trim()
    return reference ? { reference } : null
  } catch {
    return { reference: encodedReference }
  }
}

function parseAiApplicationDetailHref(href = '') {
  const value = String(href || '').trim()
  if (!value.startsWith(AI_APPLICATION_DETAIL_HREF_PREFIX)) {
    return null
  }
  const encodedReference = value.slice(AI_APPLICATION_DETAIL_HREF_PREFIX.length)
  if (!encodedReference) {
    return null
  }
  try {
    const reference = decodeURIComponent(encodedReference).trim()
    return reference ? { reference } : null
  } catch {
    return { reference: encodedReference }
  }
}

function buildAiDocumentDetailRequest(detailReference = {}) {
  const reference = String(detailReference.reference || '').trim()
  const isApplication = /^APP?-/i.test(reference)
  return {
    id: reference,
    claimId: reference,
    claimNo: reference,
    documentNo: reference,
    documentType: isApplication ? 'application' : 'reimbursement',
    documentTypeCode: isApplication ? 'application' : 'reimbursement',
    detailLookupOnly: true,
    source: 'workbench',
    returnTo: 'workbench'
  }
}

function handleAiAnswerMarkdownClick(event) {
  const target = event?.target
  const link = target?.closest?.('a[href^="#ai-open-document-detail:"], a[href^="#ai-open-application-detail:"]')
  if (!link) {
    return
  }
  const href = link.getAttribute('href')
  const detailReference = parseAiDocumentDetailHref(href) || parseAiApplicationDetailHref(href)
  if (!detailReference) {
    return
  }
  event.preventDefault()
  event.stopPropagation()
  emit('open-document', buildAiDocumentDetailRequest(detailReference))
}

function waitForAiDocumentQueryStep() {
  return new Promise((resolve) => {
    globalThis.setTimeout(resolve, AI_DOCUMENT_QUERY_STEP_DELAY_MS)
  })
}

async function updateAiDocumentQueryThinking(pendingMessage, thinkingEvents, streamStatus = 'streaming') {
  const message = conversationMessages.value.find((item) => item.id === pendingMessage.id) || pendingMessage
  message.stewardPlan = {
    ...(message.stewardPlan || {}),
    streamStatus,
    thinkingEvents
  }
  scrollInlineConversationToBottom({ force: inlineConversationAutoScrollPinned.value })
  await nextTick()
}

function completeAiDocumentQueryEvent(events, eventId, content = '') {
  return events.map((event) => (
    event.eventId === eventId
      ? {
          ...event,
          content: content || event.content,
          status: 'completed'
        }
      : event
  ))
}

function failAiDocumentQueryEvents(events) {
  return events.map((event) => ({
    ...event,
    status: event.status === 'completed' ? 'completed' : 'failed'
  }))
}

async function handleAiDocumentQueryIntent(prompt, pendingMessage) {
  const intent = resolveAiDocumentQueryIntent(prompt)
  if (!intent) {
    return false
  }

  const conditionSummary = buildAiDocumentQueryConditionSummary(intent)
  let thinkingEvents = [
    {
      eventId: 'document-query-parse',
      title: '解析自然语言筛选条件',
      content: `正在从你的问题里提取查询来源、单据类型、时间、状态、费用类型、关键词和金额条件。当前识别：${conditionSummary}。`,
      status: 'running'
    },
    {
      eventId: 'document-query-fetch',
      title: '查询业务单据接口',
      content: intent.source === 'approval' ? '等待调用待我审核单据接口。' : '等待调用我名下单据接口。',
      status: 'pending'
    },
    {
      eventId: 'document-query-filter',
      title: '组合筛选单据',
      content: '等待接口返回后，再按已识别条件做二次筛选。',
      status: 'pending'
    }
  ]
  await updateAiDocumentQueryThinking(pendingMessage, thinkingEvents)
  await waitForAiDocumentQueryStep()

  thinkingEvents = completeAiDocumentQueryEvent(thinkingEvents, 'document-query-parse')
  thinkingEvents = thinkingEvents.map((event) => (
    event.eventId === 'document-query-fetch'
      ? {
          ...event,
          content: intent.source === 'approval'
            ? '正在查询待我审核的单据，接口范围为待办/待审单据列表。'
            : '正在查询我名下的单据，接口范围为当前用户可见单据列表。',
          status: 'running'
        }
      : event
  ))
  await updateAiDocumentQueryThinking(pendingMessage, thinkingEvents)

  try {
    const payload = intent.source === 'approval'
      ? await fetchApprovalExpenseClaims({ page: 1, pageSize: 100 })
      : await fetchExpenseClaims({ page: 1, pageSize: 100 })
    const rawCount = extractExpenseClaimItems(payload).length
    const filteredRecords = filterAiDocumentQueryRecords(payload, intent)
    thinkingEvents = completeAiDocumentQueryEvent(
      thinkingEvents,
      'document-query-fetch',
      `接口返回 ${rawCount} 张候选单据，开始按自然语言条件筛选。`
    )
    thinkingEvents = thinkingEvents.map((event) => (
      event.eventId === 'document-query-filter'
        ? {
            ...event,
            content: `按“${conditionSummary}”组合筛选，当前命中 ${filteredRecords.length} 张。`,
            status: 'running'
          }
        : event
    ))
    await updateAiDocumentQueryThinking(pendingMessage, thinkingEvents)
    await waitForAiDocumentQueryStep()

    const finalMessageText = buildAiDocumentQueryMessage(intent, payload)
    thinkingEvents = completeAiDocumentQueryEvent(
      thinkingEvents,
      'document-query-filter',
      `筛选完成，命中 ${filteredRecords.length} 张单据，已生成卡片结果。`
    )
    replaceInlineMessage(
      pendingMessage.id,
      createInlineMessage('assistant', finalMessageText, {
        id: pendingMessage.id,
        stewardPlan: {
          streamStatus: 'completed',
          thinkingEvents
        },
        suggestedActions: []
      })
    )
  } catch (error) {
    const finalMessageText = error?.message || '查询单据时出现异常，请稍后再试。'
    replaceInlineMessage(
      pendingMessage.id,
      createInlineMessage('assistant', finalMessageText, {
        id: pendingMessage.id,
        stewardPlan: {
          streamStatus: 'failed',
          thinkingEvents: failAiDocumentQueryEvents(thinkingEvents)
        }
      })
    )
  }

  persistCurrentConversation()
  return true
}

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 {
    if (await handleAiDocumentQueryIntent(prompt, pendingMessage)) {
      shouldAutoScrollOnFinish = inlineConversationAutoScrollPinned.value
      return
    }

    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
          ? buildAiRequiredApplicationGateSuggestedActions(requiredApplicationContinuationFlow, prompt)
          : buildStewardSuggestedActions(plan)
      })
    )
    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 = []) {
  if (isAiModeInputLocked.value) {
    toast('请等待费用测算完成后再继续操作。')
    return
  }
  const cleanPrompt = buildInlinePromptText(prompt, files)
  if (!cleanPrompt || sending.value) {
    return
  }

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

  if (handleInlineApplicationPreviewTextAction(cleanPrompt)) {
    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 = {}, sourceMessage = null) {
  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 ([AI_APPLICATION_ACTION_SAVE_DRAFT, AI_APPLICATION_ACTION_SUBMIT].includes(actionType)) {
    if (isInlineSuggestedActionDisabled(action, sourceMessage)) {
      toast('请等待费用测算完成后再继续操作。')
      return
    }
    void executeInlineApplicationPreviewAction(actionType, sourceMessage, {
      userText: action.label,
      draftPayload: actionPayload.draftPayload || actionPayload.draft_payload || null
    })
    return
  }
  if (actionType === 'open_application_detail') {
    const claimNo = String(actionPayload.claim_no || actionPayload.claimNo || '').trim()
    const claimId = String(actionPayload.claim_id || actionPayload.claimId || '').trim()
    emit('open-document', buildAiDocumentDetailRequest({ reference: claimNo || claimId }))
    return
  }
  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() || '差旅费'
    void startAiApplicationPreview(
      expenseType,
      expenseTypeLabel,
      actionPayload.carry_text || resolveLatestInlineUserPrompt()
    )
    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()
    void startAiApplicationPreview(
      expenseType,
      expenseTypeLabel,
      action?.payload?.carry_text || resolveLatestInlineUserPrompt()
    )
    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 pushInlineApplicationActionUserMessage(text) {
  pushInlineUserMessage(text)
  assistantDraft.value = ''
  removeWorkbenchDateTag()
  closeWorkbenchDatePicker()
  clearAiModeFiles()
}

function resolveLatestInlineUserPrompt() {
  const latestUserMessage = [...conversationMessages.value].reverse().find((message) => message.role === 'user')
  return String(latestUserMessage?.content || '').trim()
}

function normalizeInlineApplicationTypeLabel(expenseTypeLabel, fallback = '费用申请') {
  const label = String(expenseTypeLabel || '').trim()
  if (!label) {
    return fallback
  }
  if (label.endsWith('费用申请') || label.endsWith('申请')) {
    return label
  }
  if (label.endsWith('费用')) {
    return `${label}申请`
  }
  if (label.endsWith('费')) {
    return `${label.slice(0, -1)}费用申请`
  }
  return `${label}申请`
}

function buildInlineApplicationPreview(expenseTypeLabel, sourceText = '') {
  const rawText = String(sourceText || '').trim()
  const preview = rawText
    ? buildLocalApplicationPreview(rawText, currentUser.value || {})
    : buildApplicationTemplatePreview(currentUser.value || {})
  const normalized = normalizeApplicationPreview(preview)
  return normalizeApplicationPreview({
    ...normalized,
    fields: {
      ...(normalized.fields || {}),
      applicationType: normalizeInlineApplicationTypeLabel(
        expenseTypeLabel,
        normalized.fields?.applicationType || '费用申请'
      )
    }
  })
}

// 选定报销类型后，在当前对话页内启动逐项收集流程；
// 差旅/招待需先查申请单，其余类型直接进入字段填写。
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()
}

// 进入申请核对表：复用原有申请预览模型，一次性展示可编辑表格和自动测算结果。
async function startAiApplicationPreview(expenseType, expenseTypeLabel, sourceText = '', options = {}) {
  if (!conversationStarted.value) {
    activateInlineConversation({ title: String(expenseTypeLabel || expenseType || '申请').trim().slice(0, 18) || '申请' })
  }
  const previewSourceText = String(sourceText || resolveLatestInlineUserPrompt()).trim()
  aiExpenseDraft.value = null
  assistantDraft.value = ''
  removeWorkbenchDateTag()
  closeWorkbenchDatePicker()
  clearAiModeFiles()
  if (options.pushUserMessage !== false) {
    pushInlineUserMessage(options.userMessage || '确认发起出差申请')
  }

  const pendingMessage = createInlineMessage('assistant', '正在生成申请核对表，请稍等...', {
    pending: true,
    stewardPlan: {
      streamStatus: 'streaming',
      thinkingEvents: [
        {
          eventId: 'application-preview-build',
          title: '整理申请表字段',
          content: '正在把已识别的时间、地点、事由和差旅类型整理成可编辑表格。',
          status: 'running'
        },
        {
          eventId: 'application-preview-estimate',
          title: '同步费用测算',
          content: '正在刷新差旅规则和费用测算，完成后会直接展示核对表。',
          status: 'pending'
        }
      ]
    }
  })
  conversationMessages.value.push(pendingMessage)
  persistCurrentConversation()
  scrollInlineConversationToBottom()

  try {
    const preview = await refreshApplicationPreviewEstimate(
      buildInlineApplicationPreview(expenseTypeLabel || expenseType, previewSourceText)
    )
    const content = buildLocalApplicationPreviewMessage(preview)
    replaceInlineMessage(pendingMessage.id, createInlineMessage('assistant', content, {
      id: pendingMessage.id,
      applicationPreview: preview,
      suggestedActions: buildInlineApplicationPreviewSuggestedActions(preview),
      stewardPlan: {
        streamStatus: 'completed',
        thinkingEvents: completeInlineThinkingEvents(resolveInlineThinkingEvents(pendingMessage))
      },
      text: content
    }))
  } catch (error) {
    replaceInlineMessage(pendingMessage.id, createInlineMessage('assistant', error?.message || '申请核对表生成失败，请稍后重试。', {
      id: pendingMessage.id,
      stewardPlan: {
        streamStatus: 'failed',
        thinkingEvents: resolveInlineThinkingEvents(pendingMessage).map((item) => ({
          ...item,
          status: 'failed'
        }))
      }
    }))
    toast(error?.message || '申请核对表生成失败。')
  } finally {
    persistCurrentConversation()
    scrollInlineConversationToBottom({ force: inlineConversationAutoScrollPinned.value })
  }
}

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() {
  if (isAiModeInputLocked.value) {
    toast('请等待费用测算完成后再继续操作。')
    return
  }
  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() {
  if (isAiModeInputLocked.value) {
    toast('请等待费用测算完成后再继续操作。')
    return
  }
  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>
