// 规格：PJ2026-010104 draft-2026-07-10-g14-wsl-python-hwpod-node。
// 职责：通过 YAML-first 管理 trans 可达的 Windows Python HWPOD 节点部署和状态。
import { createHash, randomBytes } from "node:crypto";
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { dirname, isAbsolute, join } from "node:path";
import { repoRoot, rootPath } from "./config";
import { runCommand, type CommandResult } from "./command";

type HwpodNodeAction = "plan" | "status" | "apply";
type HwpodNodeComponent = "all" | "cloud-auth" | "desktop";

interface HwpodNodeOptions {
  action: HwpodNodeAction;
  component: HwpodNodeComponent;
  node: string;
  lane: string;
  confirm: boolean;
  dryRun: boolean;
}

interface SecretSpec {
  sourceRef: string;
  sourceKey: string;
  generateIfMissing: boolean;
  secretName: string;
  targetKey: string;
}

interface SourceSpec {
  sourceId: string;
  projectId: string;
  hwpodId: string;
  nodeId: string;
  workspaceRootRef: string;
  mdtodoRootRef: string;
  capabilities: Record<string, boolean>;
}

interface HwpodNodeSpec {
  configPath: string;
  node: string;
  lane: string;
  providerId: string;
  windowsRoute: string;
  nodeId: string;
  hwpodId: string;
  interactiveUser: string;
  publicOrigin: string;
  nodeOpsUrl: string;
  cloudKubeRoute: string;
  cloudNamespace: string;
  cloudSecret: SecretSpec;
  opsAuth: { sourceRef: string; sourceKey: string };
  python: {
    probeLauncherPath: string;
    guiLauncherPath: string;
    launcherArgs: string[];
    expectedVersionPrefix: string;
  };
  runtime: {
    root: string;
    scriptPath: string;
    configPath: string;
    credentialPath: string;
    logPath: string;
    applyTimeoutMs: number;
  };
  artifact: { metadataUrl: string; channel: string; platform: string; localPath: string | null; localVersion: string | null };
  workspace: { allowedRoots: string[]; defaultRoot: string };
  desktopConfig: {
    serverUrl: string;
    autoConnect: boolean;
    requireCredential: boolean;
    updateEnabled: boolean;
    updateAutoApplyWhenIdle: boolean;
  };
  startup: {
    mode: string;
    runKeyName: string;
    initialLaunch: string;
    processPattern: string;
    legacyTaskNames: string[];
    legacyRunKeyNames: string[];
    legacyProcessPatterns: string[];
  };
  sourceRef: string;
  source: SourceSpec;
}

interface ArtifactBundle {
  latestVersion: string;
  downloadUrl: string;
  sha256: string;
  content: Buffer;
}

const CONFIG_PATH = "config/hwlab-hwpod-nodes.yaml";
const SECRET_ROOT = rootPath(".state", "secrets");

export function hwlabHwpodNodeHelp(): Record<string, unknown> {
  return {
    ok: true,
    command: "hwlab nodes hwpod-node",
    description: "通过 YAML 和 trans 部署、查询 Windows 原生 Python HWPOD 节点。",
    configPath: CONFIG_PATH,
    actions: {
      plan: "校验 YAML、SecretRef、发布文件元数据和 Windows 路由，不修改运行面。",
      status: "查询 Windows 文件、解释器、交互会话、启动项、进程和云端节点操作状态。",
      apply: "显式确认后同步云端认证 Secret 或部署 Windows Python 图形节点。",
    },
    components: {
      "cloud-auth": "生成缺失的 YAML 声明凭据，并同步 cloud-api 使用的 Kubernetes Secret。",
      desktop: "校验发布文件 SHA 后部署桌面节点、配置、凭据和 HKCU Run 启动项。",
      all: "按 cloud-auth、desktop 顺序执行。",
    },
    examples: [
      "bun scripts/cli.ts hwlab nodes hwpod-node plan --node G14-WSL --lane v03",
      "bun scripts/cli.ts hwlab nodes hwpod-node status --node G14-WSL --lane v03",
      "bun scripts/cli.ts hwlab nodes hwpod-node apply --node G14-WSL --lane v03 --component cloud-auth --confirm",
      "bun scripts/cli.ts hwlab nodes hwpod-node apply --node G14-WSL --lane v03 --component desktop --confirm",
    ],
    valuesRedacted: true,
  };
}

export async function runHwlabHwpodNodeCommand(args: string[]): Promise<Record<string, unknown>> {
  if (args.length === 0 || args.includes("--help") || args.includes("-h") || args[0] === "help") return hwlabHwpodNodeHelp();
  const options = parseOptions(args);
  const spec = readSpec(options.node, options.lane);
  const artifact = await readArtifact(spec, false);
  const secretBefore = readSecretMaterial(spec.cloudSecret);
  const remoteBefore = remoteStatus(spec);
  const graph = configGraph(spec, secretBefore, artifact, remoteBefore);
  if (options.action === "plan") return resultEnvelope(options, spec, graph, null, null, null);
  const cloudBefore = await cloudProbe(spec);
  if (options.action === "status") return resultEnvelope(options, spec, graph, remoteBefore.payload, cloudBefore, null);
  if (options.dryRun) return resultEnvelope(options, spec, graph, remoteBefore.payload, cloudBefore, { status: "dry-run", component: options.component });
  if (!options.confirm) throw new Error("hwpod-node apply 必须显式使用 --confirm 或 --dry-run");
  let cloudApply: Record<string, unknown> | null = null;
  let desktopApply: Record<string, unknown> | null = null;
  if (options.component === "all" || options.component === "cloud-auth") cloudApply = applyCloudAuth(spec);
  if (options.component === "all" || options.component === "desktop") {
    const secret = readSecretMaterial(spec.cloudSecret);
    if (!secret.present || secret.value === null) throw new Error(`${spec.cloudSecret.sourceRef}.${spec.cloudSecret.sourceKey} 缺失；先应用 cloud-auth`);
    const fullArtifact = await readArtifact(spec, true);
    desktopApply = applyDesktop(spec, fullArtifact, secret.value);
  }
  await delay(2500);
  const remoteAfter = remoteStatus(spec);
  const cloudAfter = await cloudProbe(spec);
  const applyResult = { status: "applied", component: options.component, cloudAuth: cloudApply, desktop: desktopApply, valuesRedacted: true };
  return resultEnvelope(options, spec, configGraph(spec, readSecretMaterial(spec.cloudSecret), artifact, remoteAfter), remoteAfter.payload, cloudAfter, applyResult);
}

function parseOptions(args: string[]): HwpodNodeOptions {
  const action = args[0];
  if (action !== "plan" && action !== "status" && action !== "apply") {
    throw new Error("hwpod-node 用法：plan|status|apply --node NODE --lane LANE [--component all|cloud-auth|desktop] [--confirm|--dry-run]");
  }
  const knownValue = new Set(["--node", "--lane", "--component"]);
  const knownFlag = new Set(["--confirm", "--dry-run", "--full"]);
  for (let index = 1; index < args.length; index += 1) {
    const item = args[index];
    if (knownValue.has(item)) {
      index += 1;
      if (args[index] === undefined) throw new Error(`${item} 缺少值`);
      continue;
    }
    if (!knownFlag.has(item)) throw new Error(`hwpod-node 不支持参数 ${item}`);
  }
  const node = option(args, "--node");
  const lane = option(args, "--lane");
  const componentRaw = option(args, "--component", "all");
  if (componentRaw !== "all" && componentRaw !== "cloud-auth" && componentRaw !== "desktop") throw new Error(`--component 不支持 ${componentRaw}`);
  const confirm = args.includes("--confirm");
  const dryRun = args.includes("--dry-run");
  if (confirm && dryRun) throw new Error("--confirm 与 --dry-run 不能同时使用");
  if (action !== "apply" && (confirm || dryRun)) throw new Error(`${action} 是只读动作，不接受 --confirm 或 --dry-run`);
  return { action, component: componentRaw, node, lane, confirm, dryRun };
}

function option(args: string[], name: string, fallback?: string): string {
  const index = args.indexOf(name);
  if (index < 0) {
    if (fallback !== undefined) return fallback;
    throw new Error(`缺少 ${name}`);
  }
  const value = args[index + 1];
  if (!value || value.startsWith("--")) throw new Error(`${name} 缺少值`);
  return value;
}

function readSpec(node: string, lane: string): HwpodNodeSpec {
  const config = yamlFile(CONFIG_PATH);
  const cloud = record(config.cloud, `${CONFIG_PATH}#cloud`);
  const targets = record(config.targets, `${CONFIG_PATH}#targets`);
  const target = record(targets[node], `${CONFIG_PATH}#targets.${node}`);
  if (target.enabled !== true) throw new Error(`${CONFIG_PATH}#targets.${node}.enabled 必须为 true`);
  const targetLane = text(target.lane, `targets.${node}.lane`);
  if (targetLane !== lane) throw new Error(`目标 ${node} 声明 lane=${targetLane}，不是 ${lane}`);
  const python = record(target.python, `targets.${node}.python`);
  const runtime = record(target.runtime, `targets.${node}.runtime`);
  const artifact = record(target.artifact, `targets.${node}.artifact`);
  const workspace = record(target.workspace, `targets.${node}.workspace`);
  const desktopConfig = record(target.desktopConfig, `targets.${node}.desktopConfig`);
  const startup = record(target.startup, `targets.${node}.startup`);
  const nodeAuth = record(cloud.nodeAuth, "cloud.nodeAuth");
  const opsAuth = record(cloud.opsAuth, "cloud.opsAuth");
  const sourceRef = text(target.projectManagementSourceRef, `targets.${node}.projectManagementSourceRef`);
  const source = readSource(sourceRef);
  const allowedRoots = texts(workspace.allowedRoots, `targets.${node}.workspace.allowedRoots`);
  if (allowedRoots.length === 0) throw new Error(`targets.${node}.workspace.allowedRoots 不能为空`);
  const spec: HwpodNodeSpec = {
    configPath: CONFIG_PATH,
    node,
    lane,
    providerId: text(target.providerId, `targets.${node}.providerId`),
    windowsRoute: text(target.windowsRoute, `targets.${node}.windowsRoute`),
    nodeId: text(target.nodeId, `targets.${node}.nodeId`),
    hwpodId: text(target.hwpodId, `targets.${node}.hwpodId`),
    interactiveUser: text(target.interactiveUser, `targets.${node}.interactiveUser`),
    publicOrigin: text(cloud.publicOrigin, "cloud.publicOrigin"),
    nodeOpsUrl: text(cloud.nodeOpsUrl, "cloud.nodeOpsUrl"),
    cloudKubeRoute: text(cloud.kubeRoute, "cloud.kubeRoute"),
    cloudNamespace: text(cloud.namespace, "cloud.namespace"),
    cloudSecret: {
      sourceRef: text(nodeAuth.sourceRef, "cloud.nodeAuth.sourceRef"),
      sourceKey: text(nodeAuth.sourceKey, "cloud.nodeAuth.sourceKey"),
      generateIfMissing: bool(nodeAuth.generateIfMissing, "cloud.nodeAuth.generateIfMissing"),
      secretName: text(nodeAuth.secretName, "cloud.nodeAuth.secretName"),
      targetKey: text(nodeAuth.targetKey, "cloud.nodeAuth.targetKey"),
    },
    opsAuth: { sourceRef: text(opsAuth.sourceRef, "cloud.opsAuth.sourceRef"), sourceKey: text(opsAuth.sourceKey, "cloud.opsAuth.sourceKey") },
    python: {
      probeLauncherPath: text(python.probeLauncherPath, `targets.${node}.python.probeLauncherPath`),
      guiLauncherPath: text(python.guiLauncherPath, `targets.${node}.python.guiLauncherPath`),
      launcherArgs: texts(python.launcherArgs, `targets.${node}.python.launcherArgs`),
      expectedVersionPrefix: text(python.expectedVersionPrefix, `targets.${node}.python.expectedVersionPrefix`),
    },
    runtime: {
      root: text(runtime.root, `targets.${node}.runtime.root`),
      scriptPath: text(runtime.scriptPath, `targets.${node}.runtime.scriptPath`),
      configPath: text(runtime.configPath, `targets.${node}.runtime.configPath`),
      credentialPath: text(runtime.credentialPath, `targets.${node}.runtime.credentialPath`),
      logPath: text(runtime.logPath, `targets.${node}.runtime.logPath`),
      applyTimeoutMs: positiveInteger(runtime.applyTimeoutMs, `targets.${node}.runtime.applyTimeoutMs`),
    },
    artifact: {
      metadataUrl: text(artifact.metadataUrl, `targets.${node}.artifact.metadataUrl`),
      channel: text(artifact.channel, `targets.${node}.artifact.channel`),
      platform: text(artifact.platform, `targets.${node}.artifact.platform`),
      localPath: optionalText(artifact.localPath),
      localVersion: optionalText(artifact.localVersion),
    },
    workspace: { allowedRoots, defaultRoot: text(workspace.defaultRoot, `targets.${node}.workspace.defaultRoot`) },
    desktopConfig: {
      serverUrl: text(desktopConfig.serverUrl, `targets.${node}.desktopConfig.serverUrl`),
      autoConnect: bool(desktopConfig.autoConnect, `targets.${node}.desktopConfig.autoConnect`),
      requireCredential: bool(desktopConfig.requireCredential, `targets.${node}.desktopConfig.requireCredential`),
      updateEnabled: bool(desktopConfig.updateEnabled, `targets.${node}.desktopConfig.updateEnabled`),
      updateAutoApplyWhenIdle: bool(desktopConfig.updateAutoApplyWhenIdle, `targets.${node}.desktopConfig.updateAutoApplyWhenIdle`),
    },
    startup: {
      mode: text(startup.mode, `targets.${node}.startup.mode`),
      runKeyName: text(startup.runKeyName, `targets.${node}.startup.runKeyName`),
      initialLaunch: text(startup.initialLaunch, `targets.${node}.startup.initialLaunch`),
      processPattern: text(startup.processPattern, `targets.${node}.startup.processPattern`),
      legacyTaskNames: startup.legacyTaskNames === undefined ? [] : texts(startup.legacyTaskNames, `targets.${node}.startup.legacyTaskNames`),
      legacyRunKeyNames: startup.legacyRunKeyNames === undefined ? [] : texts(startup.legacyRunKeyNames, `targets.${node}.startup.legacyRunKeyNames`),
      legacyProcessPatterns: startup.legacyProcessPatterns === undefined ? [] : texts(startup.legacyProcessPatterns, `targets.${node}.startup.legacyProcessPatterns`),
    },
    sourceRef,
    source,
  };
  validateCrossRefs(spec);
  return spec;
}

function readSource(ref: string): SourceSpec {
  const parsed = configRef(ref);
  const doc = yamlFile(parsed.file);
  const value = pathValue(doc, parsed.path);
  const source = record(value, ref);
  const capabilitiesRaw = record(source.capabilities, `${ref}.capabilities`);
  return {
    sourceId: text(source.sourceId, `${ref}.sourceId`),
    projectId: text(source.projectId, `${ref}.projectId`),
    hwpodId: text(source.hwpodId, `${ref}.hwpodId`),
    nodeId: text(source.nodeId, `${ref}.nodeId`),
    workspaceRootRef: text(source.workspaceRootRef, `${ref}.workspaceRootRef`),
    mdtodoRootRef: text(source.mdtodoRootRef, `${ref}.mdtodoRootRef`),
    capabilities: Object.fromEntries(Object.entries(capabilitiesRaw).map(([key, value]) => [key, bool(value, `${ref}.capabilities.${key}`)])),
  };
}

function validateCrossRefs(spec: HwpodNodeSpec): void {
  const conflicts: string[] = [];
  if (spec.source.nodeId !== spec.nodeId) conflicts.push(`source.nodeId=${spec.source.nodeId} != target.nodeId=${spec.nodeId}`);
  if (spec.source.hwpodId !== spec.hwpodId) conflicts.push(`source.hwpodId=${spec.source.hwpodId} != target.hwpodId=${spec.hwpodId}`);
  if (!spec.workspace.allowedRoots.includes(spec.workspace.defaultRoot)) conflicts.push("workspace.defaultRoot 不在 allowedRoots 中");
  if (spec.source.workspaceRootRef !== spec.workspace.defaultRoot) conflicts.push("source.workspaceRootRef 与 workspace.defaultRoot 不一致");
  if (spec.startup.mode !== "hkcu-run") conflicts.push(`startup.mode 仅支持 hkcu-run，实际为 ${spec.startup.mode}`);
  if (!/:win(?:\/[a-zA-Z](?:\/.*)?)?$/u.test(spec.windowsRoute)) conflicts.push("windowsRoute 必须指向 :win plane，并可附带显式 Windows cwd");
  if (conflicts.length > 0) throw new Error(`HWPOD 节点配置冲突：${conflicts.join("；")}`);
}

function configGraph(spec: HwpodNodeSpec, secret: ReturnType<typeof readSecretMaterial>, artifact: ArtifactBundle, remote: ReturnType<typeof remoteStatus>): Record<string, unknown> {
  return {
    ok: remote.reachable && artifact.sha256.length > 0 && (secret.present || spec.cloudSecret.generateIfMissing),
    configPath: spec.configPath,
    sourceRef: spec.sourceRef,
    target: {
      node: spec.node,
      lane: spec.lane,
      providerId: spec.providerId,
      windowsRoute: spec.windowsRoute,
      nodeId: spec.nodeId,
      hwpodId: spec.hwpodId,
      interactiveUser: spec.interactiveUser,
      workspaceRoots: spec.workspace.allowedRoots,
      mdtodoRootRef: spec.source.mdtodoRootRef,
    },
    cloudAuth: {
      sourceRef: spec.cloudSecret.sourceRef,
      sourceKey: spec.cloudSecret.sourceKey,
      sourcePresent: secret.present,
      fingerprint: secret.fingerprint,
      secretName: spec.cloudSecret.secretName,
      targetKey: spec.cloudSecret.targetKey,
      generateIfMissing: spec.cloudSecret.generateIfMissing,
      valuesRedacted: true,
    },
    artifact: { latestVersion: artifact.latestVersion, downloadUrl: artifact.downloadUrl, sha256: artifact.sha256, bytes: artifact.content.length },
    route: { reachable: remote.reachable, exitCode: remote.result.exitCode, timedOut: remote.result.timedOut },
    valuesRedacted: true,
  };
}

function resultEnvelope(
  options: HwpodNodeOptions,
  spec: HwpodNodeSpec,
  graph: Record<string, unknown>,
  remote: Record<string, unknown> | null,
  cloud: Record<string, unknown> | null,
  apply: Record<string, unknown> | null,
): Record<string, unknown> {
  const remoteReady = remote?.installed === true && remote?.processReady === true && remote?.interactiveProcessReady === true;
  const cloudReady = cloud?.ok === true;
  const ok = options.action === "plan" ? graph.ok === true : options.action === "status" ? remoteReady && cloudReady : apply?.status === "applied" && (options.component === "cloud-auth" || (remoteReady && cloudReady));
  return {
    ok,
    command: `hwlab nodes hwpod-node ${options.action} --node ${spec.node} --lane ${spec.lane}${options.action === "apply" ? ` --component ${options.component}${options.confirm ? " --confirm" : " --dry-run"}` : ""}`,
    status: ok ? options.action === "apply" ? "applied" : "ready" : "blocked",
    graph,
    remote,
    cloud,
    apply,
    next: {
      plan: `bun scripts/cli.ts hwlab nodes hwpod-node plan --node ${spec.node} --lane ${spec.lane}`,
      status: `bun scripts/cli.ts hwlab nodes hwpod-node status --node ${spec.node} --lane ${spec.lane}`,
      cloudAuth: `bun scripts/cli.ts hwlab nodes hwpod-node apply --node ${spec.node} --lane ${spec.lane} --component cloud-auth --confirm`,
      desktop: `bun scripts/cli.ts hwlab nodes hwpod-node apply --node ${spec.node} --lane ${spec.lane} --component desktop --confirm`,
    },
    valuesRedacted: true,
  };
}

async function readArtifact(spec: HwpodNodeSpec, includeContent: boolean): Promise<ArtifactBundle> {
  if (spec.artifact.localPath) {
    if (!spec.artifact.localVersion) throw new Error(`targets.${spec.node}.artifact.localVersion 是本地 artifact 必填项`);
    if (!existsSync(spec.artifact.localPath)) throw new Error(`HWPOD 节点本地 artifact 不存在：${spec.artifact.localPath}`);
    const content = readFileSync(spec.artifact.localPath);
    const sha256 = createHash("sha256").update(content).digest("hex");
    return {
      latestVersion: spec.artifact.localVersion,
      downloadUrl: `local:${spec.artifact.localPath}`,
      sha256,
      content: includeContent ? content : Buffer.alloc(0),
    };
  }
  const metadataUrl = new URL(spec.artifact.metadataUrl);
  metadataUrl.searchParams.set("platform", spec.artifact.platform);
  metadataUrl.searchParams.set("channel", spec.artifact.channel);
  metadataUrl.searchParams.set("current", "0.0.0");
  const metadataResponse = await fetch(metadataUrl, { signal: AbortSignal.timeout(15000) });
  if (!metadataResponse.ok) throw new Error(`HWPOD 节点更新元数据返回 HTTP ${metadataResponse.status}`);
  const metadata = record(await metadataResponse.json(), "HWPOD 节点更新元数据");
  const downloadUrl = text(metadata.downloadUrl, "更新元数据.downloadUrl");
  const sha256 = text(metadata.sha256, "更新元数据.sha256").toLowerCase();
  const latestVersion = text(metadata.latestVersion, "更新元数据.latestVersion");
  const download = new URL(downloadUrl);
  if (download.hostname !== new URL(spec.publicOrigin).hostname || !download.pathname.endsWith(".py")) throw new Error("更新元数据下载地址不属于 YAML 声明的公共入口或不是 .py 文件");
  if (!includeContent) return { latestVersion, downloadUrl, sha256, content: Buffer.alloc(0) };
  const response = await fetch(download, { signal: AbortSignal.timeout(30000) });
  if (!response.ok) throw new Error(`HWPOD 节点文件下载返回 HTTP ${response.status}`);
  const content = Buffer.from(await response.arrayBuffer());
  const actual = createHash("sha256").update(content).digest("hex");
  if (actual !== sha256) throw new Error(`HWPOD 节点文件 SHA 不匹配：expected=${sha256} actual=${actual}`);
  return { latestVersion, downloadUrl, sha256, content };
}

function readSecretMaterial(spec: { sourceRef: string; sourceKey: string }): { present: boolean; value: string | null; fingerprint: string | null; sourcePath: string } {
  const sourcePath = secretPath(spec.sourceRef);
  if (!existsSync(sourcePath)) return { present: false, value: null, fingerprint: null, sourcePath };
  const values = parseEnv(readFileSync(sourcePath, "utf8"));
  const value = values[spec.sourceKey] ?? null;
  return { present: value !== null && value.length > 0, value, fingerprint: value ? sha256(value) : null, sourcePath };
}

function ensureSecretMaterial(spec: SecretSpec): { value: string; fingerprint: string; generated: boolean; sourcePath: string } {
  const current = readSecretMaterial(spec);
  if (current.present && current.value) return { value: current.value, fingerprint: current.fingerprint ?? sha256(current.value), generated: false, sourcePath: current.sourcePath };
  if (!spec.generateIfMissing) throw new Error(`${spec.sourceRef}.${spec.sourceKey} 缺失且 generateIfMissing=false`);
  const value = randomBytes(32).toString("base64url");
  const sourcePath = secretPath(spec.sourceRef);
  mkdirSync(dirname(sourcePath), { recursive: true });
  const existing = existsSync(sourcePath) ? parseEnv(readFileSync(sourcePath, "utf8")) : {};
  existing[spec.sourceKey] = value;
  writeFileSync(sourcePath, `${Object.entries(existing).map(([key, item]) => `${key}=${item}`).join("\n")}\n`, { encoding: "utf8", mode: 0o600 });
  chmodSync(sourcePath, 0o600);
  return { value, fingerprint: sha256(value), generated: true, sourcePath };
}

function applyCloudAuth(spec: HwpodNodeSpec): Record<string, unknown> {
  const material = ensureSecretMaterial(spec.cloudSecret);
  const manifest = JSON.stringify({
    apiVersion: "v1",
    kind: "Secret",
    metadata: { name: spec.cloudSecret.secretName, namespace: spec.cloudNamespace, labels: { "app.kubernetes.io/managed-by": "unidesk", "hwlab.pikastech.local/config": "hwpod-node-auth" } },
    type: "Opaque",
    data: { [spec.cloudSecret.targetKey]: Buffer.from(material.value, "utf8").toString("base64") },
  });
  const result = runCommand(["trans", spec.cloudKubeRoute, "kubectl", "-n", spec.cloudNamespace, "apply", "-f", "-"], repoRoot, { input: manifest, timeoutMs: 55000 });
  if (!commandSucceeded(result)) throw new Error(`同步 HWPOD 节点认证 Secret 失败：${compactFailure(result)}`);
  return {
    ok: true,
    status: "applied",
    generated: material.generated,
    sourceRef: spec.cloudSecret.sourceRef,
    sourceKey: spec.cloudSecret.sourceKey,
    fingerprint: material.fingerprint,
    secretName: spec.cloudSecret.secretName,
    targetKey: spec.cloudSecret.targetKey,
    valuesRedacted: true,
  };
}

function applyDesktop(spec: HwpodNodeSpec, artifact: ArtifactBundle, credential: string): Record<string, unknown> {
  const config = {
    schemaVersion: 1,
    serverUrl: spec.desktopConfig.serverUrl,
    nodeId: spec.nodeId,
    autoConnect: spec.desktopConfig.autoConnect,
    requireCredential: spec.desktopConfig.requireCredential,
    credentialFile: spec.runtime.credentialPath,
    allowedWorkspaceRoots: spec.workspace.allowedRoots,
    defaultWorkspaceRoot: spec.workspace.defaultRoot,
    update: {
      enabled: spec.desktopConfig.updateEnabled,
      autoApplyWhenIdle: spec.desktopConfig.updateAutoApplyWhenIdle,
      channel: spec.artifact.channel,
      metadataUrl: spec.artifact.metadataUrl,
      downloadHostAllowlist: [new URL(spec.publicOrigin).hostname],
    },
  };
  const payload = {
    interactiveUser: spec.interactiveUser,
    runtimeRoot: spec.runtime.root,
    scriptPath: spec.runtime.scriptPath,
    configPath: spec.runtime.configPath,
    credentialPath: spec.runtime.credentialPath,
    logPath: spec.runtime.logPath,
    probeLauncherPath: spec.python.probeLauncherPath,
    guiLauncherPath: spec.python.guiLauncherPath,
    launcherArgs: spec.python.launcherArgs,
    expectedVersionPrefix: spec.python.expectedVersionPrefix,
    runKeyName: spec.startup.runKeyName,
    legacyTaskNames: spec.startup.legacyTaskNames,
    legacyRunKeyNames: spec.startup.legacyRunKeyNames,
    legacyProcessPatterns: spec.startup.legacyProcessPatterns,
    artifactBase64: artifact.content.toString("base64"),
    artifactSha256: artifact.sha256,
    configBase64: Buffer.from(`${JSON.stringify(config, null, 2)}\n`, "utf8").toString("base64"),
    configSha256: sha256Hex(`${JSON.stringify(config, null, 2)}\n`),
    credentialBase64: Buffer.from(`${credential}\n`, "utf8").toString("base64"),
  };
  const result = runPowerShell(spec.windowsRoute, desktopApplyScript(payload), spec.runtime.applyTimeoutMs);
  if (!commandSucceeded(result)) throw new Error(`部署 Windows HWPOD 节点失败：${compactFailure(result)}`);
  const parsed = parseJsonRecord(result.stdout);
  if (parsed?.ok !== true) throw new Error(`Windows HWPOD 节点部署未就绪：${JSON.stringify(parsed ?? { stdout: result.stdout.slice(-600) })}`);
  return { ...parsed, artifactVersion: artifact.latestVersion, artifactSha256: artifact.sha256, valuesRedacted: true };
}

function remoteStatus(spec: HwpodNodeSpec): { reachable: boolean; payload: Record<string, unknown> | null; result: CommandResult } {
  const payload = {
    interactiveUser: spec.interactiveUser,
    runtimeRoot: spec.runtime.root,
    scriptPath: spec.runtime.scriptPath,
    configPath: spec.runtime.configPath,
    credentialPath: spec.runtime.credentialPath,
    logPath: spec.runtime.logPath,
    probeLauncherPath: spec.python.probeLauncherPath,
    launcherArgs: spec.python.launcherArgs,
    expectedVersionPrefix: spec.python.expectedVersionPrefix,
    runKeyName: spec.startup.runKeyName,
  };
  const result = runPowerShell(spec.windowsRoute, desktopStatusScript(payload), 45000);
  const reachable = commandSucceeded(result);
  return { reachable, payload: reachable ? parseJsonRecord(result.stdout) : null, result };
}

async function cloudProbe(spec: HwpodNodeSpec): Promise<Record<string, unknown>> {
  const auth = readSecretMaterial(spec.opsAuth);
  if (!auth.present || !auth.value) return { ok: false, status: "blocked", blocker: "ops-auth-source-missing", sourceRef: spec.opsAuth.sourceRef, valuesRedacted: true };
  const body = {
    contractVersion: "hwpod-node-ops-v1",
    planId: `hwpod_status_${Date.now()}`,
    hwpodId: spec.hwpodId,
    nodeId: spec.nodeId,
    intent: "node.version",
    ops: [{ opId: "op_status", op: "node.version", args: {} }],
  };
  try {
    const response = await fetch(spec.nodeOpsUrl, {
      method: "POST",
      headers: { "content-type": "application/json", authorization: `Bearer ${auth.value}`, "x-source-service-id": "unidesk-hwpod-node-cli" },
      body: JSON.stringify(body),
      signal: AbortSignal.timeout(15000),
    });
    const value = record(await response.json(), "node-ops status response");
    const result = Array.isArray(value.results) ? record(value.results[0], "node-ops result") : {};
    return {
      ok: response.ok && value.ok === true && result.ok === true,
      httpStatus: response.status,
      status: value.status ?? null,
      nodeId: spec.nodeId,
      nodeVersion: record(result.output ?? {}, "node-ops output").version ?? null,
      blocker: result.blocker ?? value.blocker ?? value.error ?? null,
      requestId: record(value.requestMeta ?? {}, "requestMeta").requestId ?? null,
      valuesRedacted: true,
    };
  } catch (error) {
    return { ok: false, status: "blocked", blocker: error instanceof Error ? error.message : String(error), valuesRedacted: true };
  }
}

function desktopStatusScript(payload: Record<string, unknown>): string {
  return powerShellPayload(payload, `
$ErrorActionPreference = 'Stop'
$expectedUser = [string]$payload.interactiveUser
$scriptPath = [string]$payload.scriptPath
$configPath = [string]$payload.configPath
$credentialPath = [string]$payload.credentialPath
$logPath = [string]$payload.logPath
$probe = [string]$payload.probeLauncherPath
$launcherArgs = @($payload.launcherArgs | ForEach-Object { [string]$_ })
$versionOut = @(& $probe @launcherArgs -c "import sys;print(sys.version);print(sys.executable)" 2>&1)
$versionExit = $LASTEXITCODE
$processes = @(Get-CimInstance Win32_Process | Where-Object { $_.Name -like 'python*.exe' -and $_.CommandLine -and $_.CommandLine.Contains($scriptPath) })
$explorerSessions = @(Get-Process explorer -ErrorAction SilentlyContinue | Select-Object -ExpandProperty SessionId -Unique)
$interactive = @($processes | Where-Object { $explorerSessions -contains $_.SessionId })
$runValue = (Get-ItemProperty -Path 'HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Run' -Name ([string]$payload.runKeyName) -ErrorAction SilentlyContinue).([string]$payload.runKeyName)
function HashOrNull([string]$path) { if (Test-Path $path) { return 'sha256:' + (Get-FileHash -Algorithm SHA256 $path).Hash.ToLowerInvariant() }; return $null }
$result = [ordered]@{
  ok = $true
  status = 'observed'
  user = $env:USERNAME
  expectedUser = $expectedUser
  userMatches = [bool]($env:USERNAME -ieq $expectedUser)
  pythonReady = [bool]((Test-Path $probe) -and ($versionExit -eq 0) -and (($versionOut -join "\`n") -like "*$($payload.expectedVersionPrefix)*"))
  pythonSummary = (($versionOut | Select-Object -First 2) -join ' | ')
  installed = [bool]((Test-Path $scriptPath) -and (Test-Path $configPath) -and (Test-Path $credentialPath))
  scriptSha256 = HashOrNull $scriptPath
  configSha256 = HashOrNull $configPath
  credentialPresent = [bool](Test-Path $credentialPath)
  credentialFingerprint = HashOrNull $credentialPath
  runKeyReady = [bool](-not [string]::IsNullOrWhiteSpace([string]$runValue))
  processCount = [int]$processes.Count
  processReady = [bool]($processes.Count -eq 1)
  interactiveProcessCount = [int]$interactive.Count
  interactiveProcessReady = [bool]($interactive.Count -eq 1)
  processIds = @($processes | ForEach-Object { [int]$_.ProcessId })
  explorerSessions = @($explorerSessions)
  logPresent = [bool](Test-Path $logPath)
  valuesRedacted = $true
}
$result | ConvertTo-Json -Compress -Depth 6
`);
}

function desktopApplyScript(payload: Record<string, unknown>): string {
  return powerShellPayload(payload, `
$ErrorActionPreference = 'Stop'
if ($env:USERNAME -ine [string]$payload.interactiveUser) { throw "interactive user mismatch expected=$($payload.interactiveUser) actual=$env:USERNAME" }
$root = [string]$payload.runtimeRoot
$scriptPath = [string]$payload.scriptPath
$configPath = [string]$payload.configPath
$credentialPath = [string]$payload.credentialPath
$logPath = [string]$payload.logPath
$probe = [string]$payload.probeLauncherPath
$gui = [string]$payload.guiLauncherPath
$launcherArgs = @($payload.launcherArgs | ForEach-Object { [string]$_ })
$legacyTaskNames = @($payload.legacyTaskNames | ForEach-Object { [string]$_ })
$legacyRunKeyNames = @($payload.legacyRunKeyNames | ForEach-Object { [string]$_ })
$legacyProcessPatterns = @($payload.legacyProcessPatterns | ForEach-Object { [string]$_ })
if (-not (Test-Path $probe)) { throw "Python probe launcher missing: $probe" }
if (-not (Test-Path $gui)) { throw "Python GUI launcher missing: $gui" }
$versionOut = @(& $probe @launcherArgs -c "import sys;import tkinter;print(sys.version);print(sys.executable);print(tkinter.TkVersion)" 2>&1)
if ($LASTEXITCODE -ne 0 -or (($versionOut -join "\`n") -notlike "*$($payload.expectedVersionPrefix)*")) { throw "Python version mismatch: $($versionOut -join ' | ')" }
New-Item -ItemType Directory -Force -Path $root, (Split-Path -Parent $configPath), (Split-Path -Parent $credentialPath), (Split-Path -Parent $logPath) | Out-Null
$legacyStopped = @()
$legacyTasksStillEnabled = @()
foreach ($taskName in $legacyTaskNames) {
  $null = & schtasks.exe /Query /TN $taskName 2>$null
  if ($LASTEXITCODE -eq 0) {
    $null = & schtasks.exe /End /TN $taskName 2>$null
    $null = & schtasks.exe /Change /TN $taskName /Disable 2>$null
    if ($LASTEXITCODE -ne 0) { $legacyTasksStillEnabled += $taskName }
  }
}
foreach ($runKey in $legacyRunKeyNames) {
  Remove-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Run' -Name $runKey -ErrorAction SilentlyContinue
}
$allBefore = @(Get-CimInstance Win32_Process)
$legacy = @($allBefore | Where-Object {
  $commandLine = [string]$_.CommandLine
  $legacyProcessPatterns | Where-Object { $commandLine.Contains($_) } | Select-Object -First 1
})
foreach ($proc in $legacy) {
  Stop-Process -Id $proc.ProcessId -Force -ErrorAction SilentlyContinue
  $legacyStopped += [int]$proc.ProcessId
}
Start-Sleep -Milliseconds 500
$old = @($allBefore | Where-Object { $_.CommandLine -and $_.CommandLine.Contains($scriptPath) })
foreach ($proc in $old) { Stop-Process -Id $proc.ProcessId -Force -ErrorAction SilentlyContinue }
Start-Sleep -Milliseconds 500
[IO.File]::WriteAllBytes($scriptPath, [Convert]::FromBase64String([string]$payload.artifactBase64))
[IO.File]::WriteAllBytes($configPath, [Convert]::FromBase64String([string]$payload.configBase64))
[IO.File]::WriteAllBytes($credentialPath, [Convert]::FromBase64String([string]$payload.credentialBase64))
$artifactActual = (Get-FileHash -Algorithm SHA256 $scriptPath).Hash.ToLowerInvariant()
$configActual = (Get-FileHash -Algorithm SHA256 $configPath).Hash.ToLowerInvariant()
if ($artifactActual -ne [string]$payload.artifactSha256) { throw "artifact SHA mismatch" }
if ($configActual -ne [string]$payload.configSha256) { throw "config SHA mismatch" }
$null = & icacls $credentialPath /inheritance:r /grant:r "$env:USERNAME\`:(R,W)" 2>&1
$argText = (($launcherArgs | ForEach-Object { '"' + ($_ -replace '"','\"') + '"' }) -join ' ')
$runCommand = '"' + $gui + '" ' + $argText + ' "' + $scriptPath + '"'
$null = New-Item -Path 'HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Run' -Force
Set-ItemProperty -Path 'HKCU:\\Software\\Microsoft\\Windows\\CurrentVersion\\Run' -Name ([string]$payload.runKeyName) -Value $runCommand
$stdoutPath = $logPath + '.stdout'
$stderrPath = $logPath + '.stderr'
Start-Process -FilePath $gui -ArgumentList @($launcherArgs + @($scriptPath)) -WorkingDirectory $root -RedirectStandardOutput $stdoutPath -RedirectStandardError $stderrPath
Start-Sleep -Seconds 3
$allAfter = @(Get-CimInstance Win32_Process)
$processes = @($allAfter | Where-Object { $_.Name -like 'python*.exe' -and $_.CommandLine -and $_.CommandLine.Contains($scriptPath) })
$explorerSessions = @(Get-Process explorer -ErrorAction SilentlyContinue | Select-Object -ExpandProperty SessionId -Unique)
$interactive = @($processes | Where-Object { $explorerSessions -contains $_.SessionId })
$legacyProcessesRemaining = @($allAfter | Where-Object {
  $commandLine = [string]$_.CommandLine
  $legacyProcessPatterns | Where-Object { $commandLine.Contains($_) } | Select-Object -First 1
})
$ready = [bool](($processes.Count -eq 1) -and ($interactive.Count -eq 1) -and ($legacyTasksStillEnabled.Count -eq 0) -and ($legacyProcessesRemaining.Count -eq 0))
$result = [ordered]@{
  ok = $ready
  status = $(if ($ready) { 'applied' } elseif (($processes.Count -eq 1) -and ($interactive.Count -eq 1)) { 'legacy-authority-not-retired' } else { 'installed-not-interactive' })
  pythonSummary = (($versionOut | Select-Object -First 3) -join ' | ')
  scriptSha256 = 'sha256:' + $artifactActual
  configSha256 = 'sha256:' + $configActual
  credentialPresent = $true
  processCount = [int]$processes.Count
  interactiveProcessCount = [int]$interactive.Count
  processIds = @($processes | ForEach-Object { [int]$_.ProcessId })
  runKeyReady = $true
  stoppedProcessIds = @($old | ForEach-Object { [int]$_.ProcessId })
  retiredLegacyProcessIds = @($legacyStopped)
  retiredLegacyTaskNames = @($legacyTaskNames)
  retiredLegacyRunKeyNames = @($legacyRunKeyNames)
  legacyTasksStillEnabled = @($legacyTasksStillEnabled)
  legacyProcessIdsRemaining = @($legacyProcessesRemaining | ForEach-Object { [int]$_.ProcessId })
  valuesRedacted = $true
}
$result | ConvertTo-Json -Compress -Depth 6
`);
}

function powerShellPayload(payload: Record<string, unknown>, body: string): string {
  const encoded = Buffer.from(JSON.stringify(payload), "utf8").toString("base64");
  return `$payload = ([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('${encoded}')) | ConvertFrom-Json)\n${body.trim()}\n`;
}

function runPowerShell(route: string, script: string, timeoutMs: number): CommandResult {
  return runCommand(["trans", route, "ps"], repoRoot, { input: script, timeoutMs });
}

function yamlFile(path: string): Record<string, unknown> {
  const absolute = rootPath(path);
  if (!existsSync(absolute)) throw new Error(`${path} 不存在`);
  return record(Bun.YAML.parse(readFileSync(absolute, "utf8")) as unknown, path);
}

function configRef(ref: string): { file: string; path: string } {
  const parts = ref.split("#");
  if (parts.length !== 2 || !parts[0] || !parts[1]) throw new Error(`配置引用必须使用 path.yaml#object.path：${ref}`);
  if (isAbsolute(parts[0]) || parts[0].includes("..")) throw new Error(`配置引用文件必须是仓库内相对路径：${ref}`);
  return { file: parts[0], path: parts[1] };
}

function pathValue(input: unknown, path: string): unknown {
  let current = input;
  for (const raw of path.replace(/\[(\d+)\]/gu, ".$1").split(".").filter(Boolean)) {
    if (Array.isArray(current)) current = current[Number(raw)];
    else if (current && typeof current === "object") current = (current as Record<string, unknown>)[raw];
    else return undefined;
  }
  return current;
}

function record(value: unknown, path: string): Record<string, any> {
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${path} 必须是对象`);
  return value as Record<string, any>;
}

function text(value: unknown, path: string): string {
  if (typeof value !== "string" || value.trim().length === 0) throw new Error(`${path} 必须是非空字符串`);
  return value.trim();
}

function optionalText(value: unknown): string | null {
  return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
}

function texts(value: unknown, path: string): string[] {
  if (!Array.isArray(value) || value.some((item) => typeof item !== "string" || item.trim().length === 0)) throw new Error(`${path} 必须是非空字符串数组`);
  return value.map((item) => String(item));
}

function bool(value: unknown, path: string): boolean {
  if (typeof value !== "boolean") throw new Error(`${path} 必须是布尔值`);
  return value;
}

function positiveInteger(value: unknown, path: string): number {
  const parsed = Number(value);
  if (!Number.isInteger(parsed) || parsed < 1) throw new Error(`${path} 必须是正整数`);
  return parsed;
}

function secretPath(sourceRef: string): string {
  if (isAbsolute(sourceRef) || sourceRef.includes("..")) throw new Error(`Secret sourceRef 必须是相对路径且不能包含 ..：${sourceRef}`);
  return join(SECRET_ROOT, sourceRef);
}

function parseEnv(content: string): Record<string, string> {
  const result: Record<string, string> = {};
  for (const raw of content.split(/\r?\n/u)) {
    const line = raw.trim();
    if (!line || line.startsWith("#")) continue;
    const index = line.indexOf("=");
    if (index <= 0) continue;
    result[line.slice(0, index).trim()] = line.slice(index + 1).trim().replace(/^['"]|['"]$/gu, "");
  }
  return result;
}

function parseJsonRecord(textValue: string): Record<string, unknown> | null {
  const lines = textValue.trim().split(/\r?\n/u).filter(Boolean);
  for (let index = lines.length - 1; index >= 0; index -= 1) {
    try {
      return record(JSON.parse(lines[index]) as unknown, "远端 JSON");
    } catch {}
  }
  return null;
}

function compactFailure(result: CommandResult): string {
  const reason = result.timedOut ? "command-timed-out" : "command-failed";
  return `${reason}; exit=${result.exitCode ?? "-"}; stderr=${result.stderr.trim().slice(-500)}`;
}

function sha256(value: string | Buffer): string {
  return `sha256:${sha256Hex(value)}`;
}

function sha256Hex(value: string | Buffer): string {
  return createHash("sha256").update(value).digest("hex");
}

function commandSucceeded(result: CommandResult): boolean {
  return result.exitCode === 0 && !result.timedOut;
}

function delay(ms: number): Promise<void> {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
