<!-- SPEC: HWPOD Nodes topology and readiness. -->
<!-- Implementation reference: MDTODO R2 / PJ2026-010405 云端控制台. -->
<!-- Responsibility: 展示已连接 Node、Node 下多个 HWPOD 与 readiness。 -->

<script setup lang="ts">
import DataGrid, { type DataGridColumn } from "@/components/common/DataGrid.vue";
import ResourceCollection from "@/components/common/ResourceCollection.vue";
import type { HwpodNodeReadinessStage, HwpodNodeTopologyItem } from "@/types";
import HwpodReadinessRail from "./HwpodReadinessRail.vue";

defineProps<{ nodes: HwpodNodeTopologyItem[]; selectedId: string | null; view: "cards" | "list"; density: "compact" | "comfortable"; sort: string }>();
const emit = defineEmits<{ select: [node: HwpodNodeTopologyItem]; sort: [key: string] }>();
const columns: DataGridColumn[] = [{ key: "name", label: "Node", sortable: true, width: "210px" }, { key: "status", label: "连接", sortable: true, width: "100px" }, { key: "deviceCount", label: "HWPOD", align: "right", width: "80px" }, { key: "inFlight", label: "In-flight", width: "100px" }, { key: "capabilities", label: "能力", width: "130px" }, { key: "mounted", label: "挂载 HWPOD", width: "220px" }, { key: "readiness", label: "接入阶段", width: "180px" }, { key: "lastSeen", label: "最后心跳", width: "150px" }, { key: "blocker", label: "Blocker", width: "220px" }];
const statusLabel = (status: string): string => ({ online: "在线", offline: "离线" }[status] || status || "unknown");
function formatTime(value: string | null): string { if (!value) return "-"; const date = new Date(value); return Number.isNaN(date.getTime()) ? value : date.toLocaleString(); }
</script>

<template>
  <section class="resource-stage">
    <ResourceCollection
      v-if="view === 'cards'"
      class="node-collection"
      :items="nodes"
      :item-key="(node) => node.nodeId"
      :view="view"
      :density="density"
      :selected-id="selectedId"
      :show-controls="false"
      aria-label="HWPOD Node 卡片"
      @select="emit('select', $event)"
    >
      <template #card="{ item: node, selected, select }">
        <article
          class="node-card"
          role="group"
          :aria-label="`${node.name} HWPOD Node 卡片`"
          :aria-current="selected ? 'true' : undefined"
          :data-selected="selected ? 'true' : 'false'"
          :data-status="node.status"
          :data-testid="`hwpod-node-${node.nodeId}`"
        >
          <header>
            <div><span>{{ node.platform }} · {{ node.runtimeKind || '未识别运行时' }}</span><h2>{{ node.name }}</h2><code>{{ node.nodeId }}</code></div>
            <span class="status-chip" :data-status="node.status">{{ statusLabel(node.status) }}</span>
          </header>
          <section class="node-gauges">
            <div><small>HWPOD</small><strong>{{ node.deviceCount }}</strong></div>
            <div><small>in-flight</small><strong>{{ node.inFlight.count }}/{{ node.inFlight.max }}</strong></div>
            <div><small>capability coverage</small><strong>{{ node.capabilityCoverage.matched }}/{{ node.capabilityCoverage.declared }}</strong></div>
          </section>
          <section class="mounted-hwpods" aria-label="挂载 HWPOD">
            <header><strong>挂载 HWPOD</strong><span>{{ node.hwpods.length }}</span></header>
            <ul v-if="node.hwpods.length">
              <li v-for="device in node.hwpods" :key="device.hwpodId">
                <div><strong>{{ device.name }}</strong><code>{{ device.hwpodId }}</code></div>
                <span class="status-chip" :data-status="device.status">{{ statusLabel(device.status) }}</span>
              </li>
            </ul>
            <p v-else>节点在线，但 owning registry 尚未绑定 HWPOD spec。</p>
          </section>
          <HwpodReadinessRail :stages="node.readiness" />
          <footer>
            <span>最后心跳 {{ formatTime(node.lastSeenAt) }}</span>
            <strong v-if="node.blockers[0]">{{ node.blockers[0].code }}</strong>
            <button type="button" :aria-pressed="selected" @click="select">
              {{ selected ? '已选择' : '查看详情' }}
            </button>
          </footer>
        </article>
      </template>
    </ResourceCollection>
    <DataGrid
      v-else
      :columns="columns"
      :rows="nodes"
      :row-key="(node) => node.nodeId"
      :selected-id="selectedId"
      :density="density"
      :sort="sort"
      aria-label="HWPOD Node 列表"
      @select="emit('select', $event)"
      @sort="emit('sort', $event)"
    >
      <template #cell-name="{ row }"><span class="grid-stack"><strong>{{ row.name }}</strong><code>{{ row.nodeId }}</code><small>{{ row.platform }} · {{ row.runtimeKind || '未识别运行时' }}</small></span></template>
      <template #cell-status="{ row }"><span class="status-chip" :data-status="row.status">{{ statusLabel(row.status) }}</span></template>
      <template #cell-deviceCount="{ row }"><strong>{{ row.deviceCount }}</strong></template>
      <template #cell-inFlight="{ row }"><strong>{{ row.inFlight.count }}/{{ row.inFlight.max }}</strong></template>
      <template #cell-capabilities="{ row }"><span class="grid-stack"><strong>覆盖 {{ row.capabilityCoverage.matched }}/{{ row.capabilityCoverage.declared }}</strong><small>上报 {{ row.capabilityCoverage.actual }} · 缺 {{ row.capabilityCoverage.missing }} · 额外 {{ row.capabilityCoverage.extra }}</small></span></template>
      <template #cell-mounted="{ row }"><span class="grid-stack"><strong>{{ row.hwpods.map((item) => item.name).join(', ') || '无' }}</strong><small>{{ row.hwpods.map((item) => item.hwpodId).join(', ') || '未绑定 spec' }}</small></span></template>
      <template #cell-readiness="{ row }"><span class="grid-stack"><strong>{{ row.readiness.filter((stage: HwpodNodeReadinessStage) => stage.status === 'ready').length }}/{{ row.readiness.length }} ready</strong><small>{{ row.readiness.find((stage: HwpodNodeReadinessStage) => stage.status !== 'ready')?.label || '全部阶段 ready' }}</small></span></template>
      <template #cell-lastSeen="{ row }">{{ formatTime(row.lastSeenAt) }}</template>
      <template #cell-blocker="{ row }"><span class="grid-stack"><strong>{{ row.blockers[0]?.code || '无' }}</strong><small>{{ row.blockers[0]?.summary || '当前可操作' }}</small></span></template>
    </DataGrid>
  </section>
</template>

<style scoped>
.node-card { min-width: 0; padding: 18px; border: 1px solid #c1cbc7; border-top: 3px solid #3a766a; border-radius: 8px; background: #fbfaf6; box-shadow: 0 10px 26px rgb(19 42 36 / 7%); transition: border-color 140ms ease, box-shadow 140ms ease, transform 140ms ease; }
.node-card[data-status="offline"] { border-top-color: #8d453b; }
.node-card[data-selected="true"] { border-color: #1f7469; border-top-color: #145f57; box-shadow: 0 0 0 3px rgb(31 116 105 / 18%), 0 15px 32px rgb(20 55 48 / 13%); transform: translateY(-1px); }
.node-card > header { display: flex; min-width: 0; align-items: flex-start; justify-content: space-between; gap: 14px; }
.node-card > header div { min-width: 0; }
.node-card > header span:first-child { color: #71807b; font-size: .7rem; font-weight: 800; text-transform: uppercase; }
.node-card h2 { margin: 4px 0 2px; color: #1d302b; font-size: 1.08rem; overflow-wrap: anywhere; }
.node-card header code { color: #6d7a75; font-size: .69rem; overflow-wrap: anywhere; }
.node-gauges { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; margin: 15px 0; }
.node-gauges div { padding: 9px 10px; border: 1px solid #d4ddd9; border-radius: 6px; background: #f2f6f3; }
.node-gauges small, .node-gauges strong { display: block; }
.node-gauges small { color: #71807b; font-size: .65rem; font-weight: 800; text-transform: uppercase; }
.node-gauges strong { margin-top: 3px; color: #1e4b42; font: 800 .85rem ui-monospace, SFMono-Regular, Consolas, monospace; }
.mounted-hwpods { border: 1px solid #d2dad7; border-radius: 7px; overflow: hidden; }
.mounted-hwpods > header { display: flex; justify-content: space-between; padding: 8px 10px; background: #edf3f0; color: #3d554e; font-size: .73rem; }
.mounted-hwpods ul { margin: 0; padding: 0; list-style: none; }
.mounted-hwpods li { display: flex; min-width: 0; align-items: center; justify-content: space-between; gap: 8px; padding: 8px 10px; border-top: 1px solid #e0e5e3; }
.mounted-hwpods li div { min-width: 0; }
.mounted-hwpods li strong, .mounted-hwpods li code { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mounted-hwpods li strong { color: #2b403a; font-size: .76rem; }
.mounted-hwpods li code { margin-top: 2px; color: #71807b; font-size: .66rem; }
.mounted-hwpods p { margin: 0; padding: 10px; color: #765b25; font-size: .74rem; line-height: 1.4; }
.node-card footer { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-top: 10px; padding-top: 12px; border-top: 1px solid #d9dfdc; color: #687570; font-size: .72rem; }
.node-card footer strong { color: #8a4c2d; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; }
.node-card footer button { margin-left: auto; border: 1px solid var(--console-cyan-700); border-radius: var(--console-radius-sm); background: var(--console-cyan-100); color: var(--console-cyan-700); padding: 6px 9px; font-size: 11px; font-weight: 750; cursor: pointer; }
.node-card[data-selected="true"] footer button { border-color: #145f57; background: #dff2ed; color: #145f57; }
@media (prefers-reduced-motion: reduce) { .node-card { transition: none; } .node-card[data-selected="true"] { transform: none; } }
@media (max-width: 680px) { .node-gauges { grid-template-columns: 1fr; } .node-card > header, .node-card footer { align-items: flex-start; flex-direction: column; } .node-card footer button { margin-left: 0; } }
</style>
