﻿<template>
  <div class="space-y-8 relative">
    <!-- 全局加载遮罩 -->
    <Teleport to="body">
      <div
        v-if="isOperating"
        class="fixed inset-0 z-[200] flex items-center justify-center bg-background/80 backdrop-blur-sm"
      >
        <div class="flex flex-col items-center gap-4 rounded-2xl border border-border bg-card p-8 shadow-lg">
          <svg class="h-10 w-10 animate-spin text-primary" viewBox="0 0 24 24" fill="none">
            <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
            <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
          </svg>
          <div class="flex flex-col items-center gap-2">
            <p class="text-sm font-medium text-foreground">
              {{ batchProgress ? `处理中 ${batchProgress.current}/${batchProgress.total}` : '操作处理中...' }}
            </p>
            <div v-if="batchProgress" class="w-48 h-1.5 bg-muted rounded-full overflow-hidden">
              <div
                class="h-full bg-primary transition-all duration-300"
                :style="{ width: `${(batchProgress.current / batchProgress.total) * 100}%` }"
              ></div>
            </div>
          </div>
        </div>
      </div>
    </Teleport>

    <section class="rounded-3xl border border-border bg-card p-6">
      <div class="flex flex-wrap items-center justify-between gap-4">
        <div class="grid w-full grid-cols-2 gap-3 sm:flex sm:w-auto sm:items-center">
          <input
            v-model="searchQuery"
            type="text"
            placeholder="搜索账号 ID"
            class="w-full rounded-full border border-input bg-background px-4 py-2 text-sm sm:w-48"
          />
          <SelectMenu
            v-model="statusFilter"
            :options="statusOptions"
            class="!w-full sm:!w-40"
          />
        </div>
        <div class="flex w-full flex-wrap items-center gap-3 text-xs text-muted-foreground sm:w-auto sm:flex-nowrap">
          <Checkbox :modelValue="allSelected" @update:modelValue="toggleSelectAll">
            全选
          </Checkbox>
          <span>已选 {{ selectedCount }} / {{ filteredAccounts.length }} 个账号</span>
          <div class="ml-auto flex items-center gap-2 sm:ml-0">
            <button
              type="button"
              class="inline-flex h-8 w-8 items-center justify-center rounded-full border border-border text-muted-foreground transition-colors
                     hover:border-primary hover:text-primary"
              :class="viewMode === 'table' ? 'bg-accent text-accent-foreground' : ''"
              @click="viewMode = 'table'"
              aria-label="列表视图"
            >
              <svg aria-hidden="true" viewBox="0 0 24 24" class="h-4 w-4" fill="currentColor">
                <path d="M4 6h16v2H4V6zm0 5h16v2H4v-2zm0 5h16v2H4v-2z" />
              </svg>
            </button>
            <button
              type="button"
              class="inline-flex h-8 w-8 items-center justify-center rounded-full border border-border text-muted-foreground transition-colors
                     hover:border-primary hover:text-primary"
              :class="viewMode === 'card' ? 'bg-accent text-accent-foreground' : ''"
              @click="viewMode = 'card'"
              aria-label="卡片视图"
            >
              <svg aria-hidden="true" viewBox="0 0 24 24" class="h-4 w-4" fill="currentColor">
                <path d="M4 6h7v6H4V6zm9 0h7v6h-7V6zM4 14h7v4H4v-4zm9 0h7v4h-7v-4z" />
              </svg>
            </button>
          </div>
        </div>
      </div>

      <div class="mt-4 flex flex-wrap items-center gap-2">
        <button
          class="rounded-full border border-border px-4 py-2 text-sm font-medium text-foreground transition-colors
                 hover:border-primary hover:text-primary disabled:cursor-not-allowed disabled:opacity-50"
          :disabled="isLoading"
          @click="refreshAccounts"
        >
          刷新列表
        </button>
        <button
          class="rounded-full border border-border px-4 py-2 text-sm font-medium text-foreground transition-colors
                 hover:border-primary hover:text-primary"
          @click="openConfigPanel"
        >
          账户配置
        </button>
        <button
          class="rounded-full border border-border px-4 py-2 text-sm font-medium text-foreground transition-colors
                 hover:border-primary hover:text-primary disabled:cursor-not-allowed disabled:opacity-50"
          :disabled="isRegistering || isRefreshing"
          @click="openRegisterModal"
        >
          添加账户
        </button>

        <button
          class="relative rounded-full border border-border px-4 py-2 text-sm font-medium transition-colors
                 hover:border-primary hover:text-primary"
          @click="openTaskModal"
        >
          <span class="flex items-center gap-2">
            任务管理
            <template v-if="isTaskRunning">
              <span class="flex items-center gap-1.5 text-xs text-sky-500">
                <span class="relative flex h-2 w-2">
                  <span class="absolute inline-flex h-full w-full animate-ping rounded-full bg-sky-500 opacity-75"></span>
                  <span class="relative inline-flex h-2 w-2 rounded-full bg-sky-500"></span>
                </span>
                {{ taskProgressText }}
              </span>
            </template>
          </span>
        </button>

        <div ref="moreActionsRef" class="relative">
          <button
            class="flex items-center gap-2 rounded-full border border-input bg-background px-4 py-2 text-sm font-medium
                   text-foreground transition-colors hover:border-primary"
            :class="showMoreActions ? 'bg-accent text-accent-foreground' : ''"
            @click="toggleMoreActions"
          >
            更多操作
            <svg aria-hidden="true" viewBox="0 0 20 20" class="h-4 w-4" fill="currentColor">
              <path d="M5 7l5 6 5-6H5z" />
            </svg>
          </button>
          <div
            v-if="showMoreActions"
            class="absolute right-0 z-10 mt-2 w-full space-y-1 rounded-2xl border border-border bg-card p-2 shadow-lg"
          >
            <button
              type="button"
              class="flex w-full items-center justify-between rounded-xl px-3 py-2.5 text-left text-sm text-foreground transition-colors
                     hover:bg-accent"
              @click="triggerImportFile(); closeMoreActions()"
            >
              导入文件
            </button>
            <button
              type="button"
              class="flex w-full items-center justify-between rounded-xl px-3 py-2.5 text-left text-sm text-foreground transition-colors
                     hover:bg-accent"
              @click="openExportModal(); closeMoreActions()"
            >
              导出账户
            </button>
            <div class="my-1 border-t border-border/60"></div>
            <button
              type="button"
              class="flex w-full items-center justify-between rounded-xl px-3 py-2.5 text-left text-sm transition-colors"
              :class="isRegistering
                ? 'cursor-not-allowed text-muted-foreground'
                : 'text-foreground hover:bg-accent'"
              :disabled="isRegistering"
              @click="handleRefreshExpiring(); closeMoreActions()"
            >
              刷新过期
            </button>
            <button
              type="button"
              class="flex w-full items-center justify-between rounded-xl px-3 py-2.5 text-left text-sm transition-colors"
              :class="!selectedCount || isRegistering
                ? 'cursor-not-allowed text-muted-foreground'
                : 'text-foreground hover:bg-accent'"
              :disabled="!selectedCount || isRegistering"
              @click="handleRefreshSelected(); closeMoreActions()"
            >
              刷新选中
            </button>
            <div class="my-1 border-t border-border/60"></div>
            <button
              type="button"
              class="flex w-full items-center justify-between rounded-xl px-3 py-2.5 text-left text-sm transition-colors"
              :class="!selectedCount || isOperating
                ? 'cursor-not-allowed text-muted-foreground'
                : 'text-foreground hover:bg-accent'"
              :disabled="!selectedCount || isOperating"
              @click="handleBulkEnable(); closeMoreActions()"
            >
              <span v-if="isOperating" class="flex items-center gap-2">
                <svg class="h-4 w-4 animate-spin" viewBox="0 0 24 24" fill="none">
                  <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
                  <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
                </svg>
                处理中...
              </span>
              <span v-else>批量启用</span>
            </button>
            <button
              type="button"
              class="flex w-full items-center justify-between rounded-xl px-3 py-2.5 text-left text-sm transition-colors"
              :class="!selectedCount || isOperating
                ? 'cursor-not-allowed text-muted-foreground'
                : 'text-foreground hover:bg-accent'"
              :disabled="!selectedCount || isOperating"
              @click="handleBulkDisable(); closeMoreActions()"
            >
              <span v-if="isOperating" class="flex items-center gap-2">
                <svg class="h-4 w-4 animate-spin" viewBox="0 0 24 24" fill="none">
                  <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
                  <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
                </svg>
                处理中...
              </span>
              <span v-else>批量禁用</span>
            </button>
            <button
              type="button"
              class="flex w-full items-center justify-between rounded-xl px-3 py-2.5 text-left text-sm transition-colors"
              :class="!selectedCount || isOperating
                ? 'cursor-not-allowed text-muted-foreground'
                : 'text-destructive hover:bg-destructive/10'"
              :disabled="!selectedCount || isOperating"
              @click="handleBulkDelete(); closeMoreActions()"
            >
              <span v-if="isOperating" class="flex items-center gap-2">
                <svg class="h-4 w-4 animate-spin" viewBox="0 0 24 24" fill="none">
                  <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
                  <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
                </svg>
                处理中...
              </span>
              <span v-else>批量删除</span>
            </button>
          </div>
        </div>
      </div>

      <div v-if="viewMode === 'card'" class="mt-6 grid grid-cols-1 gap-3 md:grid-cols-2 xl:grid-cols-3">
        <div
          v-for="account in paginatedAccounts"
          :key="account.id"
          class="rounded-2xl border border-border bg-card p-4"
          :class="rowClass(account)"
          @click="toggleSelect(account.id)"
        >
          <div class="flex items-start justify-between gap-3">
            <div>
              <p class="text-xs text-muted-foreground">账号 ID</p>
              <p class="mt-1 font-mono text-xs text-foreground">{{ account.id }}</p>
            </div>
            <Checkbox
              :modelValue="selectedIds.has(account.id)"
              @update:modelValue="toggleSelect(account.id)"
              @click.stop
            />
          </div>

          <div class="mt-4 grid grid-cols-2 gap-3 text-xs text-muted-foreground">
            <div>
              <p>状态</p>
              <p class="mt-1 flex flex-wrap items-center gap-1.5 text-sm font-semibold text-foreground">
                <span
                  class="inline-flex items-center rounded-full border border-border px-2 py-0.5 text-xs"
                  :class="statusClass(account)"
                >
                  {{ statusLabel(account) }}
                </span>
                <span
                  v-if="account.trial_days_remaining != null"
                  class="inline-flex items-center gap-1 rounded-full border border-border px-2 py-0.5 text-xs font-medium"
                  :class="trialBadgeClass(account.trial_days_remaining)"
                >
                  <svg class="h-3 w-3 shrink-0" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
                    <path d="M5 1a1 1 0 0 1 1 1v.5h4V2a1 1 0 0 1 2 0v.5h1A1.5 1.5 0 0 1 14.5 4v9A1.5 1.5 0 0 1 13 14.5H3A1.5 1.5 0 0 1 1.5 13V4A1.5 1.5 0 0 1 3 2.5h1V2a1 1 0 0 1 1-1zm-2 4v1.5h10V5H3zm0 3v5h10V8H3z"/>
                  </svg>
                  {{ account.trial_days_remaining }}天
                </span>
              </p>
            </div>
            <div>
              <p>剩余时间</p>
              <p class="mt-1 text-sm font-semibold" :class="remainingClass(account)">
                {{ displayRemaining(account.remaining_display) }}
              </p>
              <p v-if="account.expires_at" class="mt-1 text-[11px]">
                {{ account.expires_at }}
              </p>
            </div>
            <div>
              <p>配额</p>
              <div class="mt-1">
                <QuotaBadge v-if="account.quota_status" :quota-status="account.quota_status" />
                <span v-else class="text-xs text-muted-foreground">-</span>
              </div>
            </div>
            <div>
              <p>失败数</p>
              <p class="mt-1 text-sm font-semibold text-foreground">{{ account.failure_count }}</p>
            </div>
            <div>
              <p>成功数</p>
              <p class="mt-1 text-sm font-semibold text-foreground">{{ account.conversation_count }}</p>
            </div>
          </div>

          <div class="mt-4 flex flex-wrap items-center gap-2">
            <button
              class="rounded-full border border-border px-3 py-1 text-xs text-foreground transition-colors
                     hover:border-primary hover:text-primary"
              @click.stop="openEdit(account.id)"
            >
              编辑
            </button>
            <button
              v-if="shouldShowEnable(account)"
              class="rounded-full border border-border px-3 py-1 text-xs text-foreground transition-colors
                     hover:border-primary hover:text-primary"
              @click.stop
              @click="handleEnable(account.id)"
            >
              启用
            </button>
            <button
              v-else
              class="rounded-full border border-border px-3 py-1 text-xs text-foreground transition-colors
                     hover:border-primary hover:text-primary"
              @click.stop
              @click="handleDisable(account.id)"
            >
              禁用
            </button>
            <button
              class="rounded-full border border-border px-3 py-1 text-xs text-destructive transition-colors
                     hover:border-destructive hover:text-destructive"
              @click.stop
              @click="handleDelete(account.id)"
            >
              删除
            </button>
          </div>
        </div>
        <div v-if="!filteredAccounts.length && !isLoading" class="rounded-2xl border border-border bg-background p-4 text-center text-xs text-muted-foreground">
          暂无账号数据，请检查后台配置。
        </div>
      </div>

      <div v-else class="relative mt-6 overflow-x-auto overflow-y-visible">
        <table class="min-w-full text-left text-sm">
          <thead class="text-xs uppercase tracking-[0.2em] text-muted-foreground">
            <tr>
              <th class="py-3 pr-4">
                <Checkbox :modelValue="allSelected" @update:modelValue="toggleSelectAll" />
              </th>
              <th class="py-3 pr-6">账号 ID</th>
              <th class="py-3 pr-6">状态</th>
              <th class="py-3 pr-6">
                <span class="inline-flex items-center gap-2">
                  剩余/过期
                  <HelpTip text="过期时间为 12 小时，账户过期以北京时间为准。" />
                </span>
              </th>
              <th class="py-3 pr-6">配额</th>
              <th class="py-3 pr-6">失败数</th>
              <th class="py-3 pr-6">成功数</th>
              <th class="py-3 text-right">操作</th>
            </tr>
          </thead>
          <tbody class="text-sm text-foreground">
            <tr v-if="!filteredAccounts.length && !isLoading">
              <td colspan="8" class="py-8 text-center text-muted-foreground">
                暂无账号数据，请检查后台配置。
              </td>
            </tr>
            <tr
              v-for="account in paginatedAccounts"
              :key="account.id"
              class="border-t border-border"
              :class="rowClass(account)"
              @click="toggleSelect(account.id)"
            >
              <td class="py-4 pr-4" @click.stop>
                <Checkbox
                  :modelValue="selectedIds.has(account.id)"
                  @update:modelValue="toggleSelect(account.id)"
                />
              </td>
              <td class="py-4 pr-6 font-mono text-xs text-foreground">
                {{ account.id }}
              </td>
              <td class="py-4 pr-6">
                <div class="flex flex-wrap items-center gap-1.5">
                  <span
                    class="inline-flex items-center rounded-full border border-border px-3 py-1 text-xs"
                    :class="statusClass(account)"
                  >
                    {{ statusLabel(account) }}
                  </span>
                  <span
                    v-if="account.trial_days_remaining != null"
                    class="inline-flex items-center gap-1 rounded-full border border-border px-2 py-1 text-xs font-medium"
                    :class="trialBadgeClass(account.trial_days_remaining)"
                  >
                    <svg class="h-3 w-3 shrink-0" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
                      <path d="M5 1a1 1 0 0 1 1 1v.5h4V2a1 1 0 0 1 2 0v.5h1A1.5 1.5 0 0 1 14.5 4v9A1.5 1.5 0 0 1 13 14.5H3A1.5 1.5 0 0 1 1.5 13V4A1.5 1.5 0 0 1 3 2.5h1V2a1 1 0 0 1 1-1zm-2 4v1.5h10V5H3zm0 3v5h10V8H3z"/>
                    </svg>
                    {{ account.trial_days_remaining }}天
                  </span>
                </div>
              </td>
              <td class="py-4 pr-6">
                <div class="text-sm font-semibold" :class="remainingClass(account)">
                  {{ displayRemaining(account.remaining_display) }}
                </div>
                <span v-if="account.expires_at" class="block text-[11px] text-muted-foreground">
                  {{ account.expires_at }}
                </span>
              </td>
              <td class="py-4 pr-6">
                <QuotaBadge v-if="account.quota_status" :quota-status="account.quota_status" />
                <span v-else class="text-xs text-muted-foreground">-</span>
              </td>
              <td class="py-4 pr-6 text-xs text-muted-foreground">
                {{ account.failure_count }}
              </td>
              <td class="py-4 pr-6 text-xs text-muted-foreground">
                {{ account.conversation_count }}
              </td>
              <td class="py-4 text-right">
                <div class="flex flex-wrap justify-end gap-2">
                  <button
                    class="rounded-full border border-border px-3 py-1 text-xs text-foreground transition-colors
                           hover:border-primary hover:text-primary"
                    @click.stop="openEdit(account.id)"
                  >
                    编辑
                  </button>
                  <button
                    v-if="shouldShowEnable(account)"
                    class="rounded-full border border-border px-3 py-1 text-xs text-foreground transition-colors
                           hover:border-primary hover:text-primary"
                    @click.stop="handleEnable(account.id)"
                  >
                    启用
                  </button>
                  <button
                    v-else
                    class="rounded-full border border-border px-3 py-1 text-xs text-foreground transition-colors
                           hover:border-primary hover:text-primary"
                    @click.stop="handleDisable(account.id)"
                  >
                    禁用
                  </button>
                  <button
                    class="rounded-full border border-border px-3 py-1 text-xs text-destructive transition-colors
                           hover:border-destructive hover:text-destructive"
                    @click.stop="handleDelete(account.id)"
                  >
                    删除
                  </button>
                </div>
              </td>
            </tr>
          </tbody>
        </table>
      </div>

      <!-- Pagination Controls -->
      <div v-if="filteredAccounts.length > pageSize" class="mt-6 flex items-center justify-between">
        <div class="text-sm text-muted-foreground">
          显示 {{ (currentPage - 1) * pageSize + 1 }}-{{ Math.min(currentPage * pageSize, filteredAccounts.length) }} / 共 {{ filteredAccounts.length }} 个账户
        </div>
        <div class="flex items-center gap-2">
          <button
            class="rounded-full border border-border px-4 py-2 text-sm transition-colors hover:border-primary hover:text-primary disabled:cursor-not-allowed disabled:opacity-50"
            :disabled="currentPage === 1"
            @click="currentPage--"
          >
            上一页
          </button>
          <span class="text-sm text-muted-foreground">{{ currentPage }} / {{ totalPages }}</span>
          <button
            class="rounded-full border border-border px-4 py-2 text-sm transition-colors hover:border-primary hover:text-primary disabled:cursor-not-allowed disabled:opacity-50"
            :disabled="currentPage === totalPages"
            @click="currentPage++"
          >
            下一页
          </button>
        </div>
      </div>
    </section>
  </div>
  <Teleport to="body">
    <div v-if="isRegisterOpen" class="fixed inset-0 z-[100] flex items-center justify-center bg-black/30 px-4">
      <div class="flex max-h-[90vh] w-full max-w-lg flex-col overflow-hidden rounded-3xl border border-border bg-card shadow-xl">
        <div class="flex items-center justify-between border-b border-border/60 px-6 py-4">
          <div>
            <p class="text-sm font-medium text-foreground">添加账户</p>
            <p class="mt-1 text-xs text-muted-foreground">
              {{ addMode === 'register' ? '创建临时邮箱账号并自动注册' : '批量导入账户配置' }}
            </p>
          </div>
          <button
            class="text-xs text-muted-foreground transition-colors hover:text-foreground"
            @click="closeRegisterModal"
          >
            关闭
          </button>
        </div>

        <div class="scrollbar-slim flex-1 overflow-y-auto px-6 py-4">
          <div class="space-y-4 text-sm">
          <div class="flex rounded-full border border-border bg-muted/30 p-1 text-xs">
            <button
              type="button"
              class="flex-1 rounded-full px-3 py-2 font-medium transition-colors"
              :class="addMode === 'register' ? 'bg-foreground text-background' : 'text-muted-foreground hover:text-foreground'"
              @click="addMode = 'register'"
            >
              自动注册
            </button>
            <button
              type="button"
              class="flex-1 rounded-full px-3 py-2 font-medium transition-colors"
              :class="addMode === 'import' ? 'bg-foreground text-background' : 'text-muted-foreground hover:text-foreground'"
              @click="addMode = 'import'"
            >
              批量导入
            </button>
          </div>

          <div v-if="addMode === 'register'" class="space-y-4">
            <label class="block text-xs text-muted-foreground">临时邮箱服务</label>
            <SelectMenu
              v-model="selectedMailProvider"
              :options="mailProviderOptions"
              class="w-full"
            />
            <label class="block text-xs text-muted-foreground">注册数量</label>
            <input
              v-model.number="registerCount"
              type="number"
              min="1"
              class="w-full rounded-2xl border border-input bg-background px-3 py-2 text-sm"
            />
            <p class="text-xs text-muted-foreground">
              注册前请确认邮箱已配置，<a href="https://github.com/Dreamy-rain/gemini-business2api?tab=readme-ov-file#-%E9%82%AE%E7%AE%B1%E6%8F%90%E4%BE%9B%E5%95%86%E9%85%8D%E7%BD%AE" target="_blank" class="text-primary hover:underline font-medium">查看邮箱配置文档</a>
            </p>
            <p class="text-xs text-muted-foreground">
              遇到注册失败、收不到验证码或刷新异常？<a href="https://github.com/Dreamy-rain/gemini-business2api/issues/46" target="_blank" class="text-primary hover:underline font-medium">查看常见问题与解决方案</a>
            </p>
          </div>

          <div v-else class="space-y-4">
            <label class="block text-xs text-muted-foreground">批量导入（每行一个）</label>
            <div class="flex items-center gap-2">
              <button
                type="button"
                class="rounded-full border border-border px-3 py-1 text-xs text-muted-foreground transition-colors
                       hover:border-primary hover:text-primary"
                @click="triggerImportFile"
              >
                上传文件
              </button>
              <span v-if="importFileName" class="text-xs text-muted-foreground">{{ importFileName }}</span>
            </div>
            <textarea
              v-model="importText"
              class="min-h-[140px] w-full rounded-2xl border border-input bg-background px-3 py-2 text-xs font-mono"
              placeholder="duckmail----you@example.com----password&#10;moemail----you@moemail.app----emailId&#10;freemail----you@freemail.local&#10;gptmail----you@example.com&#10;cfmail----you@example.com----jwtToken&#10;user@outlook.com----loginPassword----clientId----refreshToken"
            ></textarea>
            <div class="rounded-2xl border border-border bg-muted/30 px-3 py-2 text-xs text-muted-foreground">
              <p>支持三种格式：</p>
              <p class="mt-1 font-mono">duckmail----email----password</p>
              <p class="mt-1 font-mono">moemail----email----emailId</p>
              <p class="mt-1 font-mono">freemail----email</p>
              <p class="mt-1 font-mono">gptmail----email</p>
              <p class="mt-1 font-mono">cfmail----email----jwtToken</p>
              <p class="mt-1 font-mono">email----password----clientId----refreshToken</p>
              <p class="mt-2">导入后请执行一次"刷新选中"以获取 Cookie。</p>
              <p class="mt-1">注册失败建议关闭无头浏览器再试</p>
            </div>
            <div v-if="importError" class="rounded-2xl border border-rose-200 bg-rose-50 px-3 py-2 text-xs text-rose-600">
              {{ importError }}
            </div>
          </div>

          <div class="rounded-2xl border border-rose-200 bg-rose-50 px-3 py-2 text-[11px] leading-relaxed">
            <p class="flex items-center gap-1.5 text-xs font-bold text-rose-600">
              <svg class="h-4 w-4 flex-shrink-0" viewBox="0 0 20 20" fill="currentColor">
                <path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd" />
              </svg>
              严禁滥用：禁止将本工具用于商业用途或任何形式的滥用（无论规模大小）
            </p>
            <p class="mt-1 text-muted-foreground">详细声明请查看项目 <a href="https://github.com/Dreamy-rain/gemini-business2api/blob/main/docs/DISCLAIMER.md" target="_blank" class="text-primary hover:underline font-medium">DISCLAIMER.md</a></p>
          </div>
          </div>
        </div>

        <div class="border-t border-border/60 px-6 py-4">
          <div class="flex items-center justify-end gap-2">
            <button
              class="rounded-full border border-border px-4 py-2 text-sm text-muted-foreground transition-colors
                     hover:border-primary hover:text-primary"
              @click="closeRegisterModal"
            >
              取消
            </button>
            <button
              v-if="addMode === 'register'"
              class="rounded-full bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-opacity
                     hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50"
              :disabled="isRegistering"
              @click="handleRegister"
            >
              开始注册
            </button>
            <button
              v-else
              class="rounded-full bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-opacity
                     hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50"
              :disabled="isImporting"
              @click="handleImport"
            >
              导入并保存
            </button>
          </div>
        </div>
      </div>
    </div>
  </Teleport>

  <Teleport to="body">
    <div v-if="isTaskOpen" class="fixed inset-0 z-[100] flex items-center justify-center bg-black/30 px-4">
      <div class="flex h-[80vh] w-full max-w-2xl flex-col overflow-hidden rounded-3xl border border-border bg-card shadow-xl">
        <div class="flex items-center justify-between border-b border-border/60 px-6 py-4">
          <div>
            <p class="text-sm font-medium text-foreground">任务管理</p>
            <p class="mt-1 text-xs text-muted-foreground">管理注册与刷新任务</p>
          </div>
          <button
            class="text-xs text-muted-foreground transition-colors hover:text-foreground"
            @click="closeTaskModal"
          >
            关闭
          </button>
        </div>

        <!-- Tab 导航 -->
        <div class="flex border-b border-border/60 px-6">
          <button
            type="button"
            class="relative px-4 py-3 text-sm font-medium transition-colors"
            :class="activeTaskTab === 'current' ? 'text-primary' : 'text-muted-foreground hover:text-foreground'"
            @click="activeTaskTab = 'current'"
          >
            当前任务
            <span
              v-if="activeTaskTab === 'current'"
              class="absolute bottom-0 left-0 right-0 h-0.5 bg-primary"
            ></span>
          </button>
          <button
            type="button"
            class="relative px-4 py-3 text-sm font-medium transition-colors"
            :class="activeTaskTab === 'scheduled' ? 'text-primary' : 'text-muted-foreground hover:text-foreground'"
            @click="activeTaskTab = 'scheduled'; loadScheduledConfig()"
          >
            定时任务
            <span
              v-if="activeTaskTab === 'scheduled'"
              class="absolute bottom-0 left-0 right-0 h-0.5 bg-primary"
            ></span>
          </button>
          <button
            type="button"
            class="relative px-4 py-3 text-sm font-medium transition-colors"
            :class="activeTaskTab === 'history' ? 'text-primary' : 'text-muted-foreground hover:text-foreground'"
            @click="activeTaskTab = 'history'"
          >
            历史记录
            <span
              v-if="activeTaskTab === 'history'"
              class="absolute bottom-0 left-0 right-0 h-0.5 bg-primary"
            ></span>
          </button>
        </div>

        <!-- 当前任务 Tab -->
        <div v-if="activeTaskTab === 'current'" class="flex min-h-0 flex-1 flex-col">
          <!-- 固定任务信息区域 -->
          <div v-if="automationError || registerTask || loginTask" class="px-6 py-4">
            <div v-if="automationError" class="rounded-2xl bg-destructive/10 px-3 py-2 text-xs text-destructive">
              {{ automationError }}
            </div>

            <div v-if="registerTask || loginTask" class="grid gap-3 text-xs text-muted-foreground">
            <div v-if="registerTask" class="space-y-1">
              <div class="flex items-center justify-between gap-3 font-medium text-foreground">
                <div class="flex items-center gap-2">
                  <span
                    class="h-2.5 w-2.5 rounded-full"
                    :class="getTaskStatusIndicatorClass(registerTask)"
                    aria-hidden="true"
                  ></span>
                  注册任务
                </div>
                <button
                  v-if="registerTask.status === 'running' || registerTask.status === 'pending'"
                  class="rounded-full border border-border px-3 py-1 text-xs text-muted-foreground transition-colors hover:border-rose-500 hover:text-rose-600"
                  @click="cancelRegister(registerTask.id)"
                >
                  中断
                </button>
              </div>
              <div class="flex flex-wrap gap-x-4 gap-y-1">
                <span>状态：{{ formatTaskStatus(registerTask) }}</span>
                <span>进度：{{ registerTask.progress }}/{{ registerTask.count }}</span>
                <span>成功：{{ registerTask.success_count }}</span>
                <span>失败：{{ registerTask.fail_count }}</span>
              </div>
            </div>
            <div v-if="loginTask" class="space-y-1">
              <div class="flex items-center justify-between gap-3 font-medium text-foreground">
                <div class="flex items-center gap-2">
                  <span
                    class="h-2.5 w-2.5 rounded-full"
                    :class="getTaskStatusIndicatorClass(loginTask)"
                    aria-hidden="true"
                  ></span>
                  刷新任务
                </div>
                <button
                  v-if="loginTask.status === 'running' || loginTask.status === 'pending'"
                  class="rounded-full border border-border px-3 py-1 text-xs text-muted-foreground transition-colors hover:border-rose-500 hover:text-rose-600"
                  @click="cancelLogin(loginTask.id)"
                >
                  中断
                </button>
              </div>
              <div class="flex flex-wrap gap-x-4 gap-y-1">
                <span>状态：{{ formatTaskStatus(loginTask) }}</span>
                <span>进度：{{ loginTask.progress }}/{{ loginTask.account_ids.length }}</span>
                <span>成功：{{ loginTask.success_count }}</span>
                <span>失败：{{ loginTask.fail_count }}</span>
              </div>
            </div>
          </div>
          </div>

          <!-- 日志区域（独立滚动） -->
          <div
            v-if="registerTask || loginTask || registerLogs.length || loginLogs.length"
            class="flex min-h-0 flex-1 flex-col px-6 pb-4"
          >
            <div
              ref="taskLogsRef"
              class="scrollbar-slim flex-1 overflow-y-auto rounded-2xl border border-border bg-muted/30 p-3"
            >
              <div v-if="registerLogs.length" class="space-y-2">
                <p class="text-xs font-semibold text-foreground">注册日志</p>
                <div class="space-y-1 text-[11px] text-muted-foreground">
                  <div v-for="(log, index) in registerLogs" :key="`reg-${index}`" class="font-mono">
                    {{ formatLogLine(log) }}
                  </div>
                </div>
              </div>
              <div v-if="loginLogs.length" class="mt-4 space-y-2">
                <p class="text-xs font-semibold text-foreground">刷新日志</p>
                <div class="space-y-1 text-[11px] text-muted-foreground">
                  <div v-for="(log, index) in loginLogs" :key="`login-${index}`" class="font-mono">
                    {{ formatLogLine(log) }}
                  </div>
                </div>
              </div>
              <div v-if="!registerLogs.length && !loginLogs.length" class="text-xs text-muted-foreground">
                日志已清空，新的日志会继续显示。
              </div>
            </div>
          </div>

          <div
            v-if="!automationError && !registerTask && !loginTask && !registerLogs.length && !loginLogs.length"
            class="flex-1 px-6 py-4"
          >
            <div class="rounded-2xl border border-border bg-muted/30 px-3 py-2 text-xs text-muted-foreground">
              暂无任务
            </div>
          </div>

          <!-- 固定底部按钮区域 -->
          <div class="flex items-center justify-end gap-2 border-t border-border/60 px-6 py-4">
            <button
              class="rounded-full border border-border px-4 py-2 text-sm text-muted-foreground transition-colors
                     hover:border-primary hover:text-primary disabled:cursor-not-allowed disabled:opacity-50"
              :disabled="!registerLogs.length && !loginLogs.length && !registerTask && !loginTask && !automationError"
              @click="clearTaskLogs"
            >
              清空日志
            </button>
          </div>
        </div>

        <!-- 定时任务 Tab -->
        <div v-if="activeTaskTab === 'scheduled'" class="flex min-h-0 flex-1 flex-col">
          <div class="flex min-h-0 flex-1 flex-col">
              <div class="flex-1 overflow-y-auto px-6 py-4">
                <div class="space-y-4">
                  <div class="space-y-3">
                    <div class="flex items-center justify-between">
                      <div>
                        <p class="text-sm font-medium text-foreground">启用定时刷新</p>
                        <p class="mt-1 text-xs text-muted-foreground">自动检测并分批刷新即将过期的账号</p>
                      </div>
                      <button
                        type="button"
                        class="relative inline-flex h-5 w-10 items-center rounded-full transition-colors"
                        :class="scheduledRefreshEnabled ? 'bg-primary' : 'bg-muted'"
                        @click="scheduledRefreshEnabled = !scheduledRefreshEnabled"
                      >
                        <span
                          class="inline-block h-4 w-4 transform rounded-full bg-white transition-transform"
                          :class="scheduledRefreshEnabled ? 'translate-x-5' : 'translate-x-1'"
                        ></span>
                      </button>
                    </div>

                    <div class="space-y-2">
                      <label class="block text-xs text-muted-foreground">刷新时间</label>
                      <input
                        v-model="scheduledRefreshCron"
                        type="text"
                        class="w-full rounded-2xl border border-input bg-background px-3 py-2 text-sm"
                        placeholder="08:00,20:00"
                      />
                      <p class="text-xs text-muted-foreground">
                        支持两种格式，任选其一：<br>
                        <b>① 每天定时</b>：填写时间点，如 <code>08:00,20:00</code> 表示每天 8 点和 20 点各刷新一次，多个时间用逗号分隔<br>
                        <b>② 固定间隔</b>：填写 <code>*/分钟数</code>，如 <code>*/120</code> 表示每隔 2 小时刷新一次
                      </p>
                    </div>

                    <div class="grid grid-cols-3 gap-3">
                      <div class="space-y-2">
                        <label class="block text-xs text-muted-foreground">每批数量</label>
                        <input
                          v-model.number="refreshBatchSize"
                          type="number"
                          min="1"
                          max="20"
                          class="w-full rounded-2xl border border-input bg-background px-3 py-2 text-sm"
                        />
                      </div>
                      <div class="space-y-2">
                        <label class="block text-xs text-muted-foreground">批次间隔(分)</label>
                        <input
                          v-model.number="refreshBatchInterval"
                          type="number"
                          min="5"
                          max="120"
                          class="w-full rounded-2xl border border-input bg-background px-3 py-2 text-sm"
                        />
                      </div>
                      <div class="space-y-2">
                        <label class="block text-xs text-muted-foreground">冷却时间(小时)</label>
                        <input
                          v-model.number="refreshCooldownHours"
                          type="number"
                          min="1"
                          max="48"
                          class="w-full rounded-2xl border border-input bg-background px-3 py-2 text-sm"
                        />
                      </div>
                    </div>

                    <div class="space-y-2">
                      <label class="block text-xs text-muted-foreground">过期刷新窗口（小时）</label>
                      <input
                        v-model.number="refreshWindowHours"
                        type="number"
                        min="1"
                        max="168"
                        class="w-full rounded-2xl border border-input bg-background px-3 py-2 text-sm"
                      />
                      <p class="text-xs text-muted-foreground">
                        当账号距离过期小于等于该值时，会触发自动刷新
                      </p>
                    </div>

                    <div class="rounded-2xl border border-border bg-muted/30 px-4 py-3 text-xs text-muted-foreground">
                      <p class="mb-2 font-medium text-foreground">说明</p>
                      <ul class="list-inside list-disc space-y-1">
                        <li>每批刷新指定数量的账号，等当前批完成后再开始下一批</li>
                        <li>同一账号刷新成功后，在冷却时间内不会被再次选中</li>
                        <li>修改配置后立即生效，无需重启服务</li>
                      </ul>
                    </div>
                  </div>
                </div>
              </div>

            <!-- 固定底部按钮区域 -->
            <div class="flex items-center justify-end gap-2 border-t border-border/60 px-6 py-4">
              <button
                class="rounded-full border border-border px-4 py-2 text-sm text-muted-foreground transition-colors
                       hover:border-primary hover:text-primary"
                @click="loadScheduledConfig"
              >
                重置
              </button>
              <button
                class="rounded-full bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-opacity
                       hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50"
                :disabled="isSavingScheduledConfig"
                @click="saveScheduledConfig"
              >
                保存配置
              </button>
            </div>
          </div>
        </div>

        <!-- 历史记录 Tab -->
        <div v-if="activeTaskTab === 'history'" class="flex min-h-0 flex-1 flex-col">
          <div class="flex-1 overflow-y-auto px-6 py-4">
            <div v-if="isLoadingHistory" class="flex items-center justify-center py-8">
              <svg class="h-6 w-6 animate-spin text-muted-foreground" viewBox="0 0 24 24" fill="none">
                <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
                <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
              </svg>
            </div>
            <div v-else-if="taskHistory.length === 0" class="rounded-2xl border border-border bg-muted/30 px-3 py-2 text-xs text-muted-foreground">
              <p class="font-medium text-foreground mb-2">暂无历史记录</p>
              <p>完成的任务将显示在这里</p>
            </div>
            <div v-else class="space-y-3">
              <div
                v-for="(record, index) in taskHistory"
                :key="index"
                class="rounded-2xl border border-border bg-card px-4 py-3 text-sm"
              >
                <div class="flex items-center justify-between mb-2">
                  <span class="flex items-center gap-2 font-medium text-foreground">
                    <span
                      class="h-2.5 w-2.5 rounded-full"
                      :class="getHistoryStatusIndicatorClass(record)"
                      aria-hidden="true"
                    ></span>
                    {{ record.type === 'login' ? '刷新任务' : '注册任务' }}
                  </span>
                  <span class="text-xs text-muted-foreground">
                    {{ new Date(record.created_at * 1000).toLocaleString('zh-CN') }}
                  </span>
                </div>
                <div class="text-xs text-muted-foreground space-y-1">
                  <div>
                    状态：<span :class="getHistoryStatusTextClass(record)">
                      {{ formatTaskStatus(record) }}
                    </span>
                  </div>
                  <div class="flex flex-wrap gap-x-4 gap-y-1">
                    <span>进度：{{ record.progress }}/{{ getHistoryTotal(record) }}</span>
                    <span>成功：{{ record.success_count }}</span>
                    <span>失败：{{ record.fail_count }}</span>
                  </div>
                </div>
              </div>
            </div>
          </div>

          <!-- 固定底部按钮区域 -->
          <div class="flex items-center justify-end gap-2 border-t border-border/60 px-6 py-4">
            <button
              class="rounded-full border border-border px-4 py-2 text-sm text-muted-foreground transition-colors
                     hover:border-primary hover:text-primary disabled:cursor-not-allowed disabled:opacity-50"
              :disabled="taskHistory.length === 0"
              @click="clearTaskHistory"
            >
              清空历史
            </button>
          </div>
        </div>
      </div>
    </div>
  </Teleport>
  <Teleport to="body">
    <div v-if="isEditOpen" class="fixed inset-0 z-[100] flex items-center justify-center bg-black/30 px-4">
      <div class="w-full max-w-lg rounded-3xl border border-border bg-card p-6 shadow-xl">
        <div class="flex items-center justify-between">
          <p class="text-sm font-medium text-foreground">编辑账号</p>
          <button
            class="text-xs text-muted-foreground transition-colors hover:text-foreground"
            @click="closeEdit"
          >
            关闭
          </button>
        </div>

        <div v-if="editError" class="mt-4 rounded-2xl bg-destructive/10 px-4 py-3 text-sm text-destructive">
          {{ editError }}
        </div>

        <div class="mt-4 space-y-3 text-sm">
          <label class="block text-xs text-muted-foreground">账号 ID</label>
          <input
            v-model="editForm.id"
            type="text"
            class="w-full rounded-2xl border border-input bg-background px-3 py-2 text-sm"
            disabled
          />

          <label class="block text-xs text-muted-foreground">secure_c_ses</label>
          <textarea
            v-model="editForm.secure_c_ses"
            class="w-full rounded-2xl border border-input bg-background px-3 py-2 text-sm"
            rows="3"
          ></textarea>

          <label class="block text-xs text-muted-foreground">csesidx</label>
          <input
            v-model="editForm.csesidx"
            type="text"
            class="w-full rounded-2xl border border-input bg-background px-3 py-2 text-sm"
          />

          <label class="block text-xs text-muted-foreground">config_id</label>
          <input
            v-model="editForm.config_id"
            type="text"
            class="w-full rounded-2xl border border-input bg-background px-3 py-2 text-sm"
          />

          <label class="block text-xs text-muted-foreground">host_c_oses</label>
          <input
            v-model="editForm.host_c_oses"
            type="text"
            class="w-full rounded-2xl border border-input bg-background px-3 py-2 text-sm"
          />

          <label class="block text-xs text-muted-foreground">expires_at</label>
          <input
            v-model="editForm.expires_at"
            type="text"
            class="w-full rounded-2xl border border-input bg-background px-3 py-2 text-sm"
            placeholder="2025-12-23 10:59:21"
          />
        </div>

        <div class="mt-6 flex items-center justify-end gap-2">
          <button
            class="rounded-full border border-border px-4 py-2 text-sm text-muted-foreground transition-colors
                   hover:border-primary hover:text-primary"
            @click="closeEdit"
          >
            取消
          </button>
          <button
            class="rounded-full bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-opacity
                   hover:opacity-90"
            @click="saveEdit"
          >
            保存
          </button>
        </div>
      </div>
    </div>
  </Teleport>

  <Teleport to="body">
    <div v-if="isConfigOpen" class="fixed inset-0 z-[100] flex items-center justify-center bg-black/30 px-4">
      <div class="w-full max-w-3xl rounded-3xl border border-border bg-card p-6 shadow-xl">
        <div class="flex items-center justify-between">
          <p class="text-sm font-medium text-foreground">账户配置（JSON）</p>
          <div class="flex items-center gap-2">
            <button
              class="rounded-full bg-foreground px-3 py-1 text-xs text-background transition-opacity
                     hover:opacity-90"
              @click="toggleConfigMask"
            >
              {{ configMasked ? '显示原文' : '脱敏显示' }}
            </button>
            <button
              class="text-xs text-muted-foreground transition-colors hover:text-foreground"
              @click="closeConfigPanel"
            >
              关闭
            </button>
          </div>
        </div>

        <div v-if="configError" class="mt-4 rounded-2xl bg-destructive/10 px-4 py-3 text-sm text-destructive">
          {{ configError }}
        </div>

        <div class="mt-4">
          <textarea
            v-model="configJson"
            class="h-96 w-full rounded-2xl border border-input bg-background px-4 py-3 font-mono text-xs text-foreground"
            spellcheck="false"
            :readonly="configMasked"
          ></textarea>
        </div>

        <div class="mt-6 flex items-center justify-end gap-2">
          <button
            class="rounded-full border border-border px-4 py-2 text-sm text-muted-foreground transition-colors
                   hover:border-primary hover:text-primary"
            @click="closeConfigPanel"
          >
            取消
          </button>
          <button
            class="rounded-full bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-opacity
                   hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50"
            @click="saveConfigPanel"
            :disabled="configMasked"
          >
            保存
          </button>
        </div>
      </div>
    </div>
  </Teleport>
  <Teleport to="body">
    <div v-if="isExportOpen" class="fixed inset-0 z-[100] flex items-center justify-center bg-black/30 px-4">
      <div class="flex max-h-[90vh] w-full max-w-md flex-col overflow-hidden rounded-3xl border border-border bg-card shadow-xl">
        <div class="flex items-center justify-between border-b border-border/60 px-6 py-4">
          <div>
            <p class="text-sm font-medium text-foreground">导出账号配置</p>
            <p class="mt-1 text-xs text-muted-foreground">选择导出范围与格式</p>
          </div>
          <button
            class="text-xs text-muted-foreground transition-colors hover:text-foreground"
            @click="closeExportModal"
          >
            关闭
          </button>
        </div>
        <div class="scrollbar-slim flex-1 overflow-y-auto px-6 py-4">
          <div class="space-y-4 text-sm">
            <div class="flex rounded-full border border-border bg-muted/30 p-1 text-xs">
              <button
                type="button"
                class="flex-1 rounded-full px-3 py-2 font-medium transition-colors"
                :class="exportScope === 'all' ? 'bg-foreground text-background' : 'text-muted-foreground hover:text-foreground'"
                @click="exportScope = 'all'"
              >
                全部
              </button>
              <button
                type="button"
                class="flex-1 rounded-full px-3 py-2 font-medium transition-colors"
                :class="exportScope === 'selected' ? 'bg-foreground text-background' : 'text-muted-foreground hover:text-foreground'"
                :disabled="!selectedCount"
                @click="exportScope = 'selected'"
              >
                选中
              </button>
            </div>

            <div class="flex rounded-full border border-border bg-muted/30 p-1 text-xs">
              <button
                type="button"
                class="flex-1 rounded-full px-3 py-2 font-medium transition-colors"
                :class="exportFormat === 'json' ? 'bg-foreground text-background' : 'text-muted-foreground hover:text-foreground'"
                @click="exportFormat = 'json'"
              >
                JSON
              </button>
              <button
                type="button"
                class="flex-1 rounded-full px-3 py-2 font-medium transition-colors"
                :class="exportFormat === 'txt' ? 'bg-foreground text-background' : 'text-muted-foreground hover:text-foreground'"
                @click="exportFormat = 'txt'"
              >
                TXT
              </button>
            </div>
            <p class="text-xs text-muted-foreground">
              选中导出仅包含当前已勾选账号（{{ selectedCount }} 个）。
            </p>
            <p class="text-xs text-muted-foreground">
              <template v-if="exportFormat === 'json'">
                JSON 格式包含完整数据（Cookie、Token、过期时间等），导入后无需重新刷新。
              </template>
              <template v-else>
                TXT 格式仅导出邮箱和密码，导入后需要重新刷新获取 Cookie。
              </template>
            </p>
          </div>
        </div>
        <div class="border-t border-border/60 px-6 py-4">
          <div class="flex items-center justify-end gap-2">
            <button
              class="rounded-full border border-border px-4 py-2 text-sm text-muted-foreground transition-colors
                     hover:border-primary hover:text-primary"
              @click="closeExportModal"
            >
              取消
            </button>
            <button
              class="rounded-full bg-primary px-4 py-2 text-sm font-medium text-primary-foreground transition-opacity
                     hover:opacity-90 disabled:cursor-not-allowed disabled:opacity-50"
              :disabled="exportScope === 'selected' && !selectedCount"
              @click="runExport"
            >
              开始导出
            </button>
          </div>
        </div>
      </div>
    </div>
  </Teleport>
  <input
    ref="importFileInput"
    type="file"
    class="hidden"
    accept=".txt,.json,application/json,text/plain"
    @change="handleImportFile"
  />
</template>

<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { storeToRefs } from 'pinia'
import { useAccountsStore } from '@/stores/accounts'
import { useSettingsStore } from '@/stores/settings'
import SelectMenu from '@/components/ui/SelectMenu.vue'
import Checkbox from '@/components/ui/Checkbox.vue'
import ConfirmDialog from '@/components/ui/ConfirmDialog.vue'
import QuotaBadge from '@/components/QuotaBadge.vue'
import { useConfirmDialog } from '@/composables/useConfirmDialog'
import { useToast } from '@/composables/useToast'
import HelpTip from '@/components/ui/HelpTip.vue'
import { accountsApi, settingsApi } from '@/api'
import { mailProviderOptions, defaultMailProvider } from '@/constants/mailProviders'
import type { AdminAccount, AccountConfigItem, RegisterTask, LoginTask } from '@/types/api'

const accountsStore = useAccountsStore()
const { accounts, isLoading, isOperating, batchProgress } = storeToRefs(accountsStore)
const settingsStore = useSettingsStore()
const { settings } = storeToRefs(settingsStore)
const confirmDialog = useConfirmDialog()
const toast = useToast()

const searchQuery = ref('')
const statusFilter = ref('all')
const selectedIds = ref<Set<string>>(new Set())
const viewMode = ref<'table' | 'card'>((localStorage.getItem('accounts_view_mode') as 'table' | 'card') || 'table')
watch(viewMode, (val) => localStorage.setItem('accounts_view_mode', val))
const currentPage = ref(1)
const pageSize = ref(50)
const isEditOpen = ref(false)
const editError = ref('')
const isConfigOpen = ref(false)
const configError = ref('')
const configJson = ref('')
const configMasked = ref(false)
const configData = ref<AccountConfigItem[]>([])
const registerCount = ref(1)
const selectedMailProvider = ref(settings.value?.basic?.temp_mail_provider || defaultMailProvider)
const isRegisterOpen = ref(false)
const addMode = ref<'register' | 'import'>('register')
const importText = ref('')
const importError = ref('')
const isImporting = ref(false)
const importFileInput = ref<HTMLInputElement | null>(null)
const importFileName = ref('')
const isExportOpen = ref(false)
const exportScope = ref<'all' | 'selected'>('all')
const exportFormat = ref<'json' | 'txt'>('json')
const isTaskOpen = ref(false)
const activeTaskTab = ref<'current' | 'scheduled' | 'history'>('current')
const showMoreActions = ref(false)
const moreActionsRef = ref<HTMLDivElement | null>(null)
const lastRegisterTaskId = ref<string | null>(null)
const lastLoginTaskId = ref<string | null>(null)
const scheduledRefreshEnabled = ref(false)
const scheduledRefreshCron = ref('08:00,20:00')
const refreshBatchSize = ref(5)
const refreshBatchInterval = ref(30)
const refreshCooldownHours = ref(12)
const refreshWindowHours = ref(24)
const isLoadingScheduledConfig = ref(false)
const isSavingScheduledConfig = ref(false)
const cachedSettings = ref<any>(null)  // 缓存配置以避免重复API调用
const taskHistory = ref<any[]>([])  // 任务历史记录
const isLoadingHistory = ref(false)  // 加载历史记录状态
type TaskLogLine = { time: string; level: string; message: string }
const registerLogClearMarker = ref<TaskLogLine | null>(null)
const loginLogClearMarker = ref<TaskLogLine | null>(null)
const registerTask = ref<RegisterTask | null>(null)
const loginTask = ref<LoginTask | null>(null)
const refreshingAccountIds = ref<Set<string>>(new Set())  // 正在刷新的账户ID集合（仅用于显示状态）
const taskLogsRef = ref<HTMLDivElement | null>(null)
const isRegistering = ref(false)
const isRefreshing = ref(false)
const automationError = ref('')
const REGISTER_TASK_CACHE_KEY = 'accounts-register-task-cache'
const LOGIN_TASK_CACHE_KEY = 'accounts-login-task-cache'
const REGISTER_CLEAR_KEY = 'accounts-register-log-clear'
const LOGIN_CLEAR_KEY = 'accounts-login-log-clear'
const REGISTER_DISMISS_KEY = 'accounts-register-task-dismissed'
const LOGIN_DISMISS_KEY = 'accounts-login-task-dismissed'
const REGISTER_CLEARED_KEY = 'accounts-register-task-cleared'
const LOGIN_CLEARED_KEY = 'accounts-login-task-cleared'

type TaskKind = 'register' | 'login'
const TASK_KEYS = {
  register: {
    clearKey: REGISTER_CLEAR_KEY,
    dismissKey: REGISTER_DISMISS_KEY,
    clearedKey: REGISTER_CLEARED_KEY,
  },
  login: {
    clearKey: LOGIN_CLEAR_KEY,
    dismissKey: LOGIN_DISMISS_KEY,
    clearedKey: LOGIN_CLEARED_KEY,
  },
} as const
const editForm = ref<AccountConfigItem>({
  id: '',
  secure_c_ses: '',
  csesidx: '',
  config_id: '',
  host_c_oses: '',
  expires_at: '',
})
const editIndex = ref<number | null>(null)
const configAccounts = ref<AccountConfigItem[]>([])
const statusOptions = [
  { label: '全部状态', value: 'all' },
  { label: '正常', value: '正常' },
  { label: '即将过期', value: '即将过期' },
  { label: '已过期', value: '已过期' },
  { label: '手动禁用', value: '手动禁用' },
  { label: '403 禁用', value: '403 禁用' },
  { label: '429限流', value: '429限流' },
]

const filteredAccounts = computed(() => {
  const query = searchQuery.value.trim().toLowerCase()
  return accounts.value.filter(account => {
    const matchesQuery = !query || account.id.toLowerCase().includes(query)
    const matchesStatus = statusFilter.value === 'all' || statusLabel(account) === statusFilter.value
    return matchesQuery && matchesStatus
  })
})

const totalPages = computed(() => Math.ceil(filteredAccounts.value.length / pageSize.value))

const paginatedAccounts = computed(() => {
  const start = (currentPage.value - 1) * pageSize.value
  const end = start + pageSize.value
  return filteredAccounts.value.slice(start, end)
})

const selectedCount = computed(() => selectedIds.value.size)
const allSelected = computed(() =>
  filteredAccounts.value.length > 0 && filteredAccounts.value.every(account => selectedIds.value.has(account.id))
)

watch([searchQuery, statusFilter], () => {
  currentPage.value = 1
})

const refreshAccounts = async () => {
  await accountsStore.loadAccounts()
  selectedIds.value = new Set()
  showMoreActions.value = false
}

const readCachedTask = <T,>(key: string): T | null => {
  try {
    const raw = localStorage.getItem(key)
    return raw ? (JSON.parse(raw) as T) : null
  } catch {
    return null
  }
}

const writeCachedTask = (key: string, value: unknown) => {
  try {
    localStorage.setItem(key, JSON.stringify(value))
  } catch {
    // ignore storage errors
  }
}

const removeCachedTask = (key: string) => {
  try {
    localStorage.removeItem(key)
  } catch {
    // ignore storage errors
  }
}

type DismissedTaskMeta = { id?: string; created_at?: number } | null

const readDismissedTaskMeta = (key: string): DismissedTaskMeta => {
  try {
    const raw = localStorage.getItem(key)
    if (!raw) return null
    try {
      const parsed = JSON.parse(raw) as Partial<{ id: string; created_at: number }>
      if (parsed && (parsed.id || typeof parsed.created_at === 'number')) {
        return { id: parsed.id, created_at: parsed.created_at }
      }
    } catch {
      // Backward compatibility: plain id string
      return { id: raw }
    }
    return null
  } catch {
    return null
  }
}

const writeDismissedTaskMeta = (key: string, meta: DismissedTaskMeta) => {
  try {
    if (!meta || (!meta.id && typeof meta.created_at !== 'number')) {
      localStorage.removeItem(key)
      return
    }
    localStorage.setItem(key, JSON.stringify(meta))
  } catch {
    // ignore storage errors
  }
}

const readDismissedTaskId = (key: string) => readDismissedTaskMeta(key)?.id || null

const writeDismissedTaskId = (key: string, taskId: string | null) => {
  if (!taskId) {
    writeDismissedTaskMeta(key, null)
    return
  }
  writeDismissedTaskMeta(key, { id: taskId })
}

const isTaskMetaMatch = (task: { id?: string; created_at?: number } | null | undefined, meta: DismissedTaskMeta) => {
  if (!task || !meta) return false
  if (meta.id && task.id && task.id === meta.id) return true
  if (typeof meta.created_at === 'number' && typeof task.created_at === 'number' && task.created_at === meta.created_at) {
    return true
  }
  return false
}

const isTaskDismissed = (task: { id?: string; created_at?: number } | null | undefined, meta: DismissedTaskMeta) =>
  isTaskMetaMatch(task, meta)

const readClearedTaskMeta = (key: string): DismissedTaskMeta => readDismissedTaskMeta(key)
const writeClearedTaskMeta = (key: string, meta: DismissedTaskMeta) => writeDismissedTaskMeta(key, meta)

const isTaskActive = (task: RegisterTask | LoginTask | null | undefined) => {
  const status = task?.status
  return status === 'running' || status === 'pending'
}

const getTaskByKind = (kind: TaskKind) => (kind === 'register' ? registerTask.value : loginTask.value)

const markTaskCleared = (kind: TaskKind, task: RegisterTask | LoginTask) => {
  const key = TASK_KEYS[kind].clearedKey
  writeClearedTaskMeta(key, {
    id: task.id,
    created_at: task.created_at,
  })
}

const setLogClearMarker = (kind: TaskKind, marker: TaskLogLine | null) => {
  if (kind === 'register') {
    registerLogClearMarker.value = marker
  } else {
    loginLogClearMarker.value = marker
  }
}

const clearTaskSnapshot = (kind: TaskKind, persist = true) => {
  if (kind === 'register') {
    syncRegisterTask(null, persist)
  } else {
    syncLoginTask(null, persist)
  }
}

const clearFinishedTask = (kind: TaskKind) => {
  const task = getTaskByKind(kind)
  if (!task || isTaskActive(task)) return
  markTaskCleared(kind, task)
  clearTaskSnapshot(kind, true)
}

const handleTaskIdle = (kind: TaskKind) => {
  // 后端 idle：保留现有任务快照
  cleanupCancelledTasks()
}

const handleTaskNotFound = (kind: TaskKind) => {
  if (kind === 'register') {
    clearRegisterTimer()
    isRegistering.value = false
  } else {
    clearLoginTimer()
    isRefreshing.value = false
  }
}

const handleTaskActive = (kind: TaskKind, task: RegisterTask | LoginTask) => {
  if (kind === 'register') {
    syncRegisterTask(task)
    isRegistering.value = true
    startRegisterPolling(task.id)
  } else {
    syncLoginTask(task)
    isRefreshing.value = true
    startLoginPolling(task.id)
  }
}

const handleTaskInactive = (kind: TaskKind, task: RegisterTask | LoginTask) => {
  if (kind === 'register') {
    syncRegisterTask(task)
  } else {
    syncLoginTask(task)
  }
}

const shouldKeepInactiveTask = (kind: TaskKind, task: RegisterTask | LoginTask) => {
  const dismissedMeta = readDismissedTaskMeta(TASK_KEYS[kind].dismissKey)
  const clearedMeta = readClearedTaskMeta(TASK_KEYS[kind].clearedKey)
  return !isTaskDismissed(task, dismissedMeta) && !isTaskMetaMatch(task, clearedMeta)
}

const loadCurrentTaskByKind = async (kind: TaskKind) => {
  try {
    const current = kind === 'register'
      ? await accountsApi.getRegisterCurrent()
      : await accountsApi.getLoginCurrent()
    if (current && 'id' in current) {
      const isActive = current.status === 'running' || current.status === 'pending'
      if (isActive) {
        handleTaskActive(kind, current)
      } else if (shouldKeepInactiveTask(kind, current)) {
        handleTaskInactive(kind, current)
      }
    } else {
      handleTaskIdle(kind)
    }
  } catch (error: any) {
    if (error?.status === 404 || error?.message === 'Not found') {
      handleTaskNotFound(kind)
    } else {
      automationError.value = error.message || (kind === 'register' ? '加载注册任务失败' : '加载刷新任务失败')
    }
  }
}

const readClearMarker = (key: string): TaskLogLine | null => {
  const raw = localStorage.getItem(key)
  if (!raw) return null

  // Backward compatibility: older versions stored numeric offsets.
  // If we see a number, ignore it so logs still render.
  const asNumber = Number(raw)
  if (Number.isFinite(asNumber)) return null

  try {
    const parsed = JSON.parse(raw) as Partial<TaskLogLine> | null
    if (!parsed || typeof parsed !== 'object') return null
    if (typeof parsed.time !== 'string' || typeof parsed.level !== 'string' || typeof parsed.message !== 'string') {
      return null
    }
    return { time: parsed.time, level: parsed.level, message: parsed.message }
  } catch {
    return null
  }
}

const writeClearMarker = (key: string, value: TaskLogLine | null) => {
  try {
    if (!value) {
      localStorage.removeItem(key)
      return
    }
    localStorage.setItem(key, JSON.stringify(value))
  } catch {
    // ignore storage errors
  }
}

const syncRegisterTask = (task: RegisterTask | null, persist = true) => {
  if (!task) {
    registerTask.value = null
    lastRegisterTaskId.value = null
    registerLogClearMarker.value = null
    if (persist) {
      removeCachedTask(REGISTER_TASK_CACHE_KEY)
      writeClearMarker(REGISTER_CLEAR_KEY, null)
    }
    return
  }

  registerTask.value = task
  if (task.id && task.id !== lastRegisterTaskId.value) {
    lastRegisterTaskId.value = task.id
    writeDismissedTaskMeta(REGISTER_DISMISS_KEY, null)
    writeClearedTaskMeta(REGISTER_CLEARED_KEY, null)
    setLogClearMarker('register', null)
    writeClearMarker(REGISTER_CLEAR_KEY, null)
    // 新注册任务启动时，自动清理已结束的刷新任务，避免堆叠显示
    clearFinishedTask('login')
  }
  if (persist) {
    writeCachedTask(REGISTER_TASK_CACHE_KEY, task)
  }
}

const syncLoginTask = (task: LoginTask | null, persist = true) => {
  if (!task) {
    loginTask.value = null
    lastLoginTaskId.value = null
    loginLogClearMarker.value = null
    if (persist) {
      removeCachedTask(LOGIN_TASK_CACHE_KEY)
      writeClearMarker(LOGIN_CLEAR_KEY, null)
    }
    return
  }

  loginTask.value = task
  if (task.id && task.id !== lastLoginTaskId.value) {
    lastLoginTaskId.value = task.id
    writeDismissedTaskMeta(LOGIN_DISMISS_KEY, null)
    writeClearedTaskMeta(LOGIN_CLEARED_KEY, null)
    setLogClearMarker('login', null)
    writeClearMarker(LOGIN_CLEAR_KEY, null)
    // 新刷新任务启动时，自动清理已结束的注册任务，避免堆叠显示
    clearFinishedTask('register')
  }
  if (persist) {
    writeCachedTask(LOGIN_TASK_CACHE_KEY, task)
  }
}

const hydrateTaskCache = () => {
  registerLogClearMarker.value = readClearMarker(REGISTER_CLEAR_KEY)
  loginLogClearMarker.value = readClearMarker(LOGIN_CLEAR_KEY)
  const cachedRegister = readCachedTask<RegisterTask>(REGISTER_TASK_CACHE_KEY)
  if (cachedRegister) {
    const dismissedMeta = readDismissedTaskMeta(TASK_KEYS.register.dismissKey)
    const clearedMeta = readClearedTaskMeta(TASK_KEYS.register.clearedKey)
    if (!isTaskDismissed(cachedRegister, dismissedMeta) && !isTaskMetaMatch(cachedRegister, clearedMeta)) {
      registerTask.value = cachedRegister
      lastRegisterTaskId.value = cachedRegister.id || null
    }
  }
  const cachedLogin = readCachedTask<LoginTask>(LOGIN_TASK_CACHE_KEY)
  if (cachedLogin) {
    const dismissedMeta = readDismissedTaskMeta(TASK_KEYS.login.dismissKey)
    const clearedMeta = readClearedTaskMeta(TASK_KEYS.login.clearedKey)
    if (!isTaskDismissed(cachedLogin, dismissedMeta) && !isTaskMetaMatch(cachedLogin, clearedMeta)) {
      loginTask.value = cachedLogin
      lastLoginTaskId.value = cachedLogin.id || null
    }
  }
}

const cleanupCancelledTasks = () => {
  // Keep completed/cancelled task logs; do not auto-clear.
}

const openRegisterModal = () => {
  isRegisterOpen.value = true
  addMode.value = 'register'
  importText.value = ''
  importError.value = ''
  isImporting.value = false
  importFileName.value = ''
  // 重置为设置中的邮箱服务提供商
  selectedMailProvider.value = settings.value?.basic?.temp_mail_provider || defaultMailProvider
}

const openExportModal = (format: 'json' | 'txt' = 'json') => {
  exportFormat.value = format
  exportScope.value = 'all'
  isExportOpen.value = true
}

const closeExportModal = () => {
  isExportOpen.value = false
}

const closeRegisterModal = () => {
  isRegisterOpen.value = false
}

const IMPORT_EXPIRES_AT = '1970-01-01 00:00:00'

const parseImportLines = (raw: string) => {
  const items: AccountConfigItem[] = []
  const errors: string[] = []
  const lines = raw.split(/\r?\n/).map(line => line.trim()).filter(Boolean)

  lines.forEach((line, index) => {
    const parts = line.split('----').map(part => part.trim())
    const lineNo = index + 1

    if (!parts.length) return

    if (parts[0].toLowerCase() === 'duckmail') {
      if (parts.length < 3 || !parts[1] || !parts[2]) {
        errors.push(`第 ${lineNo} 行格式错误（duckmail）`)
        return
      }
      const email = parts[1]
      const password = parts.slice(2).join('----')
      items.push({
        id: email,
        secure_c_ses: '',
        csesidx: '',
        config_id: '',
        expires_at: IMPORT_EXPIRES_AT,
        mail_provider: 'duckmail',
        mail_address: email,
        mail_password: password,
      })
      return
    }

    if (parts[0].toLowerCase() === 'moemail') {
      if (parts.length < 3 || !parts[1] || !parts[2]) {
        errors.push(`第 ${lineNo} 行格式错误（moemail）`)
        return
      }
      const email = parts[1]
      const emailId = parts[2]  // moemail 的 email_id 作为 password 存储
      items.push({
        id: email,
        secure_c_ses: '',
        csesidx: '',
        config_id: '',
        expires_at: IMPORT_EXPIRES_AT,
        mail_provider: 'moemail',
        mail_address: email,
        mail_password: emailId,
      })
      return
    }

    if (parts[0].toLowerCase() === 'freemail') {
      if (parts.length < 2 || !parts[1]) {
        errors.push(`第 ${lineNo} 行格式错误（freemail）`)
        return
      }
      const email = parts[1]

      // 完整格式：freemail----email----base_url----jwt_token----verify_ssl----domain
      if (parts.length >= 6) {
        items.push({
          id: email,
          secure_c_ses: '',
          csesidx: '',
          config_id: '',
          expires_at: IMPORT_EXPIRES_AT,
          mail_provider: 'freemail',
          mail_address: email,
          mail_password: '',
          mail_base_url: parts[2] || undefined,
          mail_jwt_token: parts[3] || undefined,
          mail_verify_ssl: parts[4] === 'true' || parts[4] === '1',
          mail_domain: parts[5] || undefined,
        })
        return
      }

      // 简化格式：freemail----email
      items.push({
        id: email,
        secure_c_ses: '',
        csesidx: '',
        config_id: '',
        expires_at: IMPORT_EXPIRES_AT,
        mail_provider: 'freemail',
        mail_address: email,
        mail_password: '',
      })
      return
    }

    if (parts[0].toLowerCase() === 'gptmail') {
      if (parts.length < 2 || !parts[1]) {
        errors.push(`第 ${lineNo} 行格式错误（gptmail）`)
        return
      }
      const email = parts[1]
      items.push({
        id: email,
        secure_c_ses: '',
        csesidx: '',
        config_id: '',
        expires_at: IMPORT_EXPIRES_AT,
        mail_provider: 'gptmail',
        mail_address: email,
        mail_password: '',
      })
      return
    }

    if (parts[0].toLowerCase() === 'cfmail') {
      if (parts.length < 2 || !parts[1]) {
        errors.push(`第 ${lineNo} 行格式错误（cfmail）`)
        return
      }
      const email = parts[1]
      const jwt = parts[2] || ''
      items.push({
        id: email,
        secure_c_ses: '',
        csesidx: '',
        config_id: '',
        expires_at: IMPORT_EXPIRES_AT,
        mail_provider: 'cfmail',
        mail_address: email,
        mail_password: jwt,
      })
      return
    }

    if (parts.length >= 4 && parts[0] && parts[2] && parts[3]) {
      const email = parts[0]
      const password = parts[1] || ''
      const clientId = parts[2]
      const refreshToken = parts.slice(3).join('----')
      items.push({
        id: email,
        secure_c_ses: '',
        csesidx: '',
        config_id: '',
        expires_at: IMPORT_EXPIRES_AT,
        mail_provider: 'microsoft',
        mail_address: email,
        mail_password: password,
        mail_client_id: clientId,
        mail_refresh_token: refreshToken,
        mail_tenant: 'consumers',
      })
      return
    }

    errors.push(`第 ${lineNo} 行格式错误`)
  })

  return { items, errors }
}

const triggerImportFile = () => {
  importFileInput.value?.click()
}

const handleImportFile = async (event: Event) => {
  const target = event.target as HTMLInputElement
  const file = target.files?.[0]
  if (!file) return
  importError.value = ''
  importFileName.value = file.name

  try {
    const content = await file.text()
    if (file.name.toLowerCase().endsWith('.json') || file.type.includes('json')) {
      const parsed = JSON.parse(content)
      const importList = Array.isArray(parsed) ? parsed : parsed?.accounts
      if (!Array.isArray(importList)) {
        importError.value = 'JSON 格式错误：需要数组或包含 accounts 字段'
        return
      }
      const existing = await loadConfigList()
      const next = [...existing]
      const indexMap = new Map(next.map((acc, idx) => [acc.id, idx]))
      const importedIds: string[] = []

      importList.forEach((item: any) => {
        const idx = indexMap.get(item.id || '')
        if (idx === undefined) {
          next.push(item)
        } else {
          next[idx] = { ...next[idx], ...item }
        }
        if (item.id) importedIds.push(item.id)
      })

      await accountsStore.updateConfig(next)
      selectedIds.value = new Set(importedIds)
      toast.success(`导入 ${importList.length} 条账号配置`)

      // Check if imported accounts need refresh (no valid cookies)
      const needRefresh = importList.some((item: any) => !item.secure_c_ses)
      if (needRefresh && importedIds.length > 0) {
        closeRegisterModal()
        const confirmed = await confirmDialog.ask({
          title: '导入成功',
          message: `已导入 ${importedIds.length} 个账户。检测到部分账户缺少 Cookie，是否立即刷新？`,
          confirmText: '立即刷新',
          cancelText: '稍后手动刷新',
        })
        if (confirmed) {
          await handleRefreshSelected()
        }
      } else {
        closeRegisterModal()
      }
      return
    }

    importText.value = content
    await handleImport()
  } catch (error: any) {
    importError.value = error.message || '文件解析失败'
  } finally {
    target.value = ''
  }
}

const handleImport = async () => {
  importError.value = ''
  if (!importText.value.trim()) {
    importError.value = '请输入导入内容'
    return
  }
  const { items, errors } = parseImportLines(importText.value)
  if (!items.length) {
    importError.value = errors.length ? errors.join('，') : '未识别到有效账号'
    return
  }
  if (errors.length) {
    importError.value = errors.slice(0, 3).join('，')
    return
  }

  isImporting.value = true
  try {
    const list = await loadConfigList()
    const next = [...list]
    const indexMap = new Map(next.map((acc, idx) => [acc.id, idx]))
    const importedIds: string[] = []

    items.forEach((item) => {
      const idx = indexMap.get(item.id || '')
      if (idx === undefined) {
        next.push(item)
        importedIds.push(item.id)
        return
      }

      const existing = next[idx]
      const updated: AccountConfigItem = {
        ...existing,
        mail_provider: item.mail_provider,
        mail_address: item.mail_address,
      }

      if (item.mail_provider === 'microsoft') {
        updated.mail_client_id = item.mail_client_id
        updated.mail_refresh_token = item.mail_refresh_token
        updated.mail_tenant = item.mail_tenant
        updated.mail_password = item.mail_password
      } else {
        updated.mail_password = item.mail_password
        updated.mail_client_id = undefined
        updated.mail_refresh_token = undefined
        updated.mail_tenant = undefined
      }

      next[idx] = updated
      importedIds.push(item.id)
    })

    await accountsStore.updateConfig(next)
    await refreshAccounts()

    selectedIds.value = new Set(importedIds)
    toast.success(`成功导入 ${importedIds.length} 个账户`)
    closeRegisterModal()

    const confirmed = await confirmDialog.ask({
      title: '导入成功',
      message: `已导入 ${importedIds.length} 个账户并自动选中。是否立即刷新这些账户以获取 Cookie？`,
      confirmText: '立即刷新',
      cancelText: '稍后手动刷新',
    })

    if (confirmed) {
      await handleRefreshSelected()
    }
  } catch (error: any) {
    importError.value = error.message || '导入失败'
    toast.error(error.message || '导入失败')
  } finally {
    isImporting.value = false
  }
}

const exportConfig = async (format: 'json' | 'txt', scope: 'all' | 'selected' = 'all') => {
  try {
    const response = await accountsApi.getConfig()
    let list = Array.isArray(response.accounts) ? response.accounts : []
    if (scope === 'selected') {
      const selected = selectedIds.value
      list = list.filter((item) => selected.has(item.id))
    }
    const timestamp = new Date().toISOString().replace(/[:.]/g, '-')

    if (format === 'json') {
      const payload = JSON.stringify(list, null, 2)
      downloadText(payload, `accounts-${timestamp}.json`, 'application/json')
      toast.success('导出 JSON 成功')
      return
    }

    const lines = list.map((item) => {
      const provider = (item.mail_provider || '').toLowerCase()
      const email = item.mail_address || item.id || ''
      if (!email) return ''
      if (provider === 'moemail') {
        return `moemail----${email}----${item.mail_password || ''}`
      }
      if (provider === 'freemail') {
        return `freemail----${email}`
      }
      if (provider === 'gptmail') {
        return `gptmail----${email}`
      }
      if (provider === 'cfmail') {
        return `cfmail----${email}----${item.mail_password || ''}`
      }
      if (provider === 'duckmail') {
        return `duckmail----${email}----${item.mail_password || ''}`
      }
      if (provider === 'microsoft' || item.mail_client_id || item.mail_refresh_token) {
        return `${email}----${item.mail_password || ''}----${item.mail_client_id || ''}----${item.mail_refresh_token || ''}`
      }
      if (item.mail_password) {
        return `duckmail----${email}----${item.mail_password}`
      }
      return email
    }).filter(Boolean)

    downloadText(lines.join('\n'), `accounts-${timestamp}.txt`, 'text/plain')
    toast.success('导出 TXT 成功')
  } catch (error: any) {
    toast.error(error.message || '导出失败')
  }
}

const runExport = async () => {
  await exportConfig(exportFormat.value, exportScope.value)
  closeExportModal()
}

const downloadText = (content: string, filename: string, mime: string) => {
  const blob = new Blob([content], { type: mime })
  const url = URL.createObjectURL(blob)
  const link = document.createElement('a')
  link.href = url
  link.download = filename
  document.body.appendChild(link)
  link.click()
  document.body.removeChild(link)
  URL.revokeObjectURL(url)
}

const refreshTaskSnapshot = async () => {
  try {
    const tasks: Promise<void>[] = []
    const registerId = registerTask.value?.id
    const loginId = loginTask.value?.id

    if (registerId) {
      tasks.push(updateRegisterTask(registerId))
    }
    if (loginId) {
      tasks.push(updateLoginTask(loginId))
    }

    if (!tasks.length) {
      await loadCurrentTasks()
    } else {
      await Promise.all(tasks)
    }

    cleanupCancelledTasks()
  } catch (error: any) {
    automationError.value = error?.message || '任务状态更新失败'
  }
}

const openTaskModal = async () => {
  isTaskOpen.value = true
  activeTaskTab.value = 'current'
  await refreshTaskSnapshot()
}

const fetchTaskHistory = async () => {
  isLoadingHistory.value = true
  try {
    const response = await fetch('/admin/task-history', {
      headers: { 'Content-Type': 'application/json' }
    })
    if (!response.ok) throw new Error('获取历史记录失败')
    const data = await response.json()
    const history = Array.isArray(data.history) ? data.history : []
    const dismissedRegister = readDismissedTaskMeta(REGISTER_DISMISS_KEY)
    const dismissedLogin = readDismissedTaskMeta(LOGIN_DISMISS_KEY)
    taskHistory.value = history.filter((record: any) => {
      const meta = record?.type === 'register' ? dismissedRegister : dismissedLogin
      if (!meta) return true
      const id = typeof record?.id === 'string' ? record.id : String(record?.id || '')
      const createdAt = typeof record?.created_at === 'number' ? record.created_at : undefined
      if (meta.id && id && id === meta.id) return false
      if (typeof meta.created_at === 'number' && typeof createdAt === 'number' && createdAt === meta.created_at) {
        return false
      }
      return true
    })
  } catch (error: any) {
    toast.error(error?.message || '获取历史记录失败')
  } finally {
    isLoadingHistory.value = false
  }
}

const clearTaskHistory = async () => {
  const confirmed = await confirmDialog.ask({
    title: '清空历史记录',
    message: '确定要清空所有任务历史记录吗？',
    confirmText: '清空',
  })
  if (!confirmed) return
  try {
    const response = await fetch('/admin/task-history?confirm=yes', {
      method: 'DELETE',
      headers: { 'Content-Type': 'application/json' }
    })
    if (!response.ok) throw new Error('清空历史记录失败')
    taskHistory.value = []
    toast.success('历史记录已清空')
  } catch (error: any) {
    toast.error(error?.message || '清空历史记录失败')
  }
}

const closeTaskModal = () => {
  isTaskOpen.value = false
  // 关闭弹窗时，确保已中断任务不会被缓存"复活"
  cleanupCancelledTasks()
}

const loadScheduledConfig = async () => {
  isLoadingScheduledConfig.value = true
  try {
    const settings = await settingsApi.get()
    cachedSettings.value = settings  // 缓存配置
    scheduledRefreshEnabled.value = settings.retry.scheduled_refresh_enabled ?? false
    scheduledRefreshCron.value = settings.retry.scheduled_refresh_cron ?? '08:00,20:00'
    refreshBatchSize.value = settings.retry.refresh_batch_size ?? 5
    refreshBatchInterval.value = settings.retry.refresh_batch_interval_minutes ?? 30
    refreshCooldownHours.value = settings.retry.refresh_cooldown_hours ?? 12
    refreshWindowHours.value = settings.basic.refresh_window_hours ?? 24
  } catch (error: any) {
    toast.error(error?.message || '加载定时任务配置失败')
  } finally {
    isLoadingScheduledConfig.value = false
  }
}

const saveScheduledConfig = async () => {
  // 验证每批数量
  if (isNaN(refreshBatchSize.value) || refreshBatchSize.value < 1 || refreshBatchSize.value > 20) {
    toast.error('每批数量必须在 1-20 之间')
    return
  }
  // 验证批次间隔
  if (isNaN(refreshBatchInterval.value) || refreshBatchInterval.value < 5 || refreshBatchInterval.value > 120) {
    toast.error('批次间隔必须在 5-120 分钟之间')
    return
  }
  // 验证冷却时间
  if (isNaN(refreshCooldownHours.value) || refreshCooldownHours.value < 1 || refreshCooldownHours.value > 48) {
    toast.error('冷却时间必须在 1-48 小时之间')
    return
  }

  // 验证过期刷新窗口
  if (isNaN(refreshWindowHours.value) || !Number.isInteger(refreshWindowHours.value)) {
    toast.error('过期刷新窗口必须是有效的整数')
    return
  }
  if (refreshWindowHours.value < 1 || refreshWindowHours.value > 168) {
    toast.error('过期刷新窗口必须在 1-168 小时之间')
    return
  }

  isSavingScheduledConfig.value = true
  try {
    // 使用缓存的配置，避免重复API调用
    const settings = cachedSettings.value || await settingsApi.get()
    settings.retry.scheduled_refresh_enabled = scheduledRefreshEnabled.value
    settings.retry.scheduled_refresh_cron = scheduledRefreshCron.value
    settings.retry.refresh_batch_size = refreshBatchSize.value
    settings.retry.refresh_batch_interval_minutes = refreshBatchInterval.value
    settings.retry.refresh_cooldown_hours = refreshCooldownHours.value
    settings.basic.refresh_window_hours = refreshWindowHours.value
    await settingsApi.update(settings)
    cachedSettings.value = settings  // 更新缓存
    toast.success('定时任务配置已保存')
  } catch (error: any) {
    toast.error(error?.message || '保存定时任务配置失败')
  } finally {
    isSavingScheduledConfig.value = false
  }
}

const clearTaskLogs = async () => {
  const confirmed = await confirmDialog.ask({
    title: '清空当前日志',
    message: '确定要清空当前任务日志吗？',
    confirmText: '清空',
  })
  if (!confirmed) return
  const clearLogsFor = (kind: TaskKind) => {
    const task = getTaskByKind(kind)
    if (!task) return
    if (!isTaskActive(task)) {
      markTaskCleared(kind, task)
      clearTaskSnapshot(kind, true)
      return
    }
    const logs = (task.logs || []) as TaskLogLine[]
    if (!logs.length) return
    const marker = logs[logs.length - 1]
    setLogClearMarker(kind, marker)
    writeClearMarker(TASK_KEYS[kind].clearKey, marker)
  }

  clearLogsFor('register')
  clearLogsFor('login')

  automationError.value = ''
  toast.success('当前日志已清空')
}

const filterLogsAfterMarker = (logs: TaskLogLine[], marker: TaskLogLine | null) => {
  if (!marker) return logs
  for (let i = logs.length - 1; i >= 0; i -= 1) {
    const item = logs[i]
    if (item.time === marker.time && item.level === marker.level && item.message === marker.message) {
      return logs.slice(i + 1)
    }
  }
  // Marker not found (e.g., backend truncates to last N logs) — show current logs so new logs keep appearing.
  return logs
}

const cancelRegister = async (taskId: string) => {
  try {
    await accountsApi.cancelRegisterTask(taskId, 'cancelled_by_user')
    await refreshTaskSnapshot()
    toast.success('已请求中断注册任务')
  } catch (error: any) {
    toast.error(error?.message || '中断注册任务失败')
  }
}

const cancelLogin = async (taskId: string) => {
  try {
    await accountsApi.cancelLoginTask(taskId, 'cancelled_by_user')
    await refreshTaskSnapshot()
    toast.success('已请求中断刷新任务')
  } catch (error: any) {
    toast.error(error?.message || '中断刷新任务失败')
  }
}

const toggleMoreActions = () => {
  showMoreActions.value = !showMoreActions.value
}

const closeMoreActions = () => {
  showMoreActions.value = false
}

const handleMoreActionsClick = (event: MouseEvent) => {
  if (!showMoreActions.value) return
  const target = event.target as Node
  if (moreActionsRef.value && !moreActionsRef.value.contains(target)) {
    showMoreActions.value = false
  }
}

// 监听标签页切换，自动加载历史记录
watch(activeTaskTab, async (newTab) => {
  if (newTab === 'history') {
    await fetchTaskHistory()
  } else if (newTab === 'scheduled') {
    await loadScheduledConfig()
  }
})

onMounted(async () => {
  hydrateTaskCache()
  await refreshAccounts()
  await loadCurrentTasks()
  startBackgroundTaskPolling()
  document.addEventListener('click', handleMoreActionsClick)
})

const registerLogs = computed(() => {
  const logs = registerTask.value?.logs || []
  return filterLogsAfterMarker(logs as TaskLogLine[], registerLogClearMarker.value)
})
const loginLogs = computed(() => {
  const logs = loginTask.value?.logs || []
  return filterLogsAfterMarker(logs as TaskLogLine[], loginLogClearMarker.value)
})
const scrollTaskLogsToBottom = async () => {
  await nextTick()
  const container = taskLogsRef.value
  if (!container) return
  container.scrollTop = container.scrollHeight
}

watch([registerLogs, loginLogs, isTaskOpen], async () => {
  if (!isTaskOpen.value) return
  await scrollTaskLogsToBottom()
}, { deep: true })
const isTaskRunning = computed(() => {
  const registerStatus = registerTask.value?.status
  const loginStatus = loginTask.value?.status
  return registerStatus === 'running' ||
    registerStatus === 'pending' ||
    loginStatus === 'running' ||
    loginStatus === 'pending'
})

const taskProgressText = computed(() => {
  const register = registerTask.value
  const login = loginTask.value
  const registerActive = register?.status === 'running' || register?.status === 'pending'
  const loginActive = login?.status === 'running' || login?.status === 'pending'

  if (registerActive && loginActive) {
    return `注册 ${register.progress}/${register.count} | 刷新 ${login.progress}/${login.account_ids.length}`
  }
  if (registerActive) {
    return `注册 ${register.progress}/${register.count}`
  }
  if (loginActive) {
    return `刷新 ${login.progress}/${login.account_ids.length}`
  }
  return ''
})

onBeforeUnmount(() => {
  clearRegisterTimer()
  clearLoginTimer()
  clearBackgroundTaskTimer()
  document.removeEventListener('click', handleMoreActionsClick)
})

const statusLabel = (account: AdminAccount) => {
  // 检查是否正在刷新
  if (refreshingAccountIds.value.has(account.id)) {
    return '刷新中'
  }
  if (account.cooldown_reason?.includes('429') && account.cooldown_seconds > 0) {
    return '429限流'
  }
  if (account.disabled) {
    if (account.disabled_reason?.includes('403')) {
      return '403 禁用'
    }
    return '手动禁用'
  }
  if (account.status === '已过期') {
    return '已过期'
  }
  if (account.status === '即将过期') {
    return '即将过期'
  }
  return '正常'
}

const statusClass = (account: AdminAccount) => {
  const status = statusLabel(account)
  if (status === '刷新中') {
    return 'bg-sky-500 text-white'
  }
  if (status === '429限流' || status === '即将过期') {
    return 'bg-amber-200 text-amber-900'
  }
  if (status === '已过期') {
    return 'bg-destructive/10 text-destructive'
  }
  if (status === '手动禁用') {
    return 'bg-muted text-muted-foreground'
  }
  if (status === '403 禁用') {
    return 'bg-rose-600 text-white'
  }
  return 'bg-emerald-500 text-white'
}

const shouldShowEnable = (account: AdminAccount) => {
  if (account.cooldown_reason?.includes('429') && account.cooldown_seconds > 0) {
    return true
  }
  return account.disabled
}

const displayRemaining = (value: string) => {
  if (value === '已过期') return '过期'
  if (value === '未设置') return '未设置'
  return value
}

const remainingClass = (account: AdminAccount) => {
  if (account.status === '已过期') return 'text-rose-600'
  if (account.status === '即将过期') return 'text-amber-700'
  if (account.status === '未设置') return 'text-muted-foreground'
  return 'text-emerald-600'
}

const trialBadgeClass = (days: number | null | undefined) => {
  if (days == null) return ''
  if (days > 7) return 'bg-emerald-500 text-white'
  if (days >= 3) return 'bg-amber-500 text-white'
  return 'bg-rose-500 text-white'
}

const rowClass = (account: AdminAccount) => {
  const status = statusLabel(account)
  if (status === '手动禁用' || status === '已过期' || status === '403 禁用') {
    return 'bg-muted/70'
  }
  return ''
}

const toggleSelect = (accountId: string) => {
  const next = new Set(selectedIds.value)
  if (next.has(accountId)) {
    next.delete(accountId)
  } else {
    next.add(accountId)
  }
  selectedIds.value = next
}

const toggleSelectAll = () => {
  if (allSelected.value) {
    selectedIds.value = new Set()
    return
  }
  selectedIds.value = new Set(filteredAccounts.value.map(account => account.id))
}

const getConfigId = (acc: AccountConfigItem, index: number) =>
  acc.id || `account_${index + 1}`

const loadConfigList = async () => {
  const response = await accountsApi.getConfig()
  return response.accounts.map((acc, index) => ({
    ...acc,
    id: getConfigId(acc, index),
  }))
}

const formatLogLine = (log: { time: string; level: string; message: string }) =>
  `${log.time} [${log.level}] ${log.message}`

const applyEditTarget = (list: AccountConfigItem[], accountId: string) => {
  let targetIndex = -1
  for (let i = 0; i < list.length; i += 1) {
    if (list[i].id === accountId) {
      targetIndex = i
      break
    }
  }
  if (targetIndex === -1) {
    editError.value = '未找到对应账号配置。'
    return false
  }

  const target = list[targetIndex]
  editForm.value = {
    id: target.id,
    secure_c_ses: target.secure_c_ses,
    csesidx: target.csesidx,
    config_id: target.config_id,
    host_c_oses: target.host_c_oses,
    expires_at: target.expires_at,
  }
  configAccounts.value = list
  editIndex.value = targetIndex
  isEditOpen.value = true
  return true
}

const openEdit = async (accountId: string) => {
  editError.value = ''
  try {
    const list = await loadConfigList()
    applyEditTarget(list, accountId)
  } catch (error: any) {
    editError.value = error.message || '加载账号配置失败'
  }
}

const openConfigPanel = async () => {
  configError.value = ''
  try {
    const response = await accountsApi.getConfig()
    configData.value = Array.isArray(response.accounts) ? response.accounts : []
    configJson.value = JSON.stringify(maskConfig(configData.value), null, 2)
    configMasked.value = true
    isConfigOpen.value = true
  } catch (error: any) {
    configError.value = error.message || '加载账号配置失败'
  }
}

const closeConfigPanel = () => {
  isConfigOpen.value = false
  configError.value = ''
  configMasked.value = false
}

const getConfigFromEditor = () => {
  const parsed = JSON.parse(configJson.value)
  if (!Array.isArray(parsed)) {
    throw new Error('配置格式必须是数组。')
  }
  return parsed as AccountConfigItem[]
}

const maskValue = (value: unknown) => {
  if (typeof value !== 'string') return value
  if (!value) return value
  if (value.length <= 6) return `${value.slice(0, 2)}****`
  return `${value.slice(0, 3)}****`
}

const maskConfig = (list: AccountConfigItem[]) => {
  const fields = new Set([
    'secure_c_ses',
    'csesidx',
    'config_id',
    'host_c_oses',
    'mail_password',
    'mail_refresh_token',
    'mail_client_id',
  ])
  return list.map((item) => {
    const next = { ...item }
    fields.forEach((field) => {
      const value = (next as Record<string, unknown>)[field]
      if (value) {
        ;(next as Record<string, unknown>)[field] = maskValue(value)
      }
    })
    return next
  })
}

const toggleConfigMask = () => {
  configError.value = ''
  if (!configMasked.value) {
    try {
      configData.value = getConfigFromEditor()
    } catch (error: any) {
      configError.value = error.message || 'JSON 格式错误'
      return
    }
    configJson.value = JSON.stringify(maskConfig(configData.value), null, 2)
    configMasked.value = true
    return
  }

  configJson.value = JSON.stringify(configData.value, null, 2)
  configMasked.value = false
}

const saveConfigPanel = async () => {
  configError.value = ''
  try {
    const parsed = getConfigFromEditor()
    await accountsStore.updateConfig(parsed)
    toast.success('配置保存成功')
    closeConfigPanel()
  } catch (error: any) {
    configError.value = error.message || '保存失败'
    toast.error(error.message || '保存失败')
  }
}

const closeEdit = () => {
  isEditOpen.value = false
  editError.value = ''
}

const saveEdit = async () => {
  if (editIndex.value === null) return
  const next = [...configAccounts.value]
  next[editIndex.value] = {
    ...next[editIndex.value],
    id: editForm.value.id,
    secure_c_ses: editForm.value.secure_c_ses,
    csesidx: editForm.value.csesidx,
    config_id: editForm.value.config_id,
    host_c_oses: editForm.value.host_c_oses || undefined,
    expires_at: editForm.value.expires_at || undefined,
  }

  try {
    await accountsStore.updateConfig(next)
    toast.success('账号编辑成功')
    closeEdit()
  } catch (error: any) {
    editError.value = error.message || '保存失败'
    toast.error(error.message || '保存失败')
  }
}

const formatOpErrors = (errors: string[]) => {
  if (!errors.length) return ''
  const sample = errors[0]
  return `失败 ${errors.length} 个${sample ? `，示例：${sample}` : ''}`
}

const handleOpResult = (result: { ok: boolean; errors: string[] }, successMessage: string, failMessage: string) => {
  if (result.ok) {
    toast.success(successMessage)
    return true
  }
  const detail = formatOpErrors(result.errors)
  toast.error(detail ? `${failMessage}（${detail}）` : failMessage)
  return false
}

const handleBulkEnable = async () => {
  if (isOperating.value) return
  try {
    const result = await accountsStore.bulkEnable(Array.from(selectedIds.value))
    if (handleOpResult(result, '批量启用成功', '批量启用失败')) {
      selectedIds.value = new Set()
    }
  } catch (error: any) {
    toast.error(error.message || '批量启用失败')
  }
}

const handleBulkDisable = async () => {
  const confirmed = await confirmDialog.ask({
    title: '批量禁用',
    message: '确定要批量禁用选中的账号吗？',
  })
  if (!confirmed) return
  if (isOperating.value) return
  try {
    const result = await accountsStore.bulkDisable(Array.from(selectedIds.value))
    if (handleOpResult(result, '批量禁用成功', '批量禁用失败')) {
      selectedIds.value = new Set()
    }
  } catch (error: any) {
    toast.error(error.message || '批量禁用失败')
  }
}

const handleBulkDelete = async () => {
  if (isOperating.value) return
  const confirmed = await confirmDialog.ask({
    title: '批量删除',
    message: '确定要批量删除选中的账号吗？',
    confirmText: '删除',
  })
  if (!confirmed) return
  try {
    const result = await accountsStore.bulkDelete(Array.from(selectedIds.value))
    if (handleOpResult(result, '批量删除成功', '批量删除失败')) {
      selectedIds.value = new Set()
    }
  } catch (error: any) {
    toast.error(error.message || '批量删除失败')
  }
}

const handleEnable = async (accountId: string) => {
  if (isOperating.value) return
  try {
    const result = await accountsStore.enableAccount(accountId)
    handleOpResult(result, '账号已启用', '启用失败')
  } catch (error: any) {
    toast.error(error.message || '启用失败')
  }
}

const handleDisable = async (accountId: string) => {
  if (isOperating.value) return
  const confirmed = await confirmDialog.ask({
    title: '禁用账号',
    message: '确定要禁用该账号吗？',
  })
  if (!confirmed) return
  try {
    const result = await accountsStore.disableAccount(accountId)
    handleOpResult(result, '账号已禁用', '禁用失败')
  } catch (error: any) {
    toast.error(error.message || '禁用失败')
  }
}

const handleDelete = async (accountId: string) => {
  if (isOperating.value) return
  const confirmed = await confirmDialog.ask({
    title: '删除账号',
    message: '确定要删除该账号吗？',
    confirmText: '删除',
  })
  if (!confirmed) return
  try {
    const result = await accountsStore.deleteAccount(accountId)
    handleOpResult(result, '账号已删除', '删除失败')
  } catch (error: any) {
    toast.error(error.message || '删除失败')
  }
}

let registerTimer: number | null = null
let loginTimer: number | null = null
let backgroundTaskTimer: number | null = null
let backgroundTaskPending = false

const clearRegisterTimer = () => {
  if (registerTimer !== null) {
    window.clearInterval(registerTimer)
    registerTimer = null
  }
}

const clearLoginTimer = () => {
  if (loginTimer !== null) {
    window.clearInterval(loginTimer)
    loginTimer = null
  }
}

const clearBackgroundTaskTimer = () => {
  if (backgroundTaskTimer !== null) {
    window.clearInterval(backgroundTaskTimer)
    backgroundTaskTimer = null
  }
  backgroundTaskPending = false
}

const getTaskResultType = (
  status: string,
  success: number,
  fail: number,
  total?: number,
) => {
  if (status === 'pending' || status === 'running' || status === 'cancelled') return status
  const s = Number.isFinite(success) ? success : 0
  const f = Number.isFinite(fail) ? fail : 0
  const t = Number.isFinite(total) ? total : s + f
  if (s > 0 && f > 0) return 'partial'
  if (s > 0 && f === 0) return 'success'
  if (f > 0 && s === 0) return 'failed'
  if (t === 0) return 'none'
  return 'none'
}

const formatTaskStatus = (task: any) => {
  const status = task?.status || ''
  const success = task?.success_count ?? 0
  const fail = task?.fail_count ?? 0
  const total = Number.isFinite(task?.total) ? task.total : undefined
  const result = getTaskResultType(status, success, fail, total)
  if (result === 'pending') return '等待中'
  if (result === 'running') return '执行中'
  if (result === 'cancelled') return '已中断'
  if (result === 'success') return '已完成（全部成功）'
  if (result === 'failed') return '已完成（全部失败）'
  if (result === 'partial') return '已完成（部分失败）'
  return '已完成'
}

const getHistoryTotal = (record: any) => {
  const total = Number.isFinite(record?.total) ? record.total : undefined
  if (typeof total === 'number') return total
  const progress = Number.isFinite(record?.progress) ? record.progress : 0
  return progress
}

const getHistoryStatusTextClass = (record: any) => {
  const status = record?.status
  const success = record?.success_count ?? 0
  const fail = record?.fail_count ?? 0
  const total = getHistoryTotal(record)
  const result = getTaskResultType(status, success, fail, total)
  if (result === 'running' || result === 'pending') return 'text-sky-600'
  if (result === 'success') return 'text-emerald-600'
  if (result === 'failed') return 'text-rose-600'
  if (result === 'partial') return 'text-amber-600'
  if (result === 'cancelled') return 'text-muted-foreground'
  return 'text-muted-foreground'
}

const getHistoryStatusIndicatorClass = (record: any) => {
  const status = record?.status
  const success = record?.success_count ?? 0
  const fail = record?.fail_count ?? 0
  const total = getHistoryTotal(record)
  const result = getTaskResultType(status, success, fail, total)
  if (result === 'running' || result === 'pending') return 'bg-sky-400'
  if (result === 'success') return 'bg-emerald-400'
  if (result === 'failed') return 'bg-rose-500'
  if (result === 'partial') return 'bg-amber-400'
  return 'bg-muted-foreground'
}

const getTaskStatusIndicatorClass = (task: RegisterTask | LoginTask) => {
  const status = task.status
  const success = task.success_count ?? 0
  const fail = task.fail_count ?? 0
  const total = 'count' in task ? task.count : task.account_ids?.length
  const result = getTaskResultType(status, success, fail, total)
  if (result === 'running' || result === 'pending') return 'bg-sky-400'
  if (result === 'success') return 'bg-emerald-400'
  if (result === 'failed') return 'bg-rose-500'
  if (result === 'partial') return 'bg-amber-400'
  return 'bg-muted-foreground'
}

const updateRegisterTask = async (taskId: string) => {
  let task: RegisterTask
  try {
    task = await accountsApi.getRegisterTask(taskId)
  } catch (error: any) {
    // 任务已不存在（被清理/过期/后端重启）：静默清理，避免弹窗显示 "Not found"
    if (error?.status === 404 || error?.message === 'Not found') {
      clearRegisterTimer()
      isRegistering.value = false
      return
    }
    throw error
  }
  syncRegisterTask(task)
  if (task.status !== 'running' && task.status !== 'pending') {
    isRegistering.value = false
    clearRegisterTimer()
    await refreshAccounts()

    // 显示任务完成通知
    const successCount = task.success_count || 0
    const failCount = task.fail_count || 0
    if (successCount > 0 && failCount > 0) {
      toast.success(`注册任务完成：成功 ${successCount}，失败 ${failCount}`)
    } else if (successCount > 0 && failCount === 0) {
      toast.success(`注册任务完成：全部成功 (${successCount})`)
    } else if (failCount > 0 && successCount === 0) {
      toast.error(`注册任务完成：全部失败 (${failCount})`)
    } else {
      toast.error('注册任务失败')
    }

    await fetchTaskHistory()
    return
  }
}

const updateLoginTask = async (taskId: string) => {
  let task: LoginTask
  try {
    task = await accountsApi.getLoginTask(taskId)
  } catch (error: any) {
    // 任务已不存在（被清理/过期/后端重启）：静默清理，避免弹窗显示 "Not found"
    if (error?.status === 404 || error?.message === 'Not found') {
      clearLoginTimer()
      isRefreshing.value = false
      refreshingAccountIds.value = new Set()  // 清空刷新状态
      return
    }
    throw error
  }
  syncLoginTask(task)

  // 更新正在刷新的账户列表
  if (task.status === 'running' || task.status === 'pending') {
    refreshingAccountIds.value = new Set(task.account_ids || [])
  } else {
    refreshingAccountIds.value = new Set()  // 任务完成，清空刷新状态
  }

  if (task.status !== 'running' && task.status !== 'pending') {
    isRefreshing.value = false
    clearLoginTimer()
    await refreshAccounts()

    // 显示任务完成通知
    const successCount = task.success_count || 0
    const failCount = task.fail_count || 0
    if (successCount > 0 && failCount > 0) {
      toast.success(`刷新任务完成：成功 ${successCount}，失败 ${failCount}`)
    } else if (successCount > 0 && failCount === 0) {
      toast.success(`刷新任务完成：全部成功 (${successCount})`)
    } else if (failCount > 0 && successCount === 0) {
      toast.error(`刷新任务完成：全部失败 (${failCount})`)
    } else {
      toast.error('刷新任务失败')
    }

    await fetchTaskHistory()
    return
  }
}

const startRegisterPolling = (taskId: string) => {
  clearRegisterTimer()
  registerTimer = window.setInterval(() => {
    updateRegisterTask(taskId).catch((error) => {
      automationError.value = error?.message || '注册任务更新失败'
      clearRegisterTimer()
      isRegistering.value = false
    })
  }, 3000)
}

const startLoginPolling = (taskId: string) => {
  clearLoginTimer()
  loginTimer = window.setInterval(() => {
    updateLoginTask(taskId).catch((error) => {
      automationError.value = error?.message || '刷新任务更新失败'
      clearLoginTimer()
      isRefreshing.value = false
    })
  }, 3000)
}

const startBackgroundTaskPolling = () => {
  if (backgroundTaskTimer !== null) return
  backgroundTaskTimer = window.setInterval(async () => {
    if (backgroundTaskPending) return
    if (isTaskOpen.value) return
    if (registerTimer !== null || loginTimer !== null) return
    if (!isRegistering.value && !isRefreshing.value && !registerTask.value && !loginTask.value) return
    backgroundTaskPending = true
    try {
      await loadCurrentTasks()
    } catch (error: any) {
      automationError.value = error?.message || '后台刷新失败'
    } finally {
      backgroundTaskPending = false
    }
  }, 6000)
}

const loadCurrentTasks = async () => {
  await loadCurrentTaskByKind('register')
  await loadCurrentTaskByKind('login')
}

const handleRegister = async () => {
  automationError.value = ''
  isRegistering.value = true
  try {
    const count = Number.isFinite(registerCount.value) && registerCount.value > 0
      ? registerCount.value
      : undefined
    const task = await accountsApi.startRegister(count, undefined, selectedMailProvider.value)
    syncRegisterTask(task)
    startRegisterPolling(task.id)
    isRegisterOpen.value = false
    isTaskOpen.value = true
  } catch (error: any) {
    automationError.value = error.message || '启动注册失败'
    isRegistering.value = false
  }
}

// 统一的刷新函数 - 所有刷新入口都调用这个
const startRefresh = async (accountIds: string[]) => {
  if (!accountIds.length) return
  automationError.value = ''
  isRefreshing.value = true
  try {
    const task = await accountsApi.startLogin(accountIds)
    syncLoginTask(task)
    // 更新正在刷新的账户列表
    refreshingAccountIds.value = new Set(task.account_ids || [])
    startLoginPolling(task.id)
    // 自动打开任务状态弹窗
    openTaskModal()
  } catch (error: any) {
    automationError.value = error.message || '启动刷新失败'
    toast.error(error.message || '启动刷新失败')
    isRefreshing.value = false
  }
}

const handleRefreshSelected = async () => {
  if (!selectedIds.value.size) return
  await startRefresh(Array.from(selectedIds.value))
}

const handleRefreshExpiring = async () => {
  automationError.value = ''
  isRefreshing.value = true
  try {
    const taskOrIdle = await accountsApi.checkLogin()
    if (taskOrIdle && 'id' in taskOrIdle) {
      syncLoginTask(taskOrIdle)
      // 更新正在刷新的账户列表
      refreshingAccountIds.value = new Set(taskOrIdle.account_ids || [])
      startLoginPolling(taskOrIdle.id)
      // 自动打开任务状态弹窗
      openTaskModal()
      return
    }
    // 没有新任务时，尝试读取当前任务（可能已有 running/pending）
    const current = await accountsApi.getLoginCurrent()
    if (current && 'id' in current) {
      syncLoginTask(current)
      // 更新正在刷新的账户列表
      refreshingAccountIds.value = new Set(current.account_ids || [])
      startLoginPolling(current.id)
      openTaskModal()
      return
    }
    isRefreshing.value = false
  } catch (error: any) {
    automationError.value = error.message || '触发刷新失败'
    toast.error(error.message || '触发刷新失败')
    isRefreshing.value = false
  }
}
</script>
