/*
 * SPEC: PJ2026-010405 云端控制台；PJ2026-010103 HWPOD 服务。
 * Implementation reference: draft-2026-07-13-p0-cloud-console。
 * Responsibility: 验证 HWPOD typed topology 的状态、脱敏、筛选与双向 cursor。
 */
import assert from "node:assert/strict";
import { test } from "bun:test";

import { buildHwpodTopologyReadModel } from "./hwpod-topology-read-model.ts";

test("typed topology covers multi-device, offline, unconfigured, invalid, mismatch, and busy states", () => {
  const specs = [
    spec("hwpod-a", "node-a", ["workspace.ls"], true),
    spec("hwpod-b", "node-a", ["workspace.ls", "debug.download"], true),
    spec("hwpod-c", "node-c", ["workspace.ls"], null),
    { ok: false, status: "invalid", specPath: "/registry/broken.yaml", error: { code: "invalid_hwpod_spec", message: "missing targetDevice" } }
  ];
  const registry = {
    mode: "hwpod-node-outbound-native-ws",
    pendingCount: 1,
    nodes: [
      {
        nodeId: "node-a",
        name: "D601 Python Node",
        status: "online",
        platform: "win32",
        version: "0.1.2-python-gui",
        runtimeKind: "python-tkinter-gui",
        installed: true,
        desktopVisible: true,
        capabilities: ["workspace.ls", "node.health"],
        inFlightCount: 1,
        maxInFlight: 1,
        lastDiagnostic: { level: "ERROR", source: "ws", message: "connect failed: Bearer private-token?token=still-secret" },
        latestOperation: { requestId: "req_busy", planId: "plan_busy", hwpodId: "hwpod-a", status: "running", opCount: 1, startedAt: "2026-07-13T00:00:00.000Z" }
      },
      { nodeId: "node-b", name: "Online without spec", status: "online", capabilities: [], inFlightCount: 0, maxInFlight: 1 }
    ]
  };

  const devicePayload = buildHwpodTopologyReadModel(specs, registry, { resource: "device", observedAt: "2026-07-13T01:00:00.000Z" });
  assert.equal(devicePayload.contractVersion, "hwpod-topology-v1");
  assert.equal(devicePayload.valuesRedacted, true);
  assert.equal(devicePayload.summary.nodeCount, 3);
  assert.equal(devicePayload.summary.deviceCount, 3);
  assert.equal(devicePayload.summary.invalidSpecCount, 1);
  const deviceA = devicePayload.items.find((item: any) => item.hwpodId === "hwpod-a");
  const deviceB = devicePayload.items.find((item: any) => item.hwpodId === "hwpod-b");
  assert.equal(deviceA.status, "online");
  assert.equal(deviceA.busy, true);
  assert.equal(deviceA.available, false);
  assert.equal(deviceB.status, "online");
  assert.equal(deviceB.capabilityMismatch, true);
  assert.deepEqual(deviceB.missingCapabilities, ["debug.download"]);
  assert.deepEqual(deviceB.capabilityCoverage, { matched: 1, declared: 2, actual: 2, missing: 1, extra: 1 });
  assert.equal(deviceB.blockers.some((item: any) => item.code === "hwpod_capability_mismatch"), true);
  assert.equal(devicePayload.items.find((item: any) => item.hwpodId === "hwpod-c").status, "offline");

  const nodePayload = buildHwpodTopologyReadModel(specs, registry, { resource: "node", observedAt: "2026-07-13T01:00:00.000Z" });
  const nodeA = nodePayload.items.find((item: any) => item.nodeId === "node-a");
  assert.equal(nodeA.deviceCount, 2);
  assert.equal(nodeA.status, "online");
  assert.equal(nodeA.inFlight.busy, true);
  assert.deepEqual(nodeA.capabilityCoverage, { matched: 1, declared: 2, actual: 2, missing: 1, extra: 1 });
  assert.equal(nodeA.diagnostic.message.includes("private-token"), false);
  assert.equal(nodeA.diagnostic.message.includes("still-secret"), false);
  assert.equal(nodeA.diagnostic.message.includes("[REDACTED]"), true);
  assert.equal(nodeA.diagnostic.valuesRedacted, true);
  assert.equal(nodeA.readiness.find((stage: any) => stage.id === "desktop-visible").status, "ready");
  assert.equal(nodeA.readiness.find((stage: any) => stage.id === "desktop-visible").nextAction, null);
  assert.equal(nodeA.readiness.find((stage: any) => stage.id === "hwpod-ready").nextAction.entrypoint, "hwpod-topology");
  assert.equal(nodePayload.items.find((item: any) => item.nodeId === "node-b").readiness.find((stage: any) => stage.id === "workspace-ready").blocker.code, "hwpod_node_spec_missing");
  assert.equal(nodePayload.items.find((item: any) => item.nodeId === "node-b").status, "online");
  assert.equal(nodePayload.summary.unconfiguredNodeCount, 1);
  assert.equal(nodePayload.items.find((item: any) => item.nodeId === "node-c").status, "offline");
  assert.equal(nodePayload.invalidSpecs[0].blocker.code, "invalid_hwpod_spec");
});

test("typed topology keeps server-side status filtering and cursor bounded", () => {
  const specs = [spec("hwpod-a", "node-a", ["workspace.ls"], true), spec("hwpod-b", "node-b", ["workspace.ls"], true)];
  const registry = {
    nodes: [
      { nodeId: "node-a", status: "online", capabilities: ["workspace.ls"], inFlightCount: 0, maxInFlight: 1 },
      { nodeId: "node-b", status: "online", capabilities: ["workspace.ls"], inFlightCount: 0, maxInFlight: 1 }
    ]
  };
  const first = buildHwpodTopologyReadModel(specs, registry, { resource: "device", status: "online", limit: 1 });
  assert.equal(first.items.length, 1);
  assert.equal(first.page.total, 2);
  assert.ok(first.page.nextCursor);
  const second = buildHwpodTopologyReadModel(specs, registry, { resource: "device", status: "online", limit: 1, cursor: first.page.nextCursor });
  assert.equal(second.items.length, 1);
  assert.notEqual(first.items[0].hwpodId, second.items[0].hwpodId);
  assert.equal(second.page.previousCursor, "b2Zmc2V0OjA");
});

function spec(hwpodId: string, nodeId: string, capabilities: string[], available: boolean | null) {
  return {
    ok: true,
    hwpodId,
    name: hwpodId,
    uid: hwpodId.toUpperCase(),
    nodeId,
    authority: "test-registry",
    specPath: `/registry/${hwpodId}.yaml`,
    availability: { ok: available, status: available === true ? "available" : available === false ? "blocked" : "unprobed", checkedAt: null },
    document: {
      kind: "Hwpod",
      metadata: { name: hwpodId },
      spec: {
        capabilities,
        nodeBinding: { nodeId },
        targetDevice: { board: `${hwpodId} board`, mcu: "STM32" },
        workspace: { path: `F:\\Work\\${hwpodId}`, toolchain: "keil-mdk" },
        debugProbe: { type: "cmsis-dap", probeName: "MicroLink" },
        ioProbe: { uart: { id: "uart/1", port: "COM4" } }
      }
    }
  };
}
