<template>
  <v-container fluid class="pa-4 pa-md-6">
    <StatCardsRow :items="statItems" />

    <!-- Server Table -->
    <v-card elevation="0" rounded="lg" class="my-5" border>
      <v-card-title class="d-flex align-center">
        服务器列表
        <v-spacer />
          <v-text-field
          v-model="search"
          prepend-inner-icon="mdi-magnify"
          label="搜索..."
          density="compact"
          hide-details
          rounded
          style="max-width: 240px"
          variant="outlined"
          @update:model-value="onSearch"
        />
        <v-btn color="primary" variant="flat" class="ml-3" prepend-icon="mdi-plus" size="small" @click="openAddServer">
          添加
        </v-btn>
        <v-btn variant="tonal" class="ml-2" prepend-icon="mdi-flash" size="small" @click="openQuickAdd">
          快速添加(IP)
        </v-btn>
        <v-btn variant="tonal" class="ml-2" prepend-icon="mdi-download" size="small" @click="exportCSV">
          导出CSV
        </v-btn>
        <v-btn variant="tonal" class="ml-2" prepend-icon="mdi-file-import" size="small" @click="showImport = true">
          导入
        </v-btn>
        <v-btn
          variant="tonal"
          class="ml-2"
          prepend-icon="mdi-folder-search"
          size="small"
          :disabled="selectedIds.size === 0"
          title="请先勾选至少一台服务器"
          @click="openDetectPathConfirm"
        >
          检测路径
        </v-btn>
      </v-card-title>

      <!-- Batch Actions -->
      <v-card v-if="selectedIds.size > 0" class="mx-4 mb-2" color="primary" variant="tonal" rounded="lg">
        <v-card-text class="d-flex align-center py-2">
          <span class="text-body-2 mr-4">已选择 {{ selectedIds.size }} 台服务器</span>
          <v-spacer />
          <v-btn size="small" variant="text" @click="batchHealthCheck">健康检查</v-btn>
          <v-btn size="small" variant="text" @click="openDetectPathConfirm">检测目标路径</v-btn>
          <v-btn size="small" variant="text" @click="batchInstallAgent" :loading="batchAgentLoading">安装Agent</v-btn>
          <v-btn size="small" variant="text" @click="batchUpgradeAgent" :loading="batchAgentLoading">升级Agent</v-btn>
          <v-btn size="small" variant="text" color="error" @click="batchUninstallAgent" :loading="batchAgentLoading">卸载Agent</v-btn>
          <v-btn size="small" variant="text" color="error" @click="confirmBatchDelete">批量删除</v-btn>
          <v-btn size="small" variant="text" @click="selectedItems = []">取消选择</v-btn>
        </v-card-text>
      </v-card>

      <v-data-table-server
        :items="servers"
        :headers="headers"
        :items-length="total"
        :loading="loading"
        :page="page"
        :items-per-page="itemsPerPage"
        hover
        density="comfortable"
        item-value="id"
        show-select
        v-model="selectedItems"
        @update:page="onPageChange"
        @update:items-per-page="onItemsPerPageChange"
        @click:row="(_e: Event, { item }: { item: ServerApiItem }) => showDetail(item)"
      >
        <template #item.status="{ item }">
          <v-chip :color="statusChipColor(item.status)" size="x-small" variant="tonal" label border="sm">
            {{ statusLabel(item.status) }}
          </v-chip>
        </template>

        <template #item.name="{ item }">
          <span class="font-weight-medium">{{ item.name }}</span>
        </template>

        <template #item.address="{ item }">
          <span class="text-medium-emphasis">{{ item.domain }}:{{ item.port }}</span>
        </template>

        <template #item.category="{ item }">
          <v-chip v-if="item.category" color="primary" variant="tonal" size="small" label>
            {{ item.category }}
          </v-chip>
          <span v-else class="text-medium-emphasis">—</span>
        </template>

        <template #item.target_path="{ item }">
          <div v-if="editingPathId === item.id" class="d-flex align-center ga-1" @click.stop>
            <v-text-field
              v-model="editingPathValue"
              density="compact"
              variant="outlined"
              hide-details
              placeholder="/www/wwwroot"
              style="max-width: 140px"
              @keyup.enter="saveTargetPath(item.id)"
              @keyup.esc="cancelEditPath"
            />
            <v-btn icon="mdi-check" size="x-small" variant="text" color="primary" :loading="savingPathId === item.id" @click="saveTargetPath(item.id)" />
            <v-btn icon="mdi-close" size="x-small" variant="text" @click="cancelEditPath" />
          </div>
          <div v-else class="d-flex align-center ga-1">
            <span
              class="text-caption text-medium-emphasis text-truncate d-inline-block"
              style="max-width: 140px"
              :title="item.target_path || '未配置'"
            >
              {{ item.target_path?.trim() || '—' }}
            </span>
            <v-btn
              icon="mdi-pencil"
              size="x-small"
              variant="text"
              density="compact"
              title="编辑目标路径"
              @click.stop="startEditPath(item)"
            />
          </div>
        </template>

        <template #item.agent="{ item }">
          <span class="text-medium-emphasis">{{ item.agent_version || '—' }}</span>
        </template>

        <template #item.heartbeat="{ item }">
          <span class="text-caption text-medium-emphasis">{{ formatRelativeTime(item.last_heartbeat) }}</span>
        </template>

        <template #item.actions="{ item }">
          <div class="d-flex ga-1">
            <v-btn variant="text" size="x-small" color="primary" density="compact" @click.stop="openTerminal(item)">终端</v-btn>
            <v-btn variant="text" size="x-small" density="compact" @click.stop="openFiles(item)">文件</v-btn>
            <v-btn variant="text" size="x-small" density="compact" @click.stop="editServer(item)">编辑</v-btn>
            <v-btn variant="text" size="x-small" color="error" density="compact" @click.stop="confirmDelete(item)">删除</v-btn>
          </div>
        </template>

        <template #no-data>
          <div class="text-center text-medium-emphasis py-6">暂无服务器数据</div>
        </template>
      </v-data-table-server>
    </v-card>

    <!-- Pending / failed SSH connections -->
    <v-card elevation="0" rounded="lg" class="my-5" border>
      <v-card-title class="d-flex align-center">
        连接失败列表
        <v-chip v-if="pendingServers.length" size="small" class="ml-2" color="error" variant="tonal" label>
          {{ pendingServers.length }}
        </v-chip>
        <v-spacer />
        <v-btn variant="text" size="small" prepend-icon="mdi-refresh" :loading="pendingLoading" @click="loadPendingServers">
          刷新
        </v-btn>
      </v-card-title>
      <v-data-table
        :items="pendingServers"
        :headers="pendingHeaders"
        :loading="pendingLoading"
        hover
        density="comfortable"
        :items-per-page="10"
      >
        <template #item.address="{ item }">
          <span class="text-medium-emphasis">{{ item.domain }}:{{ item.port }}</span>
        </template>
        <template #item.last_attempt_at="{ item }">
          <span class="text-caption text-medium-emphasis">{{ formatRelativeTime(item.last_attempt_at) }}</span>
        </template>
        <template #item.last_error="{ item }">
          <span class="text-caption text-error text-truncate d-inline-block" style="max-width: 320px" :title="item.last_error">
            {{ item.last_error || '—' }}
          </span>
        </template>
        <template #item.actions="{ item }">
          <v-btn variant="text" size="x-small" color="primary" density="compact" :loading="retryingId === item.id" @click="retryPending(item)">
            重试
          </v-btn>
          <v-btn variant="text" size="x-small" color="error" density="compact" @click="confirmDeletePending(item)">删除</v-btn>
        </template>
        <template #no-data>
          <div class="text-center text-medium-emphasis py-6">暂无连接失败记录</div>
        </template>
      </v-data-table>
    </v-card>

    <!-- Server Detail Panel -->
    <v-expand-transition>
      <v-card v-if="detailServer" class="mt-4" elevation="0" border rounded="lg">
        <v-card-title class="d-flex align-center">
          <v-icon class="mr-2">mdi-information-outline</v-icon>
          {{ detailServer.name }}
          <v-chip :color="statusChipColor(detailServer.status)" size="small" variant="tonal" label class="ml-2">
            {{ statusLabel(detailServer.status) }}
          </v-chip>
          <v-spacer />
          <v-btn icon="mdi-close" size="small" variant="text" @click="detailServer = null" />
        </v-card-title>
        <v-divider />
        <v-tabs v-model="detailTab" density="compact">
          <v-tab value="info">系统信息</v-tab>
          <v-tab value="logs">同步日志</v-tab>
        </v-tabs>
        <v-tabs-window v-model="detailTab">
          <v-tabs-window-item value="info">
            <v-card-text>
              <v-row dense>
                <v-col cols="6" md="3">
                  <div class="text-caption text-medium-emphasis">CPU</div>
                  <div class="text-h6">{{ detailServer.system_info?.cpu_usage ?? '—' }}%</div>
                </v-col>
                <v-col cols="6" md="3">
                  <div class="text-caption text-medium-emphasis">内存</div>
                  <div class="text-h6">{{ detailServer.system_info?.mem_usage ?? '—' }}%</div>
                </v-col>
                <v-col cols="6" md="3">
                  <div class="text-caption text-medium-emphasis">磁盘</div>
                  <div class="text-h6">{{ detailServer.system_info?.disk_usage ?? '—' }}%</div>
                </v-col>
                <v-col cols="6" md="3">
                  <div class="text-caption text-medium-emphasis">Agent</div>
                  <div class="text-h6">{{ detailServer.agent_version || '未安装' }}</div>
                </v-col>
                <v-col cols="12" md="6">
                  <div class="text-caption text-medium-emphasis">目标路径</div>
                  <div class="text-body-1 font-weight-medium text-truncate">
                    {{ detailServer.target_path?.trim() || '未配置（推送页留空时无法推送到此机）' }}
                  </div>
                </v-col>
              </v-row>
            </v-card-text>
          </v-tabs-window-item>
          <v-tabs-window-item value="logs">
            <v-card-text>
              <div v-if="syncLogs.length === 0" class="text-medium-emphasis text-center py-4">暂无同步日志</div>
              <v-list v-else density="compact">
                <v-list-item v-for="log in syncLogs" :key="log.id" :subtitle="log.created_at" :title="`${log.source_path} → ${log.target_path}`">
                  <template #append>
                    <v-chip size="x-small" :color="log.status === 'success' ? 'green' : 'red'" variant="tonal" label>{{ log.status }}</v-chip>
                  </template>
                </v-list-item>
              </v-list>
            </v-card-text>
          </v-tabs-window-item>
        </v-tabs-window>
      </v-card>
    </v-expand-transition>

    <ServerFormDialog
      v-model="serverFormVisible"
      :form="serverForm"
      :is-editing="isEditingServer"
      :saving="serverFormSaving"
      :lookups-loading="serverFormLookupsLoading"
      :password-presets="passwordPresets"
      :ssh-key-presets="sshKeyPresets"
      :category-options="categoryOptions"
      :platforms="serverFormPlatforms"
      :nodes="serverFormNodes"
      :edit-hints="serverFormEditHints"
      :files-elevation-items="filesElevationItems"
      @save="saveServerForm"
    />

    <!-- Delete Confirm -->
    <v-dialog v-model="showDelete" max-width="400">
      <v-card border>
        <v-card-title>确认删除</v-card-title>
        <v-card-text>确定要删除服务器 <strong>{{ deletingServer?.name }}</strong> 吗？</v-card-text>
        <v-card-actions>
          <v-spacer />
          <v-btn variant="text" @click="showDelete = false">取消</v-btn>
          <v-btn color="error" variant="flat" @click="doDelete" :loading="deleting">删除</v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>

    <!-- Quick add by IP -->
    <v-dialog v-model="showQuickAdd" max-width="480" persistent>
      <v-card border>
        <v-card-title>快速添加服务器（仅 IP）</v-card-title>
        <v-card-text>
          <p class="text-body-2 text-medium-emphasis mb-3">
            将按顺序轮询凭据管理中的全部密码预设与 SSH 密钥预设尝试登录，首个成功即加入服务器列表。
          </p>
          <v-text-field
            v-model="quickAddForm.domain"
            label="IP 或域名"
            variant="outlined"
            density="compact"
            class="mb-2"
            placeholder="192.168.1.10"
            :rules="[v => !!v?.trim() || '必填']"
          />
          <v-text-field
            v-model.number="quickAddForm.port"
            label="SSH 端口"
            variant="outlined"
            density="compact"
            type="number"
            class="mb-2"
          />
          <v-text-field
            v-model="quickAddForm.name"
            label="显示名称（可选）"
            variant="outlined"
            density="compact"
            hint="留空则使用 IP"
            persistent-hint
          />
        </v-card-text>
        <v-card-actions>
          <v-spacer />
          <v-btn variant="text" :disabled="quickAddLoading" @click="showQuickAdd = false">取消</v-btn>
          <v-btn color="primary" variant="flat" :loading="quickAddLoading" @click="submitQuickAdd">开始轮询</v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>

    <!-- Poll failure detail -->
    <v-dialog v-model="showPollFailure" max-width="560">
      <v-card border>
        <v-card-title class="text-error">SSH 连接失败</v-card-title>
        <v-card-text>
          <p class="text-body-2 mb-3">已尝试全部凭据预设，均未登录成功。该服务器已加入「连接失败列表」，可稍后重试。</p>
          <v-list v-if="pollFailureErrors.length" density="compact" class="bg-grey-lighten-4 rounded">
            <v-list-item v-for="(err, i) in pollFailureErrors" :key="i">
              <v-list-item-title class="text-body-2">
                [{{ err.preset_type === 'password' ? '密码' : err.preset_type === 'system' ? '系统' : '密钥' }}] {{ err.preset_name }} ({{ err.username }})
              </v-list-item-title>
              <v-list-item-subtitle class="text-error">{{ err.error }}</v-list-item-subtitle>
            </v-list-item>
          </v-list>
          <p v-else class="text-medium-emphasis">{{ pollFailureMessage }}</p>
        </v-card-text>
        <v-card-actions>
          <v-spacer />
          <v-btn color="primary" variant="flat" @click="showPollFailure = false">知道了</v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>

    <!-- Delete pending confirm -->
    <v-dialog v-model="showDeletePending" max-width="400">
      <v-card border>
        <v-card-title>确认删除</v-card-title>
        <v-card-text>从失败列表移除 <strong>{{ deletingPending?.domain }}</strong>？</v-card-text>
        <v-card-actions>
          <v-spacer />
          <v-btn variant="text" @click="showDeletePending = false">取消</v-btn>
          <v-btn color="error" variant="flat" :loading="deletingPendingLoading" @click="doDeletePending">删除</v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>

    <!-- Detect target_path confirm -->
    <v-dialog v-model="showDetectPathConfirm" max-width="480">
      <v-card border>
        <v-card-title>自动检测目标路径</v-card-title>
        <v-card-text>
          <p class="text-body-2 mb-2">
            将对选中的 <strong>{{ selectedIds.size }}</strong> 台服务器执行 SSH 检测。
          </p>
          <p class="text-body-2 text-medium-emphasis mb-0">
            在远程 <code>/www/wwwroot</code> 下搜索 <code>workerman.bat</code>，找到后将所在目录写入各服务器的
            <strong>target_path</strong>（找到第一个即停止）。
          </p>
        </v-card-text>
        <v-card-actions>
          <v-spacer />
          <v-btn variant="text" @click="showDetectPathConfirm = false">取消</v-btn>
          <v-btn color="primary" variant="flat" @click="runBatchDetectPath">开始检测</v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>

    <BatchAgentResultDialog
      v-model="batchResultOpen"
      :title="batchResultTitle"
      :loading="batchResultLoading"
      :results="batchResultItems"
    />

    <!-- Import CSV Dialog -->
    <v-dialog v-model="showImport" max-width="500">
      <v-card border>
        <v-card-title>导入服务器</v-card-title>
        <v-card-text>
          <div class="text-body-2 text-medium-emphasis mb-3">
            上传 CSV 文件批量导入服务器。列：名称、地址（必填），SSH端口、用户名、认证方式、密码、目标路径等可选。
            <a href="/app/servers_import_template.csv" download class="text-primary">下载模板</a>
            <span class="text-caption text-medium-emphasis">（亦可登录后访问 API <code>/api/servers/import/template</code>）</span>
          </div>
          <v-file-input v-model="importFile" label="选择 CSV 文件" variant="outlined" density="compact" accept=".csv" show-size />
          <v-alert v-if="importResult" :type="importResult.failed > 0 ? 'warning' : 'success'" density="compact" variant="tonal" class="mt-3">
            成功 {{ importResult.created }}，跳过 {{ importResult.skipped }}，失败 {{ importResult.failed }}
          </v-alert>
        </v-card-text>
        <v-card-actions>
          <v-spacer />
          <v-btn variant="text" @click="showImport = false">关闭</v-btn>
          <v-btn color="primary" variant="flat" @click="doImport" :loading="importing" :disabled="!importFile?.length">开始导入</v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>
  </v-container>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, watch } from 'vue'
import { useRouter } from 'vue-router'
import { http } from '@/api'
import { useSnackbar } from '@/composables/useSnackbar'
import { useServerPagination } from '@/composables/useServerPagination'
import { statusChipColor, statusLabel, formatRelativeTime } from '@/utils/status'
import type { AddByIpResponse, PendingServerItem, PollErrorItem, ServerApiItem } from '@/types/api'
import { normalizeServerIds, type BatchAgentResultItem } from '@/utils/serverSelection'
import { formatApiError } from '@/utils/apiError'
import StatCardsRow from '@/components/StatCardsRow.vue'
import ServerFormDialog from '@/components/servers/ServerFormDialog.vue'
import BatchAgentResultDialog from '@/components/servers/BatchAgentResultDialog.vue'
import { useServerFormDialog } from '@/composables/servers/useServerFormDialog'

const snackbar = useSnackbar()
const router = useRouter()

// ── Paginated server list (composable) ──
const {
  servers, loading, page, itemsPerPage, total, search,
  loadServers: _load, onSearch, onPageChange, onItemsPerPageChange,
} = useServerPagination()

async function loadServers() {
  try {
    await _load()
  } catch {
    snackbar('加载服务器列表失败', 'error')
  }
}

const showDelete = ref(false)
const deleting = ref(false)
const deletingServer = ref<ServerApiItem | null>(null)

// ── Quick add by IP + pending failure list ──
const showQuickAdd = ref(false)
const quickAddLoading = ref(false)
const quickAddForm = ref({ domain: '', port: 22, name: '' })
const pendingServers = ref<PendingServerItem[]>([])
const pendingLoading = ref(false)
const retryingId = ref<number | null>(null)
const showPollFailure = ref(false)
const pollFailureErrors = ref<PollErrorItem[]>([])
const pollFailureMessage = ref('')
const showDeletePending = ref(false)
const deletingPending = ref<PendingServerItem | null>(null)
const deletingPendingLoading = ref(false)

const pendingHeaders = [
  { title: '名称', key: 'name' },
  { title: '地址', key: 'address' },
  { title: '尝试次数', key: 'attempts', width: 100 },
  { title: '最后错误', key: 'last_error' },
  { title: '最后尝试', key: 'last_attempt_at', width: 160 },
  { title: '操作', key: 'actions', width: 140, align: 'end' as const },
]

function openQuickAdd() {
  quickAddForm.value = { domain: '', port: 22, name: '' }
  showQuickAdd.value = true
}

function showFailureDialog(errors: PollErrorItem[], message?: string) {
  pollFailureErrors.value = errors
  pollFailureMessage.value = message || ''
  showPollFailure.value = true
}

async function loadPendingServers() {
  pendingLoading.value = true
  try {
    const res = await http.get<{ items: PendingServerItem[]; total: number }>('/servers/pending')
    pendingServers.value = res.items || []
  } catch (e: unknown) {
    pendingServers.value = []
    snackbar(formatApiError(e, '加载失败列表失败'), 'error')
  } finally {
    pendingLoading.value = false
  }
}

async function submitQuickAdd() {
  const domain = quickAddForm.value.domain.trim()
  if (!domain) {
    snackbar('请填写 IP 或域名', 'error')
    return
  }
  quickAddLoading.value = true
  try {
    const body: Record<string, unknown> = {
      domain,
      port: quickAddForm.value.port || 22,
    }
    const name = quickAddForm.value.name.trim()
    if (name) body.name = name

    const res = await http.post<AddByIpResponse>('/servers/add-by-ip', body)
    if (res.success && res.server) {
      showQuickAdd.value = false
      snackbar(`添加成功：${res.matched_preset} (${res.matched_username})`)
      loadServers()
      loadStats()
    } else {
      showQuickAdd.value = false
      showFailureDialog(res.errors || [], res.message)
      loadPendingServers()
    }
  } catch (e: unknown) {
    snackbar(formatApiError(e, '添加失败'), 'error')
  } finally {
    quickAddLoading.value = false
  }
}

async function retryPending(item: PendingServerItem) {
  retryingId.value = item.id
  try {
    const res = await http.post<AddByIpResponse>(`/servers/pending/${item.id}/retry`, {})
    if (res.success && res.server) {
      snackbar(`重试成功：${res.matched_preset} (${res.matched_username})`)
      loadServers()
      loadStats()
      loadPendingServers()
    } else {
      showFailureDialog(res.errors || [], res.message)
      loadPendingServers()
    }
  } catch (e: unknown) {
    snackbar(formatApiError(e, '重试失败'), 'error')
  } finally {
    retryingId.value = null
  }
}

function confirmDeletePending(item: PendingServerItem) {
  deletingPending.value = item
  showDeletePending.value = true
}

async function doDeletePending() {
  if (!deletingPending.value) return
  deletingPendingLoading.value = true
  try {
    await http.delete(`/servers/pending/${deletingPending.value.id}`)
    showDeletePending.value = false
    snackbar('已从失败列表移除')
    loadPendingServers()
  } catch (e: unknown) {
    snackbar(formatApiError(e, '删除失败'), 'error')
  } finally {
    deletingPendingLoading.value = false
  }
}

const {
  visible: serverFormVisible,
  form: serverForm,
  isEditing: isEditingServer,
  saving: serverFormSaving,
  lookupsLoading: serverFormLookupsLoading,
  passwordPresets,
  sshKeyPresets,
  categoryOptions,
  platforms: serverFormPlatforms,
  nodes: serverFormNodes,
  editHints: serverFormEditHints,
  filesElevationItems,
  openAdd: openAddServer,
  openEdit: openEditServerDialog,
  close: closeServerForm,
  save: saveServerForm,
} = useServerFormDialog(() => {
  loadServers()
  loadStats()
})

watch(serverFormVisible, (open, wasOpen) => {
  if (wasOpen && !open) closeServerForm()
})

// ── Batch selection (Vuetify returns IDs when item-value="id", objects when return-object) ──
const selectedItems = ref<Array<ServerApiItem | number>>([])
const selectedIds = computed(() => new Set(normalizeServerIds(selectedItems.value)))

// ── Detail panel ──
const detailServer = ref<ServerApiItem | null>(null)
const detailTab = ref('info')
const syncLogs = ref<{ id: number; source_path: string; target_path: string; status: string; created_at: string }[]>([])

// ── Table config ──
const headers = [
  { title: '状态', key: 'status', width: 100 },
  { title: '名称', key: 'name' },
  { title: '地址', key: 'address' },
  { title: '分类', key: 'category', width: 100 },
  { title: '目标路径', key: 'target_path', width: 200 },
  { title: 'Agent', key: 'agent', width: 100 },
  { title: '心跳', key: 'heartbeat', width: 120 },
  { title: '操作', key: 'actions', width: 200, align: 'end' as const },
]

// ── Stats（与仪表盘 StatCardsRow 一致）──
const statItems = ref([
  { subtitle: '服务器总数', title: '0', icon: 'mdi-server-network', color: 'blue' },
  { subtitle: '在线', title: '0', icon: 'mdi-check-circle-outline', color: 'green' },
  { subtitle: '离线', title: '0', icon: 'mdi-alert-circle-outline', color: 'orange' },
  { subtitle: '告警中', title: '0', icon: 'mdi-bell-alert-outline', color: 'red' },
])

async function loadStats() {
  try {
    const s = await http.get<{ total: number; online: number; offline: number; alerts: number }>('/servers/stats')
    statItems.value[0].title = String(s.total)
    statItems.value[1].title = String(s.online)
    statItems.value[2].title = String(s.offline)
    statItems.value[3].title = String(s.alerts ?? 0)
  } catch { /* non-critical */ }
}

// ── Handlers ──
function openTerminal(item: ServerApiItem) {
  router.push({ path: '/terminal', query: { server_id: String(item.id) } })
}
function openFiles(item: ServerApiItem) {
  router.push({ path: '/files', query: { server_id: String(item.id) } })
}

// ── Batch operations ──
const batchAgentLoading = ref(false)
const showDetectPathConfirm = ref(false)
const batchResultOpen = ref(false)
const batchResultLoading = ref(false)
const batchResultTitle = ref('')
const batchResultItems = ref<BatchAgentResultItem[]>([])

const editingPathId = ref<number | null>(null)
const editingPathValue = ref('')
const savingPathId = ref<number | null>(null)

function openBatchResultDialog(title: string) {
  batchResultTitle.value = title
  batchResultItems.value = []
  batchResultLoading.value = true
  batchResultOpen.value = true
}

function finishBatchResultDialog(results: BatchAgentResultItem[]) {
  batchResultItems.value = results
  batchResultLoading.value = false
}

async function runBatchAgentOp(
  title: string,
  path: string,
  opts?: { confirm?: string; reload?: boolean },
) {
  const ids = normalizeServerIds(selectedItems.value)
  if (!ids.length) {
    snackbar('请先选择服务器', 'warning')
    return
  }
  if (opts?.confirm && !confirm(opts.confirm.replace('{n}', String(ids.length)))) return

  openBatchResultDialog(title)
  try {
    const res = await http.post<{ results: BatchAgentResultItem[] }>(path, { server_ids: ids })
    finishBatchResultDialog(res.results || [])
    if (opts?.reload !== false) loadServers()
  } catch (e: unknown) {
    batchResultOpen.value = false
    snackbar(formatApiError(e, '操作失败'), 'error')
  }
}

function startEditPath(item: ServerApiItem) {
  editingPathId.value = item.id
  editingPathValue.value = item.target_path?.trim() || '/www/wwwroot'
}

function cancelEditPath() {
  editingPathId.value = null
  editingPathValue.value = ''
  savingPathId.value = null
}

async function saveTargetPath(serverId: number) {
  const tp = editingPathValue.value.trim()
  if (!tp) {
    snackbar('目标路径不能为空', 'warning')
    return
  }
  if (!tp.startsWith('/')) {
    snackbar('目标路径须为绝对路径（以 / 开头）', 'warning')
    return
  }
  savingPathId.value = serverId
  try {
    await http.put(`/servers/${serverId}`, { target_path: tp })
    snackbar('目标路径已更新')
    cancelEditPath()
    loadServers()
    if (detailServer.value?.id === serverId) {
      detailServer.value = { ...detailServer.value, target_path: tp }
    }
  } catch (e: unknown) {
    snackbar(formatApiError(e, '保存失败'), 'error')
  } finally {
    savingPathId.value = null
  }
}

function openDetectPathConfirm() {
  if (!selectedIds.value.size) {
    snackbar('请先选择服务器', 'warning')
    return
  }
  showDetectPathConfirm.value = true
}

async function runBatchDetectPath() {
  showDetectPathConfirm.value = false
  await runBatchAgentOp('自动检测目标路径', '/servers/batch/detect-path')
}

async function batchHealthCheck() {
  const ids = normalizeServerIds(selectedItems.value)
  if (!ids.length) return
  snackbar(`正在检查 ${ids.length} 台服务器...`)
  try {
    await http.post('/servers/check', { server_ids: ids })
    snackbar('健康检查完成')
    loadServers()
  } catch (e: unknown) { snackbar(formatApiError(e, '检查失败'), 'error') }
}

async function confirmBatchDelete() {
  if (!confirm(`确定删除选中的 ${selectedIds.value.size} 台服务器？`)) return
  for (const id of selectedIds.value) {
    try { await http.delete(`/servers/${id}`) } catch { /* skip individual errors */ }
  }
  selectedItems.value = []
  snackbar('批量删除完成')
  loadServers()
  loadStats()
}

async function batchInstallAgent() {
  batchAgentLoading.value = true
  try {
    await runBatchAgentOp('批量安装 Agent', '/servers/batch/install-agent', { reload: true })
  } finally {
    batchAgentLoading.value = false
  }
}

async function batchUpgradeAgent() {
  batchAgentLoading.value = true
  try {
    await runBatchAgentOp(
      '批量升级 Agent',
      '/servers/batch/upgrade-agent',
      { confirm: '确定对 {n} 台服务器升级 Agent？', reload: true },
    )
  } finally {
    batchAgentLoading.value = false
  }
}

async function batchUninstallAgent() {
  batchAgentLoading.value = true
  try {
    await runBatchAgentOp(
      '批量卸载 Agent',
      '/servers/batch/uninstall-agent',
      {
        confirm:
          '确定对 {n} 台已安装 Agent 的服务器执行卸载？\n将停止服务、删除 /opt/nexus-agent 并清除 Agent 状态。',
        reload: true,
      },
    )
  } finally {
    batchAgentLoading.value = false
  }
}

// ── CSV export ──
async function exportCSV() {
  try {
    const res = await http.getList<ServerApiItem>('/servers/', { per_page: 5000 })
    const headers = ['ID', '名称', '地址', '端口', '用户名', '分类', '状态', 'Agent版本', '最后心跳']
    const rows = (res.items || []).map(s => [
      s.id, s.name, s.domain, s.port, s.username, s.category || '', s.status,
      s.agent_version || '', s.last_heartbeat || '',
    ])
    const bom = '﻿'
    const csv = bom + [headers.join(','), ...rows.map(r => r.map(v => `"${String(v).replace(/"/g, '""')}"`).join(','))].join('\n')
    const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' })
    const url = URL.createObjectURL(blob)
    const a = document.createElement('a')
    a.href = url
    a.download = `servers_${new Date().toISOString().slice(0, 10)}.csv`
    a.click()
    URL.revokeObjectURL(url)
    snackbar('导出完成')
  } catch (e: any) { snackbar(e.message || '导出失败', 'error') }
}

// ── CSV import ──
const showImport = ref(false)
const importFile = ref<File[]>([])
const importing = ref(false)
const importResult = ref<{ created: number; skipped: number; failed: number } | null>(null)

async function doImport() {
  if (!importFile.value?.length) return
  importing.value = true
  importResult.value = null
  try {
    const form = new FormData()
    form.append('file', importFile.value[0])
    const res = await http.upload<{ created: number; skipped: number; failed: number }>('/servers/import', form)
    importResult.value = res
    snackbar('导入完成')
    loadServers()
  } catch (e: unknown) {
    const msg = e instanceof Error ? e.message : '导入失败'
    snackbar(msg, 'error')
  } finally {
    importing.value = false
  }
}

// ── Server detail panel ──
async function showDetail(item: ServerApiItem) {
  detailServer.value = item
  detailTab.value = 'info'
  try {
    const res = await http.get<{ items: typeof syncLogs.value }>(`/servers/${item.id}/logs`, { limit: 20 })
    syncLogs.value = res.items || []
  } catch { syncLogs.value = [] }
}

function editServer(item: ServerApiItem) {
  void openEditServerDialog(item)
}

function confirmDelete(item: ServerApiItem) {
  deletingServer.value = item
  showDelete.value = true
}

async function doDelete() {
  if (!deletingServer.value) return
  deleting.value = true
  try {
    await http.delete(`/servers/${deletingServer.value.id}`)
    showDelete.value = false
    loadServers()
    loadStats()
    snackbar('已删除')
  } catch (e: any) {
    snackbar(e.message || '删除失败', 'error')
  } finally {
    deleting.value = false
  }
}

// ── Init ──
onMounted(() => {
  loadStats()
  loadServers()
  loadPendingServers()
})
</script>
