<template>
  <aside class="conversation-sidebar" :class="{ collapsed }">
    <div class="conversation-sidebar-inner">
      <div class="conversation-primary-actions">
        <button
          type="button"
          class="sidebar-nav-row conversation-menu-btn"
          :title="collapsed ? '展开对话记录' : '收起对话记录'"
          @click="$emit('toggle')"
        >
          <span
            class="sidebar-nav-icon chat-icon"
            data-tutorial="conversation-menu"
            aria-hidden="true"
          >
            <svg viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
              <path
                d="M5 6.5c0-1.38 1.12-2.5 2.5-2.5h13c1.38 0 2.5 1.12 2.5 2.5v8.5c0 1.38-1.12 2.5-2.5 2.5h-5.6l-3.4 3.2.6-3.2H7.5c-1.38 0-2.5-1.12-2.5-2.5V6.5z"
                stroke="currentColor"
                stroke-width="1.45"
                stroke-linejoin="round"
              />
              <path
                d="M9 9.5h10"
                stroke="currentColor"
                stroke-width="1.45"
                stroke-linecap="round"
              />
              <path d="M9 13h6" stroke="currentColor" stroke-width="1.45" stroke-linecap="round" />
            </svg>
          </span>
          <span class="sidebar-nav-label">对话记录</span>
        </button>

        <button type="button" class="sidebar-nav-row" title="新建对话" @click="$emit('create')">
          <span
            class="sidebar-nav-icon pencil-icon"
            data-tutorial="quick-new-conversation"
            aria-hidden="true"
          >
            <svg
              xmlns="http://www.w3.org/2000/svg"
              width="24"
              height="24"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="1.6"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <path
                d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"
              />
              <path d="m15 5 4 4" />
            </svg>
          </span>
          <span class="sidebar-nav-label">新建对话</span>
        </button>

        <button
          v-if="hostWorkspaceEnabled"
          type="button"
          class="sidebar-nav-row workspace-entry-btn"
          :class="{ active: workspaceSwitcherOpen }"
          :title="workspaceKind === 'project' ? '项目' : '工作区'"
          data-tutorial="workspace-toggle"
          ref="workspaceEntryBtn"
          @click="toggleWorkspaceSwitcher"
        >
          <span class="sidebar-nav-icon layers-icon" aria-hidden="true">
            <!-- 与其他导航按钮统一：内联 layers 图标，CSS 限到 18px 与铅笔一致，避免 mask 图标异步加载闪烁 -->
            <svg
              xmlns="http://www.w3.org/2000/svg"
              width="24"
              height="24"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
              stroke-width="1.6"
              stroke-linecap="round"
              stroke-linejoin="round"
            >
              <path
                d="M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z"
              />
              <path
                d="M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12"
              />
              <path
                d="M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17"
              />
            </svg>
          </span>
          <span class="sidebar-nav-label">{{ workspaceKind === 'project' ? '项目' : '工作区' }}</span>
        </button>
      </div>

      <div class="conversation-search">
        <label class="search-input-wrap">
          <span class="sidebar-nav-icon search-inline-icon" aria-hidden="true">
            <span class="icon icon-sm" :style="iconStyle('search')"></span>
          </span>
          <input
            class="search-input"
            :value="searchQuery"
            placeholder="搜索对话"
            @input="$emit('search', ($event.target as HTMLInputElement).value)"
            @keydown.enter.prevent="
              $emit('search-submit', ($event.target as HTMLInputElement).value)
            "
          />
        </label>
      </div>

      <div class="conversation-list">
        <div v-if="runningTaskItems.length && !searchActive && !isGroupByWorkspaceActive" class="running-task-section">
          <div class="running-task-section-title">运行中的任务</div>
          <button
            v-for="task in runningTaskItems"
            :key="task.task_id"
            type="button"
            class="running-task-item"
            :class="{ active: task.conversation_id === currentConversationId }"
            @click="$emit('select-running-task', task)"
          >
            <span class="running-task-workspace">{{ task.workspace_label || task.workspace_id }}</span>
            <span class="running-task-main">
              <span class="running-task-title">{{
                task.conversation_title || task.message || '运行中的对话'
              }}</span>
              <span
                v-if="isTaskActive(task)"
                class="conversation-running-loader running-task-state"
                aria-label="运行中"
              ></span>
              <span v-else class="conversation-complete-check running-task-state" aria-label="已完成"></span>
            </span>
          </button>
        </div>
        <template v-if="isGroupByWorkspaceActive && !searchActive">
          <div class="workspace-groups" :class="{ 'switch-instant': workspaceSwitchInstant }">
            <div
              v-for="ws in sortedWorkspaces"
              :key="String(ws.workspace_id || ws.label)"
              class="workspace-group"
            >
              <div
                class="workspace-group-header"
                :class="{ active: String(ws.workspace_id || '') === currentWorkspaceId }"
                @mouseenter="hoverWorkspaceId = String(ws.workspace_id || '')"
                @mouseleave="hoverWorkspaceId = null"
              >
                <button
                  type="button"
                  class="workspace-group-toggle"
                  @click="toggleWorkspaceExpanded(String(ws.workspace_id || ''))"
                >
                  <span
                    class="icon icon-sm workspace-folder-icon"
                    :style="iconStyle(isWorkspaceExpanded(String(ws.workspace_id || '')) ? 'folderOpen' : 'folderClosed')"
                    aria-hidden="true"
                  ></span>
                  <span class="workspace-group-label">{{ ws.label || ws.workspace_id }}</span>
                  <span
                    v-if="String(ws.workspace_id || '') === currentWorkspaceId"
                    class="workspace-current-dot"
                    aria-label="当前工作区"
                  ></span>
                  <span
                    v-if="
                      isWorkspaceRunning(String(ws.workspace_id || '')) &&
                      !isWorkspaceExpanded(String(ws.workspace_id || '')) &&
                      hoverWorkspaceId !== String(ws.workspace_id || '') &&
                      openWorkspaceMenuId !== String(ws.workspace_id || '')
                    "
                    class="workspace-running-loader"
                    aria-label="运行中"
                  ></span>
                </button>
                <div
                  class="workspace-group-actions"
                  :class="{ visible: hoverWorkspaceId === String(ws.workspace_id || '') || openWorkspaceMenuId === String(ws.workspace_id || '') }"
                >
                  <button
                    type="button"
                    class="workspace-new-btn"
                    title="新建对话"
                    aria-label="新建对话"
                    @click="handleCreateWorkspaceConversation(String(ws.workspace_id || ''))"
                  >
                    <span class="icon icon-sm" :style="iconStyle('pencil')" aria-hidden="true"></span>
                  </button>
                  <button
                    type="button"
                    class="workspace-more-btn"
                    title="更多操作"
                    aria-label="更多操作"
                    :aria-expanded="openWorkspaceMenuId === String(ws.workspace_id || '')"
                    @click.stop="toggleWorkspaceMenu(String(ws.workspace_id || ''))"
                  >
                    <span aria-hidden="true"></span>
                  </button>
                  <div
                    class="workspace-actions-menu"
                    :class="{ open: openWorkspaceMenuId === String(ws.workspace_id || '') }"
                  >
                    <button type="button" @click="handlePinWorkspace(String(ws.workspace_id || ''))">
                      <span>{{ pinnedWorkspaceIds.has(String(ws.workspace_id || '')) ? '取消置顶' : '置顶' }}</span>
                    </button>
                    <button
                      v-if="props.versioningHostMode"
                      type="button"
                      @click="handleRevealWorkspace(String(ws.workspace_id || ''))"
                    >
                      <span>在文件夹中打开</span>
                    </button>
                    <button type="button" @click="startRenameWorkspace(ws)">
                      <span>重命名</span>
                    </button>
                  </div>
                </div>
              </div>
              <div
                class="workspace-group-children"
                :class="{ expanded: isWorkspaceExpanded(String(ws.workspace_id || '')) }"
              >
                <div class="workspace-group-children-inner">
                  <div
                    v-if="!getWorkspaceGroup(String(ws.workspace_id || ''))?.conversations?.length"
                    class="workspace-no-conversations"
                  >
                    暂无对话
                  </div>
                  <template v-else>
                    <div
                      class="workspace-conversations-viewport"
                      :style="{
                        '--workspace-visible-count': getWorkspaceGroup(String(ws.workspace_id || ''))?.visibleLimit || 5
                      }"
                    >
                      <transition-group
                        name="conversation-delete"
                        tag="div"
                        class="workspace-conversations-list"
                        :class="`animation-mode-${conversationListAnimationMode}`"
                        @before-leave="lockLeaveItemPosition"
                      >
                        <div
                          v-for="conv in getWorkspaceVisibleAndBufferConversations(String(ws.workspace_id || ''))"
                          :key="conv.id"
                          class="conversation-item workspace-conversation-item"
                          :class="{
                            active: conv.id === currentConversationId,
                            running: isConversationActive(conv.id) || isConversationCompleted(conv.id),
                            'insert-from-left': conversationInsertAnimations[conv.id] === 'create',
                            'insert-from-under': conversationInsertAnimations[conv.id] === 'duplicate',
                            'duplicate-source-mask': conversationInsertAnimations[conv.id] === 'duplicateSource'
                          }"
                          @click="handleWorkspaceConversationClick(conv.id, String(ws.workspace_id || ''))"
                        >
                          <div class="conversation-title">{{ conv.title }}</div>
                          <div
                            class="conversation-action-wrap"
                            :class="{ 'menu-open': openActionMenuId === conv.id }"
                            @click.stop
                          >
                            <span
                              v-if="isConversationActive(conv.id)"
                              class="conversation-running-loader"
                              aria-label="运行中"
                              title="运行中"
                            ></span>
                            <span
                              v-else-if="isConversationCompleted(conv.id)"
                              class="conversation-complete-check"
                              aria-label="已完成"
                              title="已完成"
                            ></span>
                            <button
                              v-else
                              type="button"
                              class="conversation-more-btn"
                              title="更多操作"
                              aria-label="更多操作"
                              :data-action-trigger="conv.id"
                              :aria-expanded="openActionMenuId === conv.id"
                              @click="toggleActionMenu(conv.id)"
                            >
                              <span aria-hidden="true"></span>
                            </button>
                            <div
                              v-if="shouldShowActionMenu(conv.id)"
                              class="conversation-actions-menu conversation-actions-menu--fixed"
                              :class="{ open: openActionMenuId === conv.id }"
                              :style="{
                                top: actionMenuPosition.top + 'px',
                                right: actionMenuPosition.right + 'px'
                              }"
                            >
                              <button
                                type="button"
                                @click="
                                  openActionMenuId = null;
                                  $emit('duplicate', conv.id);
                                "
                              >
                                <span class="icon icon-sm" :style="iconStyle('copy')" aria-hidden="true"></span>
                                <span>复制</span>
                              </button>
                              <button
                                type="button"
                                class="danger"
                                @click="
                                  openActionMenuId = null;
                                  $emit('delete', { id: conv.id, workspaceId: String(ws.workspace_id || '') });
                                "
                              >
                                <span class="icon icon-sm" :style="iconStyle('trash')" aria-hidden="true"></span>
                                <span>删除</span>
                              </button>
                            </div>
                          </div>
                        </div>
                      </transition-group>
                    </div>
                    <div
                      v-if="getWorkspaceHasMore(String(ws.workspace_id || ''))"
                      class="load-more workspace-load-more"
                    >
                      <button
                        class="load-more-btn"
                        type="button"
                        :disabled="getWorkspaceGroup(String(ws.workspace_id || ''))?.loadingMore"
                        @click="loadMoreWorkspaceConversations(String(ws.workspace_id || ''))"
                      >
                        {{ getWorkspaceGroup(String(ws.workspace_id || ''))?.loadingMore ? '载入中...' : '加载更多' }}
                      </button>
                    </div>
                  </template>
                </div>
              </div>
            </div>
          </div>
        </template>

        <template v-else-if="isGroupByWorkspaceActive && searchActive">
          <div class="workspace-groups search-result-groups">
            <div v-if="!searchInProgress && !sortedSearchGroups.length" class="no-conversations">
              未找到匹配对话
            </div>
            <div
              v-for="group in sortedSearchGroups"
              :key="String(group.workspace_id || '')"
              class="workspace-group"
            >
              <div class="workspace-group-header search-result-group-header">
                <div class="workspace-group-toggle search-result-group-toggle">
                  <span
                    class="icon icon-sm workspace-folder-icon"
                    :style="iconStyle('folderClosed')"
                    aria-hidden="true"
                  ></span>
                  <span class="workspace-group-label">{{ group.label || group.workspace_id }}</span>
                </div>
              </div>
              <div class="workspace-group-children expanded">
                <div class="workspace-group-children-inner">
                  <div class="workspace-conversations-list">
                    <div
                      v-for="conv in group.results"
                      :key="conv.id"
                      class="conversation-item workspace-conversation-item"
                      :class="{
                        active: conv.id === currentConversationId,
                        running: isConversationActive(conv.id) || isConversationCompleted(conv.id)
                      }"
                      @click="handleWorkspaceConversationClick(conv.id, String(group.workspace_id || ''))"
                    >
                      <div class="conversation-title">{{ conv.title }}</div>
                      <div
                        class="conversation-action-wrap"
                        :class="{ 'menu-open': openActionMenuId === conv.id }"
                        @click.stop
                      >
                        <span
                          v-if="isConversationActive(conv.id)"
                          class="conversation-running-loader"
                          aria-label="运行中"
                          title="运行中"
                        ></span>
                        <span
                          v-else-if="isConversationCompleted(conv.id)"
                          class="conversation-complete-check"
                          aria-label="已完成"
                          title="已完成"
                        ></span>
                        <button
                          v-else
                          type="button"
                          class="conversation-more-btn"
                          title="更多操作"
                          aria-label="更多操作"
                          :data-action-trigger="conv.id"
                          :aria-expanded="openActionMenuId === conv.id"
                          @click="toggleActionMenu(conv.id)"
                        >
                          <span aria-hidden="true"></span>
                        </button>
                        <div
                          v-if="shouldShowActionMenu(conv.id)"
                          class="conversation-actions-menu conversation-actions-menu--fixed"
                          :class="{ open: openActionMenuId === conv.id }"
                          :style="{
                            top: actionMenuPosition.top + 'px',
                            right: actionMenuPosition.right + 'px'
                          }"
                        >
                          <button
                            type="button"
                            @click="
                              openActionMenuId = null;
                              $emit('duplicate', conv.id);
                            "
                          >
                            <span class="icon icon-sm" :style="iconStyle('copy')" aria-hidden="true"></span>
                            <span>复制</span>
                          </button>
                          <button
                            type="button"
                            class="danger"
                            @click="
                              openActionMenuId = null;
                              $emit('delete', { id: conv.id, workspaceId: String(group.workspace_id || '') });
                            "
                          >
                            <span class="icon icon-sm" :style="iconStyle('trash')" aria-hidden="true"></span>
                            <span>删除</span>
                          </button>
                        </div>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </template>

        <template v-else>
          <div
            v-if="loading && !displayConversations.length && !searchActive && !isDeletingConversation"
            class="loading-conversations"
          >
            正在加载...
          </div>
          <div v-else-if="!displayConversations.length && !loading && !isDeletingConversation" class="no-conversations">
            {{ searchActive ? '未找到匹配对话' : '暂无对话记录' }}
          </div>
          <transition-group
            v-else
            name="conversation-delete"
            tag="div"
            class="conversation-list-items"
            :class="`animation-mode-${conversationListAnimationMode}`"
          >
            <div
              v-for="conv in displayConversations"
              :key="conv.id"
              class="conversation-item"
              :class="{
                active: conv.id === currentConversationId,
                running: isConversationActive(conv.id) || isConversationCompleted(conv.id),
                'insert-from-left': conversationInsertAnimations[conv.id] === 'create',
                'insert-from-under': conversationInsertAnimations[conv.id] === 'duplicate',
                'duplicate-source-mask': conversationInsertAnimations[conv.id] === 'duplicateSource',
                deleting: pendingDeletingConversationIds.includes(conv.id)
              }"
              @click="
                openActionMenuId = null;
                $emit('select', conv.id);
              "
            >
              <div class="conversation-title">{{ conv.title }}</div>
              <div
                class="conversation-action-wrap"
                :class="{ 'menu-open': openActionMenuId === conv.id }"
                @click.stop
              >
                <span
                  v-if="isConversationActive(conv.id)"
                  class="conversation-running-loader"
                  aria-label="运行中"
                  title="运行中"
                ></span>
                <span
                  v-else-if="isConversationCompleted(conv.id)"
                  class="conversation-complete-check"
                  aria-label="已完成"
                  title="已完成"
                ></span>
                <button
                  v-else
                  type="button"
                  class="conversation-more-btn"
                  title="更多操作"
                  aria-label="更多操作"
                  :data-action-trigger="conv.id"
                  :aria-expanded="openActionMenuId === conv.id"
                  @click="toggleActionMenu(conv.id)"
                >
                  <span aria-hidden="true"></span>
                </button>
                            <div
                              v-if="!isConversationActive(conv.id) && !isConversationCompleted(conv.id) && openActionMenuId === conv.id"
                              class="conversation-actions-menu conversation-actions-menu--fixed"
                              :class="{ open: openActionMenuId === conv.id }"
                              :style="{
                                top: actionMenuPosition.top + 'px',
                                right: actionMenuPosition.right + 'px'
                              }"
                            >
                              <button
                                type="button"
                                @click="
                                  openActionMenuId = null;
                                  $emit('duplicate', conv.id);
                                "
                              >
                                <span class="icon icon-sm" :style="iconStyle('copy')" aria-hidden="true"></span>
                                <span>复制</span>
                              </button>
                              <button
                                type="button"
                                class="danger"
                                @click="
                                  openActionMenuId = null;
                                  $emit('delete', conv.id);
                                "
                              >
                                <span class="icon icon-sm" :style="iconStyle('trash')" aria-hidden="true"></span>
                                <span>删除</span>
                              </button>
                            </div>
              </div>
            </div>
          </transition-group>
        </template>

        <div v-if="!searchActive && !isGroupByWorkspaceActive && hasMore" class="load-more">
          <button
            class="load-more-btn"
            type="button"
            :disabled="loadingMore"
            @click="$emit('load-more')"
          >
            {{ loadingMore ? '载入中...' : '加载更多' }}
          </button>
        </div>
        <div v-else-if="searchActive" class="load-more search-more">
          <div v-if="searchInProgress" class="search-progress">
            <span class="search-spinner" aria-hidden="true">
              <span class="search-spinner-orbit">
                <span class="icon icon-sm search-spinner-icon" :style="iconStyle('search')"></span>
              </span>
            </span>
            <span class="search-progress-text">搜索中...</span>
          </div>
          <button
            v-else-if="searchMoreAvailable"
            class="load-more-btn"
            type="button"
            @click="$emit('search-more')"
          >
            搜索更多
          </button>
          <div v-else-if="displayConversations.length" class="search-done">已全部搜索</div>
        </div>
      </div>

      <div
        v-if="renameWorkspaceId"
        class="workspace-rename-overlay"
        @click.self="cancelRenameWorkspace"
      >
        <div class="workspace-rename-card">
          <div class="workspace-rename-title">
            重命名{{ workspaceKind === 'workspace' ? '工作区' : '项目' }}
          </div>
          <input
            ref="renameInput"
            v-model="renameWorkspaceLabel"
            type="text"
            class="workspace-rename-input"
            placeholder="请输入名称"
            @keydown.enter.prevent="submitRenameWorkspace"
            @keydown.esc.prevent="cancelRenameWorkspace"
          />
          <div class="workspace-rename-actions">
            <button type="button" class="workspace-rename-cancel" @click="cancelRenameWorkspace">
              取消
            </button>
            <button type="button" class="workspace-rename-confirm" @click="submitRenameWorkspace">
              确认
            </button>
          </div>
        </div>
      </div>

      <WorkspaceSwitcher
        :open="workspaceSwitcherOpen"
        :anchor-rect="workspaceAnchorRect"
        :workspaces="workspaces"
        :current-workspace-id="currentWorkspaceId"
        :default-workspace-id="defaultWorkspaceId"
        :workspace-kind="workspaceKind"
        :busy="workspaceBusy"
        :create-submitting="workspaceCreateSubmitting"
        :error-message="workspaceError"
        @close="workspaceSwitcherOpen = false"
        @switch="$emit('switch-workspace', $event)"
        @create="$emit('create-workspace', $event)"
        @rename="$emit('rename-workspace', $event)"
        @delete="$emit('delete-workspace', $event)"
        @set-default="$emit('set-default-workspace', $event)"
        @reveal="$emit('reveal-workspace', $event)"
      />

      <div class="conversation-personal-entry" :class="{ active: personalPageVisible }">
        <button
          type="button"
          class="sidebar-nav-row personal-page-btn"
          data-tutorial="open-personal-space"
          title="个人空间"
          @click="$emit('personal')"
        >
          <span class="sidebar-nav-icon" aria-hidden="true">
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor">
              <path
                fill-rule="evenodd"
                clip-rule="evenodd"
                d="M10 9a3 3 0 100-6 3 3 0 000 6zm-7 9a7 7 0 1114 0H3z"
              ></path>
            </svg>
          </span>
          <span class="sidebar-nav-label personal-label">个人空间</span>
        </button>
      </div>
    </div>
  </aside>
</template>

<script setup lang="ts">
defineOptions({ name: 'ConversationSidebar' });

import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { storeToRefs } from 'pinia';
import { useUiStore } from '@/stores/ui';
import { useConversationStore } from '@/stores/conversation';
import { usePersonalizationStore } from '@/stores/personalization';
import WorkspaceSwitcher from './WorkspaceSwitcher.vue';

const props = withDefaults(
  defineProps<{
    formatTime?: (input: unknown) => string;
    iconStyle?: (key: string) => Record<string, string>;
    showCollapseButton?: boolean;
    collapseButtonVariant?: 'toggle' | 'close';
    runningTasks?: any[];
    currentWorkspaceId?: string;
    defaultWorkspaceId?: string;
    workspaces?: any[];
    workspaceKind?: 'workspace' | 'project';
    workspaceBusy?: boolean;
    workspaceCreateSubmitting?: boolean;
    workspaceError?: string;
    hostWorkspaceEnabled?: boolean;
    groupByWorkspace?: boolean;
    versioningHostMode?: boolean;
    /** 当前会话是否仍在运行（用于标记当前正在查看的对话也显示运行中转圈） */
    currentTaskInProgress?: boolean;
    currentConversationId?: string;
  }>(),
  {
    showCollapseButton: true,
    collapseButtonVariant: 'toggle',
    runningTasks: () => [],
    currentWorkspaceId: '',
    defaultWorkspaceId: '',
    workspaces: () => [],
    workspaceKind: 'workspace',
    workspaceBusy: false,
    workspaceCreateSubmitting: false,
    workspaceError: '',
    hostWorkspaceEnabled: false,
    groupByWorkspace: false,
    versioningHostMode: false,
    currentTaskInProgress: false,
    currentConversationId: ''
  }
);

const emit = defineEmits<{
  (event: 'toggle'): void;
  (event: 'create'): void;
  (event: 'search', value: string): void;
  (event: 'search-submit', value: string): void;
  (event: 'select', id: string): void;
  (event: 'select-running-task', task: any): void;
  (event: 'load-more'): void;
  (event: 'search-more'): void;
  (event: 'personal'): void;
  (event: 'delete', id: string): void;
  (event: 'duplicate', id: string): void;
  (event: 'switch-workspace', workspaceId: string): void;
  (event: 'create-workspace', payload: { path: string; label: string }): void;
  (event: 'delete-workspace', item: any): void;
  (event: 'set-default-workspace', workspaceId: string): void;
  (event: 'select-workspace-conversation', payload: { conversationId: string; workspaceId: string }): void;
  (event: 'create-workspace-conversation', workspaceId: string): void;
  (event: 'reveal-workspace', workspaceId: string): void;
  (event: 'rename-workspace', payload: { workspaceId: string; label: string }): void;
  (event: 'pin-workspace', workspaceId: string): void;
}>();

const uiStore = useUiStore();
const conversationStore = useConversationStore();
const personalizationStore = usePersonalizationStore();

const { sidebarCollapsed: collapsed } = storeToRefs(uiStore);
const {
  searchQuery,
  searchResults,
  searchGroups,
  searchActive,
  searchInProgress,
  searchMoreAvailable,
  conversations,
  conversationInsertAnimations,
  conversationListAnimationMode,
  pendingDeletingConversationIds,
  conversationsLoading,
  hasMoreConversations: hasMore,
  loadingMoreConversations: loadingMore,
  currentConversationId
} = storeToRefs(conversationStore);
const { visible: personalPageVisible } = storeToRefs(personalizationStore);

const iconStyle = (key: string) => (props.iconStyle ? props.iconStyle(key) : {});

/* ---------- 工作区切换浮层 ---------- */
const workspaceEntryBtn = ref<HTMLElement | null>(null);
const workspaceSwitcherOpen = ref(false);
const workspaceAnchorRect = ref<{ left: number; top: number; right: number; bottom: number } | null>(null);

const toggleWorkspaceSwitcher = () => {
  if (workspaceSwitcherOpen.value) {
    workspaceSwitcherOpen.value = false;
    return;
  }
  const el = workspaceEntryBtn.value;
  if (el) {
    const rect = el.getBoundingClientRect();
    /* 折叠时 sidebar-inner 保持展开宽、超出部分被 overflow 裁切，
       按钮布局右缘在可视区外；锚点右缘需夹紧到侧边栏可视右缘，
       否则浮层会出现在「展开后的位置」而不是紧贴折叠栏 */
    const sidebarEl = el.closest('.conversation-sidebar');
    const visibleRight = sidebarEl ? sidebarEl.getBoundingClientRect().right : rect.right;
    workspaceAnchorRect.value = {
      left: rect.left,
      top: rect.top,
      right: Math.min(rect.right, visibleRight),
      bottom: rect.bottom
    };
  }
  workspaceSwitcherOpen.value = true;
};
const openActionMenuId = ref<string | null>(null);
const actionMenuPosition = ref<{ top: number; right: number }>({ top: 0, right: 0 });
/* 切换工作区重排期间为 true：禁用子对话 FLIP 位移与分组展开折叠过渡，
   使分组头与子对话全部瞬间移动（不影响新建/复制/删除动画） */
const workspaceSwitchInstant = ref(false);
const hoverWorkspaceId = ref<string | null>(null);
const openWorkspaceMenuId = ref<string | null>(null);
const renameWorkspaceId = ref<string | null>(null);
const renameWorkspaceLabel = ref('');
const renameInput = ref<HTMLInputElement | null>(null);

watch(renameWorkspaceId, async (id) => {
  if (id) {
    await nextTick();
    renameInput.value?.focus();
    renameInput.value?.select();
  }
});

const updateActionMenuPosition = (conversationId: string) => {
  if (typeof document === 'undefined') return;
  const trigger = document.querySelector(`[data-action-trigger="${conversationId}"]`) as HTMLElement | null;
  if (!trigger) return;
  const rect = trigger.getBoundingClientRect();
  actionMenuPosition.value = {
    top: rect.bottom + 6,
    right: (typeof window !== 'undefined' ? window.innerWidth : 0) - rect.right
  };
};

const toggleActionMenu = (conversationId: string) => {
  if (openActionMenuId.value === conversationId) {
    openActionMenuId.value = null;
  } else {
    updateActionMenuPosition(conversationId);
    openActionMenuId.value = conversationId;
  }
};

watch(openActionMenuId, async (id) => {
  if (id) {
    await nextTick();
    updateActionMenuPosition(id);
  }
});

const shouldShowActionMenu = (conversationId: string) => {
  return (
    openActionMenuId.value === conversationId &&
    !isConversationActive(conversationId) &&
    !isConversationCompleted(conversationId)
  );
};

/* 删除离场动画定位修复：分组列表是 flex 容器，按 CSS Flexbox 规范，
   绝对定位子元素的静态位置会跑到容器顶部（而不是元素原来的行位置），
   导致删除非首条对话时离场元素瞬移到顶部。before-leave 时元素仍在文档流中，
   用 offsetTop 锁定原位，leave-active 的 position:absolute 生效后即停在原行。 */
const lockLeaveItemPosition = (el: Element) => {
  const item = el as HTMLElement;
  item.style.top = `${item.offsetTop}px`;
};

const closeActionMenu = () => {
  openActionMenuId.value = null;
};

const pinnedWorkspaceIds = ref<Set<string>>(new Set());
const workspaceOrder = ref<string[]>([]);
const sidebarSortLoaded = ref(false);

const syncPinnedFromStore = () => {
  pinnedWorkspaceIds.value = new Set(
    Array.isArray(personalizationStore.form.sidebar_pinned_workspaces)
      ? personalizationStore.form.sidebar_pinned_workspaces
      : []
  );
};

const syncOrderFromStore = () => {
  workspaceOrder.value = Array.isArray(personalizationStore.form.sidebar_workspace_order)
    ? [...personalizationStore.form.sidebar_workspace_order]
    : [];
};

const persistPinnedWorkspaces = () => {
  if (!sidebarSortLoaded.value) return;
  const next = Array.from(pinnedWorkspaceIds.value);
  const current = personalizationStore.form.sidebar_pinned_workspaces || [];
  if (JSON.stringify(next) === JSON.stringify(current)) return;
  personalizationStore.updateField({
    key: 'sidebar_pinned_workspaces',
    value: next
  });
};

const persistWorkspaceOrder = () => {
  if (!sidebarSortLoaded.value) return;
  const next = Array.from(workspaceOrder.value);
  const current = personalizationStore.form.sidebar_workspace_order || [];
  if (JSON.stringify(next) === JSON.stringify(current)) return;
  personalizationStore.updateField({
    key: 'sidebar_workspace_order',
    value: next
  });
};

const bumpCurrentWorkspaceToTop = () => {
  if (!sidebarSortLoaded.value) return;
  const currentId = String(props.currentWorkspaceId || '');
  if (!currentId || pinnedWorkspaceIds.value.has(currentId)) return;
  if (workspaceOrder.value[0] === currentId) return;
  const order = workspaceOrder.value.filter((item) => item !== currentId);
  order.unshift(currentId);
  workspaceOrder.value = order;
  persistWorkspaceOrder();
};

const syncWorkspaceOrderWithWorkspaces = (workspaces: any[]) => {
  if (!sidebarSortLoaded.value || !Array.isArray(workspaces)) return;
  const ids = workspaces
    .map((ws) => String(ws?.workspace_id || ''))
    .filter(Boolean);
  const existing = new Set(workspaceOrder.value);
  const pinnedIds = new Set(
    ids.filter((id) => pinnedWorkspaceIds.value.has(id))
  );
  const newIds = ids.filter((id) => !existing.has(id) && !pinnedIds.has(id));
  const removedIds = new Set(
    workspaceOrder.value.filter((id) => !ids.includes(id))
  );
  if (newIds.length || removedIds.size) {
    workspaceOrder.value = workspaceOrder.value
      .filter((id) => !removedIds.has(id))
      .concat(newIds);
    persistWorkspaceOrder();
  }
};

const loadSidebarSortFromStore = () => {
  if (!personalizationStore.loaded || sidebarSortLoaded.value) return;
  syncPinnedFromStore();
  syncOrderFromStore();
  sidebarSortLoaded.value = true;
  if (Array.isArray(props.workspaces) && props.workspaces.length > 0) {
    syncWorkspaceOrderWithWorkspaces(props.workspaces);
  }
  bumpCurrentWorkspaceToTop();
};

watch(
  () => personalizationStore.loaded,
  (loaded) => {
    if (loaded) loadSidebarSortFromStore();
  },
  { immediate: true }
);

watch(
  () => personalizationStore.form.sidebar_pinned_workspaces,
  () => {
    if (!sidebarSortLoaded.value) return;
    syncPinnedFromStore();
  },
  { deep: true }
);

watch(
  () => personalizationStore.form.sidebar_workspace_order,
  () => {
    if (!sidebarSortLoaded.value) return;
    syncOrderFromStore();
    if (Array.isArray(props.workspaces) && props.workspaces.length > 0) {
      syncWorkspaceOrderWithWorkspaces(props.workspaces);
    }
    bumpCurrentWorkspaceToTop();
  },
  { deep: true }
);

watch(
  () => props.workspaces,
  (list) => {
    if (Array.isArray(list) && list.length > 0) {
      syncWorkspaceOrderWithWorkspaces(list);
    }
  },
  { immediate: true, deep: true }
);

const isGroupByWorkspaceActive = computed(
  () => props.groupByWorkspace && props.hostWorkspaceEnabled && Array.isArray(props.workspaces) && props.workspaces.length > 0
);

/* 注意：本 watcher 声明必须放在 isGroupByWorkspaceActive 之后——
   immediate 回调会在 setup 阶段同步执行，提前引用会触发 TDZ 报错。 */
watch(
  () => props.currentWorkspaceId,
  async () => {
    /* 切换工作区会触发 bumpCurrentWorkspaceToTop 重排分组：分组头是瞬时
       移动的，而 transition-group 会对子对话做 FLIP 位移动画，节奏不一致
       很诡异。重排期间挂上 switch-instant 让两者都瞬间完成。 */
    if (isGroupByWorkspaceActive.value) {
      workspaceSwitchInstant.value = true;
      bumpCurrentWorkspaceToTop();
      await nextTick();
      workspaceSwitchInstant.value = false;
    } else {
      bumpCurrentWorkspaceToTop();
    }
  },
  { immediate: true }
);
const sortedWorkspaces = computed(() => {
  const list = Array.isArray(props.workspaces) ? [...props.workspaces] : [];
  return list.sort((a: any, b: any) => {
    const aId = String(a?.workspace_id || '');
    const bId = String(b?.workspace_id || '');
    const aPinned = pinnedWorkspaceIds.value.has(aId) ? 1 : 0;
    const bPinned = pinnedWorkspaceIds.value.has(bId) ? 1 : 0;
    if (aPinned !== bPinned) return bPinned - aPinned;
    const aIndex = workspaceOrder.value.indexOf(aId);
    const bIndex = workspaceOrder.value.indexOf(bId);
    if (aIndex !== -1 && bIndex !== -1) return aIndex - bIndex;
    if (aIndex !== -1) return -1;
    if (bIndex !== -1) return 1;
    return String(a?.label || aId).localeCompare(String(b?.label || bId));
  });
});

const displayConversations = computed(() =>
  searchActive.value ? searchResults.value : conversations.value
);

/* 跨工作区搜索结果：按与工作区分组列表一致的顺序排列（置顶 > 自定义顺序 > 名称） */
const sortedSearchGroups = computed(() => {
  const groups = Array.isArray(searchGroups.value) ? [...searchGroups.value] : [];
  return groups.sort((a: any, b: any) => {
    const aId = String(a?.workspace_id || '');
    const bId = String(b?.workspace_id || '');
    const aPinned = pinnedWorkspaceIds.value.has(aId) ? 1 : 0;
    const bPinned = pinnedWorkspaceIds.value.has(bId) ? 1 : 0;
    if (aPinned !== bPinned) return bPinned - aPinned;
    const aIndex = workspaceOrder.value.indexOf(aId);
    const bIndex = workspaceOrder.value.indexOf(bId);
    if (aIndex !== -1 && bIndex !== -1) return aIndex - bIndex;
    if (aIndex !== -1) return -1;
    if (bIndex !== -1) return 1;
    return String(a?.label || aId).localeCompare(String(b?.label || bId));
  });
});
const loading = computed(() =>
  searchActive.value ? searchInProgress.value : conversationsLoading.value
);
const isDeletingConversation = computed(() => conversationListAnimationMode.value === 'delete');
const runningTaskItems = computed(() =>
  Array.isArray(props.runningTasks)
    ? props.runningTasks.filter(
        (task) =>
          task &&
          task.task_id &&
          task.conversation_id &&
          task.conversation_id !== currentConversationId.value &&
          String(task.workspace_id || '') !== String(props.currentWorkspaceId || '')
      )
    : []
);
const activeStatuses = new Set(['pending', 'running', 'cancel_requested']);
const terminalStatuses = new Set(['succeeded', 'failed', 'canceled']);
const isTaskActive = (task: any) => activeStatuses.has(String(task?.status || ''));
const isTaskCompleted = (task: any) => terminalStatuses.has(String(task?.status || ''));
const activeConversationIds = computed(
  () =>
    new Set(
      (Array.isArray(props.runningTasks) ? props.runningTasks : [])
        .filter((task) => isTaskActive(task))
        .map((task) => String(task?.conversation_id || ''))
        .filter(Boolean)
    )
);
const completedConversationIds = computed(
  () =>
    new Set(
      (Array.isArray(props.runningTasks) ? props.runningTasks : [])
        .filter((task) => isTaskCompleted(task))
        .map((task) => String(task?.conversation_id || ''))
        .filter(Boolean)
    )
);
const isConversationActive = (conversationId: string) => {
  if (activeConversationIds.value.has(String(conversationId || ''))) return true;
  // 后端 runningTasks 库只跟踪跨工作的任务（当前会话排除了），
  // 所以当前会话的运行态要单独用 currentTaskInProgress 标记。
  if (props.currentTaskInProgress && String(conversationId || '') === String(props.currentConversationId || '')) {
    return true;
  }
  return false;
};
const isConversationCompleted = (conversationId: string) =>
  completedConversationIds.value.has(String(conversationId || ''));
const isWorkspaceRunning = (workspaceId: string) =>
  (Array.isArray(props.runningTasks) ? props.runningTasks : []).some(
    (task) => isTaskActive(task) && String(task?.workspace_id || '') === workspaceId
  );

const getWorkspaceGroup = (workspaceId: string) => {
  return conversationStore.workspaceGroups.find((g) => g.workspaceId === workspaceId);
};

const getWorkspaceVisibleAndBufferConversations = (workspaceId: string) => {
  const group = getWorkspaceGroup(workspaceId);
  if (!group) return [];
  const end = group.visibleLimit + group.bufferLimit;
  return group.conversations.slice(0, end);
};

const getWorkspaceHasMore = (workspaceId: string) => {
  const group = getWorkspaceGroup(workspaceId);
  if (!group) return false;
  return group.hasMore || group.conversations.length > group.visibleLimit;
};

const isWorkspaceExpanded = (workspaceId: string) => {
  const group = getWorkspaceGroup(workspaceId);
  return group ? group.expanded : true;
};

const toggleWorkspaceExpanded = (workspaceId: string) => {
  const group = getWorkspaceGroup(workspaceId);
  if (!group) {
    conversationStore.ensureWorkspaceGroup(workspaceId);
    void conversationStore.loadWorkspaceConversations(workspaceId);
    return;
  }
  conversationStore.setWorkspaceGroupExpanded(workspaceId, !group.expanded);
  if (!group.expanded && group.conversations.length === 0 && !group.loading) {
    void conversationStore.loadWorkspaceConversations(workspaceId);
  }
};

const ensureWorkspaceGroup = (workspaceId: string) => {
  const group = getWorkspaceGroup(workspaceId);
  if (!group) {
    conversationStore.ensureWorkspaceGroup(workspaceId);
    void conversationStore.loadWorkspaceConversations(workspaceId);
  } else if (group.conversations.length === 0 && !group.loading && !group.loadingMore) {
    void conversationStore.loadWorkspaceConversations(workspaceId);
  }
};

watch(
  [isGroupByWorkspaceActive, sortedWorkspaces],
  ([active, list]) => {
    if (active && list.length > 0) {
      list.forEach((ws: any) => ensureWorkspaceGroup(String(ws?.workspace_id || '')));
    }
  },
  { immediate: true }
);

const handleWorkspaceConversationClick = (conversationId: string, workspaceId: string) => {
  openWorkspaceMenuId.value = null;
  emit('select-workspace-conversation', { conversationId, workspaceId });
};

const handleCreateWorkspaceConversation = (workspaceId: string) => {
  openWorkspaceMenuId.value = null;
  emit('create-workspace-conversation', workspaceId);
};

const toggleWorkspaceMenu = (workspaceId: string) => {
  openWorkspaceMenuId.value = openWorkspaceMenuId.value === workspaceId ? null : workspaceId;
};

const closeWorkspaceMenu = () => {
  openWorkspaceMenuId.value = null;
};

const handlePinWorkspace = (workspaceId: string) => {
  openWorkspaceMenuId.value = null;
  const order = [...workspaceOrder.value];
  const orderIndex = order.indexOf(workspaceId);
  if (pinnedWorkspaceIds.value.has(workspaceId)) {
    pinnedWorkspaceIds.value.delete(workspaceId);
    if (orderIndex !== -1) order.splice(orderIndex, 1);
    const currentId = String(props.currentWorkspaceId || '');
    const currentIndex = order.indexOf(currentId);
    const insertIndex = currentIndex !== -1 ? currentIndex + 1 : 0;
    order.splice(insertIndex, 0, workspaceId);
  } else {
    pinnedWorkspaceIds.value.add(workspaceId);
    if (orderIndex !== -1) order.splice(orderIndex, 1);
  }
  workspaceOrder.value = order;
  persistPinnedWorkspaces();
  persistWorkspaceOrder();
  emit('pin-workspace', workspaceId);
};

const handleRevealWorkspace = (workspaceId: string) => {
  openWorkspaceMenuId.value = null;
  emit('reveal-workspace', workspaceId);
};

const startRenameWorkspace = (workspace: any) => {
  openWorkspaceMenuId.value = null;
  renameWorkspaceId.value = String(workspace?.workspace_id || '');
  renameWorkspaceLabel.value = String(workspace?.label || workspace?.workspace_id || '');
};

const submitRenameWorkspace = () => {
  const workspaceId = renameWorkspaceId.value;
  const label = renameWorkspaceLabel.value.trim();
  if (workspaceId && label) {
    emit('rename-workspace', { workspaceId, label });
  }
  renameWorkspaceId.value = null;
  renameWorkspaceLabel.value = '';
};

const cancelRenameWorkspace = () => {
  renameWorkspaceId.value = null;
  renameWorkspaceLabel.value = '';
};

const loadMoreWorkspaceConversations = (workspaceId: string) => {
  void conversationStore.loadMoreWorkspaceConversations(workspaceId);
};

onMounted(() => {
  document.addEventListener('click', closeActionMenu);
  document.addEventListener('click', closeWorkspaceMenu);
  if (isGroupByWorkspaceActive.value) {
    sortedWorkspaces.value.forEach((ws: any) => ensureWorkspaceGroup(String(ws?.workspace_id || '')));
  }
});

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