<template>
  <div class="page-content">
    <div class="mb-5 flex items-start justify-between gap-4">
      <div>
        <h2 class="text-xl font-semibold">智能体节点</h2>
        <p class="text-g-500 mt-1 text-sm">登记执行节点并监控心跳；令牌只在创建或轮换时显示一次，当前版本不自动下发任务。</p>
      </div>
      <div class="flex gap-2"><ElButton @click="openRoutePreview">路由预览</ElButton><ElButton type="primary" @click="openCreate">登记节点</ElButton></div>
    </div>

    <ElAlert class="mb-4" type="info" :closable="false" title="节点每次心跳都会刷新在线状态；超过 90 秒未心跳显示为离线。路由预览只验证在线候选和稳定哈希顺序，不会触发远程执行。" />
    <ElTable v-loading="loading" :data="items" row-key="id">
      <ElTableColumn label="状态" width="95"><template #default="{ row }"><ElTag :type="statusType(row.status)">{{ statusLabel(row.status) }}</ElTag></template></ElTableColumn>
      <ElTableColumn label="节点" min-width="190"><template #default="{ row }"><div class="font-medium">{{ row.name }}</div><div class="text-g-500 text-xs">{{ row.code }}</div></template></ElTableColumn>
      <ElTableColumn label="类型 / 节点池" min-width="180"><template #default="{ row }">{{ typeLabel(row.node_type) }} · {{ row.pool_type === 'public' ? '公有' : '私有' }} / {{ row.pool_code }}</template></ElTableColumn>
      <ElTableColumn label="版本 / 能力" min-width="180"><template #default="{ row }"><div>{{ row.version || '—' }}</div><div class="text-g-500 text-xs">{{ capabilityCount(row.capabilities) }} 项能力</div></template></ElTableColumn>
      <ElTableColumn label="最近心跳" min-width="180"><template #default="{ row }"><div>{{ formatTime(row.last_heartbeat_at) }}</div><div v-if="row.last_heartbeat_ip" class="text-g-500 text-xs">{{ row.last_heartbeat_ip }}</div></template></ElTableColumn>
      <ElTableColumn label="Endpoint" prop="endpoint" min-width="180" show-overflow-tooltip />
      <ElTableColumn label="操作" width="250" fixed="right"><template #default="{ row }"><ElButton link type="primary" @click="openEdit(row)">编辑</ElButton><ElButton link type="warning" @click="rotate(row)">轮换令牌</ElButton><ElButton link type="danger" @click="remove(row)">删除</ElButton></template></ElTableColumn>
    </ElTable>

    <ElDialog v-model="routePreviewVisible" title="节点池路由预览" width="820px" destroy-on-close>
      <ElAlert class="mb-4" type="warning" :closable="false" title="这是只读验证：系统会按节点池、能力和 90 秒在线窗口筛选候选，再用 request key 做稳定哈希选出首节点；不会向节点发送任务。" />
      <ElForm :model="routeForm" label-width="112px">
        <div class="grid grid-cols-2 gap-4"><ElFormItem label="节点池"><ElSelect v-model="routeForm.pool_type" class="w-full"><ElOption label="私有池" value="private" /><ElOption label="公有池" value="public" /></ElSelect></ElFormItem><ElFormItem label="池编码"><ElInput v-model="routeForm.pool_code" maxlength="64" /></ElFormItem></div>
        <ElFormItem label="Request key"><ElInput v-model="routeForm.request_key" maxlength="512" placeholder="同一请求 key 会稳定命中同一排序" /></ElFormItem>
        <ElFormItem label="所需能力"><ElInput v-model="routeForm.capabilities" placeholder="多个能力用英文逗号分隔，例如 tool_exec,browser" /></ElFormItem>
      </ElForm>
      <div v-if="routePreviewResult" class="mt-2">
        <ElAlert v-if="!routePreviewResult.selected" class="mb-4" type="warning" :closable="false" :title="routeReasonLabel(routePreviewResult.reason)" />
        <div v-else class="mb-4 flex items-center justify-between rounded border border-primary/30 bg-primary/5 px-4 py-3"><div><div class="text-xs text-g-500">本次预览首选节点</div><div class="font-medium">{{ routePreviewResult.selected.name }} <span class="text-g-500 text-xs">{{ routePreviewResult.selected.code }}</span></div></div><ElTag type="success">在线 / 已选</ElTag></div>
        <div class="mb-2 text-sm text-g-500">候选节点 {{ routePreviewResult.candidates.length }} 个 · 策略 {{ routePreviewResult.selection_policy }}</div>
        <ElTable :data="routePreviewResult.candidates" max-height="260" row-key="id"><ElTableColumn label="顺序" width="70"><template #default="{ $index }">{{ $index + 1 }}</template></ElTableColumn><ElTableColumn label="节点" min-width="190"><template #default="{ row }"><div class="font-medium">{{ row.name }}</div><div class="text-g-500 text-xs">{{ row.code }}</div></template></ElTableColumn><ElTableColumn label="类型 / 节点池" min-width="180"><template #default="{ row }">{{ typeLabel(row.node_type) }} · {{ row.pool_type === 'public' ? '公有' : '私有' }} / {{ row.pool_code }}</template></ElTableColumn><ElTableColumn label="版本" prop="version" min-width="120" /><ElTableColumn label="最近心跳" min-width="170"><template #default="{ row }">{{ formatTime(row.last_heartbeat_at) }}</template></ElTableColumn></ElTable>
      </div>
      <template #footer><ElButton @click="routePreviewVisible = false">关闭</ElButton><ElButton type="primary" :loading="routePreviewing" @click="runRoutePreview">开始预览</ElButton></template>
    </ElDialog>

    <ElDialog v-model="dialogVisible" :title="editingID ? '编辑智能体节点' : '登记智能体节点'" width="680px" destroy-on-close>
      <ElForm ref="formRef" :model="form" :rules="rules" label-width="110px">
        <ElFormItem label="节点编码" prop="code"><ElInput v-model="form.code" :disabled="!!editingID" maxlength="128" placeholder="agent-worker-01" /></ElFormItem>
        <ElFormItem label="节点名称" prop="name"><ElInput v-model="form.name" maxlength="128" /></ElFormItem>
        <ElFormItem label="节点类型"><ElSelect v-model="form.node_type" class="w-full"><ElOption label="执行 Worker" value="worker" /><ElOption label="网关节点" value="gateway" /><ElOption label="执行器" value="executor" /></ElSelect></ElFormItem>
        <div class="grid grid-cols-2 gap-4"><ElFormItem label="节点池"><ElSelect v-model="form.pool_type" class="w-full"><ElOption label="私有池" value="private" /><ElOption label="公有池" value="public" /></ElSelect></ElFormItem><ElFormItem label="池编码"><ElInput v-model="form.pool_code" maxlength="64" /></ElFormItem></div>
        <ElFormItem label="Endpoint"><ElInput v-model="form.endpoint" maxlength="512" placeholder="可选，仅作登记信息" /></ElFormItem>
        <ElFormItem label="描述"><ElInput v-model="form.description" type="textarea" :rows="3" maxlength="4000" show-word-limit /></ElFormItem>
        <ElFormItem label="启用"><ElSwitch v-model="form.enabled" /></ElFormItem>
      </ElForm>
      <template #footer><ElButton @click="dialogVisible = false">取消</ElButton><ElButton type="primary" :loading="saving" @click="save">保存</ElButton></template>
    </ElDialog>

    <ElDialog v-model="tokenVisible" title="保存节点令牌" width="620px">
      <ElAlert class="mb-4" type="warning" :closable="false" title="令牌只显示这一次；关闭后无法恢复，只能重新轮换。" />
      <ElInput v-model="tokenText" readonly><template #append><ElButton @click="copyToken">复制</ElButton></template></ElInput>
    </ElDialog>
  </div>
</template>

<script setup lang="ts">
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
import { createAgentNode, deleteAgentNode, fetchAgentNodes, previewAgentNodeRoute, rotateAgentNodeToken, updateAgentNode, type AgentNode, type AgentNodeInput, type AgentNodeRoutePreview } from '@/api/agent-nodes'

const items = ref<AgentNode[]>([])
const loading = ref(false)
const saving = ref(false)
const dialogVisible = ref(false)
const tokenVisible = ref(false)
const tokenText = ref('')
const editingID = ref('')
const routePreviewVisible = ref(false)
const routePreviewing = ref(false)
const routePreviewResult = ref<AgentNodeRoutePreview>()
const formRef = ref<FormInstance>()
const form = reactive<AgentNodeInput>({ code: '', name: '', description: '', endpoint: '', node_type: 'worker', pool_type: 'private', pool_code: 'default', enabled: true })
const routeForm = reactive({ pool_type: 'public' as AgentNode['pool_type'], pool_code: 'shared', request_key: 'preview-request-1', capabilities: '' })
const rules: FormRules = { code: [{ required: true, message: '请输入节点编码', trigger: 'blur' }], name: [{ required: true, message: '请输入节点名称', trigger: 'blur' }] }

async function load() { loading.value = true; try { items.value = await fetchAgentNodes() } finally { loading.value = false } }
function reset() { Object.assign(form, { code: '', name: '', description: '', endpoint: '', node_type: 'worker', pool_type: 'private', pool_code: 'default', enabled: true }) }
function openCreate() { editingID.value = ''; reset(); dialogVisible.value = true }
function openEdit(row: AgentNode) { editingID.value = row.id; Object.assign(form, { code: row.code, name: row.name, description: row.description, endpoint: row.endpoint, node_type: row.node_type, pool_type: row.pool_type, pool_code: row.pool_code, enabled: row.enabled }); dialogVisible.value = true }
function openRoutePreview() { routePreviewResult.value = undefined; routePreviewVisible.value = true }
async function runRoutePreview() { routePreviewing.value = true; try { routePreviewResult.value = await previewAgentNodeRoute({ pool_type: routeForm.pool_type, pool_code: routeForm.pool_code, request_key: routeForm.request_key, required_capabilities: routeForm.capabilities.split(',').map(value => value.trim()).filter(Boolean) }) } finally { routePreviewing.value = false } }
async function save() { if (!(await formRef.value?.validate())) return; saving.value = true; try { const result = editingID.value ? await updateAgentNode(editingID.value, form) : await createAgentNode(form); dialogVisible.value = false; if (!editingID.value && 'token' in result) { tokenText.value = result.token; tokenVisible.value = true } ElMessage.success('节点已保存'); await load() } finally { saving.value = false } }
async function rotate(row: AgentNode) { await ElMessageBox.confirm(`轮换节点“${row.name}”令牌后旧令牌会立即失效，继续？`, '轮换节点令牌', { type: 'warning' }); const result = await rotateAgentNodeToken(row.id); tokenText.value = result.token; tokenVisible.value = true; await load() }
async function remove(row: AgentNode) { await ElMessageBox.confirm(`确认删除节点“${row.name}”？删除后该节点令牌立即失效。`, '删除智能体节点', { type: 'warning' }); await deleteAgentNode(row.id); ElMessage.success('节点已删除'); await load() }
async function copyToken() { await navigator.clipboard.writeText(tokenText.value); ElMessage.success('令牌已复制') }
function formatTime(value?: string) { return value ? new Date(value).toLocaleString() : '尚未心跳' }
function statusLabel(value: string) { return ({ pending: '待上线', online: '在线', offline: '离线', disabled: '已停用' } as Record<string, string>)[value] ?? value }
function statusType(value: string) { return value === 'online' ? 'success' : value === 'offline' ? 'danger' : value === 'disabled' ? 'info' : 'warning' }
function typeLabel(value: string) { return ({ worker: 'Worker', gateway: 'Gateway', executor: 'Executor' } as Record<string, string>)[value] ?? value }
function capabilityCount(value: Record<string, unknown>) { return Object.keys(value || {}).length }
function routeReasonLabel(value: AgentNodeRoutePreview['reason']) { return value === 'no_online_node' ? '该节点池暂无在线节点' : value === 'no_capable_node' ? '在线节点均不满足所需能力' : '已找到可用节点' }
onMounted(load)
</script>
