<!--
  SPEC: PJ2026-010405 云端控制台。
  Implementation reference: draft-2026-07-13-p0-cloud-console.
  Responsibility: 将 typed HWPOD 四要素状态投影为有界 PCB 卡片，并遵守 reduced-motion。
-->
<script setup lang="ts">
import { computed } from "vue";
import type { HwpodDeviceTopologyItem } from "@/types";

const props = withDefaults(
  defineProps<{ device: HwpodDeviceTopologyItem; selected?: boolean }>(),
  { selected: false },
);
const emit = defineEmits<{ select: [id: string] }>();

const statusLabel = computed(() => ({
  online: "在线",
  offline: "节点离线",
}[props.device.status] ?? props.device.status));

function linkState(kind: "target" | "workspace" | "debug" | "io"): string {
  if (!props.device.online) return "offline";
  if (kind === "workspace" && props.device.availability.ok === false) return "blocked";
  const prefix = kind === "debug" ? "debug." : kind === "io" ? "io." : "";
  if (prefix && props.device.missingCapabilities.some((capability) => capability.startsWith(prefix))) return "mismatch";
  if (props.device.busy) return "active";
  return props.device.available ? "ready" : "pending";
}
</script>

<template>
  <article
    class="pcb-card"
    role="group"
    :aria-label="`${device.name} HWPOD 设备卡片`"
    :aria-current="selected ? 'true' : undefined"
    :data-selected="selected ? 'true' : 'false'"
    :data-status="device.status"
    :data-testid="`hwpod-device-${device.hwpodId}`"
  >
    <header class="pcb-card__header">
      <div>
        <span class="pcb-card__eyebrow">{{ device.nodeName }}</span>
        <h2>{{ device.name }}</h2>
      </div>
      <span class="pcb-status" :data-status="device.status"><i aria-hidden="true" />{{ statusLabel }}</span>
    </header>

    <svg class="pcb-map" viewBox="0 0 520 220" role="img" :aria-label="`${device.name} HWPOD 四要素状态图`">
      <defs>
        <pattern :id="`grid-${device.hwpodId}`" width="16" height="16" patternUnits="userSpaceOnUse">
          <path d="M 16 0 L 0 0 0 16" class="pcb-grid-line" />
        </pattern>
      </defs>
      <rect x="8" y="8" width="504" height="204" rx="20" class="pcb-board" />
      <rect x="8" y="8" width="504" height="204" rx="20" :fill="`url(#grid-${device.hwpodId})`" />
      <path d="M260 110 H128 V56 H84" class="pcb-trace" :data-state="linkState('target')" />
      <path d="M260 110 H128 V164 H84" class="pcb-trace" :data-state="linkState('workspace')" />
      <path d="M260 110 H392 V56 H436" class="pcb-trace" :data-state="linkState('debug')" />
      <path d="M260 110 H392 V164 H436" class="pcb-trace" :data-state="linkState('io')" />
      <rect x="210" y="76" width="100" height="68" rx="12" class="pcb-chip" />
      <text x="260" y="104" text-anchor="middle" class="pcb-chip-label">HWPOD</text>
      <text x="260" y="124" text-anchor="middle" class="pcb-chip-id">{{ device.hwpodId.slice(0, 18) }}</text>
      <g class="pcb-pad" :data-state="linkState('target')"><circle cx="66" cy="56" r="20" /><text x="66" y="61" text-anchor="middle">TGT</text></g>
      <g class="pcb-pad" :data-state="linkState('workspace')"><circle cx="66" cy="164" r="20" /><text x="66" y="169" text-anchor="middle">WS</text></g>
      <g class="pcb-pad" :data-state="linkState('debug')"><circle cx="454" cy="56" r="20" /><text x="454" y="61" text-anchor="middle">DBG</text></g>
      <g class="pcb-pad" :data-state="linkState('io')"><circle cx="454" cy="164" r="20" /><text x="454" y="169" text-anchor="middle">IO</text></g>
    </svg>

    <dl class="element-grid">
      <div><dt>目标板</dt><dd>{{ device.elements.target.label }}</dd><small>{{ device.elements.target.mcu || '未声明 MCU' }}</small></div>
      <div><dt>工作区</dt><dd>{{ device.elements.workspace.label }}</dd><small>{{ device.elements.workspace.toolchain || '未声明工具链' }}</small></div>
      <div><dt>调试探头</dt><dd>{{ device.elements.debugProbe.label }}</dd><small>{{ device.elements.debugProbe.type || '未声明类型' }}</small></div>
      <div><dt>IO 探头</dt><dd>{{ device.elements.ioProbe.label }}</dd><small>{{ device.elements.ioProbe.kind }}</small></div>
    </dl>

    <p v-if="device.blockers[0]" class="pcb-blocker"><strong>{{ device.blockers[0].code }}</strong>{{ device.blockers[0].summary }}</p>
    <footer>
      <span>能力覆盖 {{ device.capabilityCoverage.matched }}/{{ device.capabilityCoverage.declared }} · 上报 {{ device.capabilityCoverage.actual }}</span>
      <span v-if="device.operation">{{ device.operation.status }} · {{ device.operation.opCount }} ops</span>
      <button
        type="button"
        :aria-pressed="selected"
        @click="emit('select', device.hwpodId)"
      >{{ selected ? '已选择' : '查看详情' }}</button>
    </footer>
  </article>
</template>

<style scoped>
.pcb-card { position: relative; min-width: 0; border: 1px solid #cbd2cf; border-top: 3px solid #2e6b67; border-radius: 8px; background: #fbfaf6; box-shadow: 0 12px 28px rgb(20 34 32 / 8%); overflow: hidden; transition: border-color 140ms ease, box-shadow 140ms ease, transform 140ms ease; }
.pcb-card[data-selected="true"] { border-color: #1f7469; border-top-color: #145f57; box-shadow: 0 0 0 3px rgb(31 116 105 / 18%), 0 16px 34px rgb(20 55 48 / 14%); transform: translateY(-1px); }
.pcb-card[data-selected="true"] footer button { border-color: #145f57; background: #dff2ed; color: #145f57; }
.pcb-card[data-status="offline"] { border-top-color: #8d453b; }
.pcb-card__header { display: flex; justify-content: space-between; gap: 16px; align-items: flex-start; padding: 18px 20px 12px; }
.pcb-card__header h2 { margin: 3px 0 0; color: #152421; font-size: 1.08rem; letter-spacing: 0; overflow-wrap: anywhere; }
.pcb-card__eyebrow { color: #687572; font: 600 .72rem/1.2 ui-monospace, SFMono-Regular, Consolas, monospace; text-transform: uppercase; }
.pcb-status { display: inline-flex; align-items: center; gap: 7px; flex: 0 0 auto; padding: 5px 9px; border: 1px solid #b7c6c1; border-radius: 999px; color: #244943; background: #eef7f2; font-size: .76rem; font-weight: 700; }
.pcb-status i { width: 7px; height: 7px; border-radius: 50%; background: currentcolor; }
.pcb-status[data-status="offline"] { color: #7b332c; background: #fff0ed; border-color: #ddb2ac; }
.pcb-map { display: block; width: calc(100% - 28px); max-height: 220px; margin: 0 14px; }
.pcb-board { fill: #142c28; stroke: #31514a; stroke-width: 2; }
.pcb-grid-line { fill: none; stroke: rgb(137 184 168 / 9%); stroke-width: 1; }
.pcb-trace { fill: none; stroke: #789087; stroke-width: 4; stroke-linecap: round; stroke-linejoin: round; }
.pcb-trace[data-state="ready"] { stroke: #50c5a4; }
.pcb-trace[data-state="active"] { stroke: #efad51; stroke-dasharray: 8 8; animation: trace-flow .75s linear infinite; }
.pcb-trace[data-state="mismatch"] { stroke: #e5ca54; stroke-dasharray: 5 6; }
.pcb-trace[data-state="blocked"], .pcb-trace[data-state="offline"] { stroke: #bf675d; }
.pcb-chip { fill: #0d1917; stroke: #7ea398; stroke-width: 2; }
.pcb-chip-label { fill: #eef9f4; font: 700 15px/1 ui-monospace, SFMono-Regular, Consolas, monospace; letter-spacing: 0; }
.pcb-chip-id { fill: #91aaa2; font: 10px/1 ui-monospace, SFMono-Regular, Consolas, monospace; }
.pcb-pad circle { fill: #213d37; stroke: #789087; stroke-width: 3; }
.pcb-pad text { fill: #d7e5df; font: 700 10px/1 ui-monospace, SFMono-Regular, Consolas, monospace; }
.pcb-pad[data-state="ready"] circle { stroke: #50c5a4; }
.pcb-pad[data-state="active"] circle { stroke: #efad51; }
.pcb-pad[data-state="mismatch"] circle { stroke: #e5ca54; }
.pcb-pad[data-state="blocked"] circle, .pcb-pad[data-state="offline"] circle { stroke: #bf675d; }
.element-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1px; margin: 12px 18px; border: 1px solid #d6dcda; background: #d6dcda; }
.element-grid div { min-width: 0; padding: 10px 12px; background: #f7f6f1; }
.element-grid dt { color: #73807c; font-size: .67rem; font-weight: 800; letter-spacing: 0; text-transform: uppercase; }
.element-grid dd { margin: 4px 0 2px; color: #172b27; font-size: .84rem; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.element-grid small { display: block; color: #6b7673; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pcb-blocker { margin: 12px 18px; padding: 10px 12px; border-left: 3px solid #aa5d37; background: #fff4e9; color: #5f3a29; font-size: .78rem; line-height: 1.45; }
.pcb-blocker strong { display: block; margin-bottom: 3px; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; }
footer { display: flex; align-items: center; gap: 12px; padding: 12px 18px 16px; color: #697571; font-size: .74rem; }
footer button { margin-left: auto; border: 1px solid #446c63; border-radius: 7px; background: #183d36; color: #f4fbf8; padding: 7px 11px; font-weight: 700; cursor: pointer; }
@keyframes trace-flow { to { stroke-dashoffset: -16; } }
@media (prefers-reduced-motion: reduce) { .pcb-card { transition: none; } .pcb-card[data-selected="true"] { transform: none; } .pcb-trace { animation: none !important; } }
@media (max-width: 560px) { .element-grid { grid-template-columns: 1fr; } .pcb-card__header { padding-inline: 14px; } footer { align-items: flex-start; flex-wrap: wrap; } }
</style>
