import { createHash, randomUUID } from "node:crypto";

import {
  ENVIRONMENT_DEV,
  ERROR_CODES,
  HwlabProtocolError,
  assertProtocolRecord
} from "../protocol/index.mjs";
import {
  CLOUD_API_SERVICE_ID,
  createProtocolAuditEvent
} from "../audit/index.mjs";
import {
  requestRuntimeJson,
  normalizeRuntimeJsonResponse,
  persistRuntimeRegistrations,
  tryRuntimeWrite,
  successControlResult,
  blockedControlResult,
  blockedResult,
  writeOutcomeRecords,
  createEvidenceRecord,
  evidenceState,
  auditState,
  recordWriteState,
  durableStatus,
  trustBlocker,
  redactedBlockerClassification,
  blockerCategory,
  redactBlockerMessage,
  controlTarget,
  persistenceSummary,
  safeRuntimeSummary,
  isDurableRuntimeReady,
  readValueFromGatewayResult,
  gatewayResultSummary,
  publicGatewayStatus,
  finalizeControlResult,
  ensureTrailingSlash,
  trimUrl,
  parseTimeout,
  redactUrl
} from "./m3-io-control-runtime.ts";
import { HWLAB_M3_IO_CAPABILITY_LEVELS } from "../../skills/hwlab-agent-runtime/scripts/src/m3-io-skill-client.mjs";

export const M3_IO_CONTROL_CONTRACT_VERSION = "m3-io-control-v1";
export const M3_IO_CONTROL_ROUTE = "/v1/m3/io";
export const M3_STATUS_CONTRACT_VERSION = "m3-status-v1";
export const M3_STATUS_ROUTE = "/v1/m3/status";
export const M3_IO_RPC_METHODS = Object.freeze({
  doWrite: "m3.io.do.write",
  diRead: "m3.io.di.read"
});

export const M3_IO_CHAIN = Object.freeze({
  projectId: "prj_m3_hardware_loop",
  sourceGatewayId: "gwsimu_1",
  sourceGatewaySessionId: "gws_gwsimu_1",
  sourceResourceId: "res_boxsimu_1",
  sourceBoxId: "boxsimu_1",
  sourcePort: "DO1",
  targetGatewayId: "gwsimu_2",
  targetGatewaySessionId: "gws_gwsimu_2",
  targetResourceId: "res_boxsimu_2",
  targetBoxId: "boxsimu_2",
  targetPort: "DI1",
  patchPanelServiceId: "hwlab-patch-panel"
});

export const M3_IO_BLOCKER_CODES = Object.freeze({
  gatewayUnavailable: "m3_gateway_session_unavailable",
  boxUnavailable: "m3_box_resource_unavailable",
  portDirectionInvalid: "m3_port_direction_invalid",
  wiringMissing: "m3_wiring_missing",
  patchPanelUnavailable: "m3_patch_panel_unavailable",
  dispatchFailed: "m3_gateway_dispatch_failed",
  runtimeDurableBlocked: "runtime_durable_not_green"
});

export const DEFAULT_TIMEOUT_MS = 2200;
const DEV_SERVICE_ENDPOINTS = Object.freeze({
  gateway1: "",
  gateway2: "",
  patchPanel: ""
});
const ACTIONS = new Set(["do.write", "di.read"]);

export function describeM3IoControl(options = {}) {
  const env = options.env ?? process.env;
  const config = buildM3IoConfig(env);
  return {
    serviceId: CLOUD_API_SERVICE_ID,
    contractVersion: M3_IO_CONTROL_CONTRACT_VERSION,
    route: M3_IO_CONTROL_ROUTE,
    status: config.enabled ? "available" : "blocked",
    capabilityLevel: HWLAB_M3_IO_CAPABILITY_LEVELS.blocked,
    readinessStatus: "not_checked",
    capabilityLevels: [
      HWLAB_M3_IO_CAPABILITY_LEVELS.blocked,
      HWLAB_M3_IO_CAPABILITY_LEVELS.ready
    ],
    sourceKind: "SOURCE",
    chain: M3_IO_CHAIN,
    actions: [
      {
        action: "do.write",
        label: "box-simu-1 DO1 write",
        gatewayId: M3_IO_CHAIN.sourceGatewayId,
        resourceId: M3_IO_CHAIN.sourceResourceId,
        boxId: M3_IO_CHAIN.sourceBoxId,
        port: M3_IO_CHAIN.sourcePort,
        valueType: "boolean"
      },
      {
        action: "di.read",
        label: "box-simu-2 DI1 read",
        gatewayId: M3_IO_CHAIN.targetGatewayId,
        resourceId: M3_IO_CHAIN.targetResourceId,
        boxId: M3_IO_CHAIN.targetBoxId,
        port: M3_IO_CHAIN.targetPort,
        valueType: "boolean"
      }
    ],
    boundaries: {
      frontendCallsOnly: M3_IO_CONTROL_ROUTE,
      cloudApiDispatchesToGateway: true,
      patchPanelOwnsPropagation: true,
      directFrontendGatewayOrBoxAccess: false,
      genericHardwareRpcExposedToFrontend: false
    },
    endpointsConfigured: {
      gateway1: Boolean(config.gateway1Url),
      gateway2: Boolean(config.gateway2Url),
      patchPanel: Boolean(config.patchPanelUrl)
    },
    blockedReason: config.enabled ? null : "M3 IO 控制未启用；前端保持 blocked，不直连 gateway/box-simu。"
  };
}

export async function describeM3IoControlLive(options = {}) {
  const contract = describeM3IoControl(options);
  const env = options.env ?? process.env;
  const config = buildM3IoConfig(env);
  const runtime = await safeRuntimeSummary(options.runtimeStore);
  const readiness = await buildM3IoReadiness({
    config,
    requestJson: options.m3IoRequestJson ?? options.requestJson,
    now: options.now
  });
  const controlReady = readiness.status === "ready" && readiness.controlReady === true;
  const capabilityLevel = controlReady
    ? HWLAB_M3_IO_CAPABILITY_LEVELS.ready
    : HWLAB_M3_IO_CAPABILITY_LEVELS.blocked;
  const trustReadiness = buildM3IoTrustReadiness(runtime, {
    observedAt: readiness.observedAt,
    controlReady
  });

  return {
    ...contract,
    status: controlReady ? "available" : "blocked",
    sourceKind: controlReady ? "DEV-LIVE" : "BLOCKED",
    capabilityLevel,
    readinessStatus: readiness.status,
    readiness: {
      ...readiness,
      capabilityLevel,
      trust: trustReadiness
    },
    trustReadiness,
    controlReady,
    blockedReason: controlReady
      ? null
      : readiness.blocker?.zh ?? contract.blockedReason ?? "M3 IO 只读 readiness 未 green；控制面板保持阻塞。"
  };
}

export async function describeM3StatusLive(options = {}) {
  const env = options.env ?? process.env;
  const config = buildM3IoConfig(env);
  const requestJson = options.m3IoRequestJson ?? options.requestJson;
  const observedAt = options.now?.() ?? new Date().toISOString();
  const runtime = await safeRuntimeSummary(options.runtimeStore);
  const trust = await buildM3StatusTrust({
    runtimeStore: options.runtimeStore,
    runtime
  });

  const base = {
    serviceId: CLOUD_API_SERVICE_ID,
    contractVersion: M3_STATUS_CONTRACT_VERSION,
    route: M3_STATUS_ROUTE,
    observedAt,
    chain: M3_IO_CHAIN,
    boundaries: {
      frontendCallsOnly: M3_STATUS_ROUTE,
      cloudApiAggregatesRuntimeStatus: true,
      patchPanelOwnsPropagation: true,
      directFrontendGatewayOrBoxAccess: false,
      sourceTopologyFallbackMustStayUnverified: true
    }
  };

  if (!config.enabled) {
    const blocker = {
      code: "m3_control_disabled",
      layer: "cloud-api",
      zh: "M3 状态聚合未启用；前端不能绕过 cloud-api 直连 gateway/box-simu。"
    };
    const gateways = defaultM3Gateways({ sourceKind: "UNVERIFIED", observedAt });
    const boxes = defaultM3Boxes({ sourceKind: "UNVERIFIED", observedAt });
    const patchPanel = defaultM3PatchPanel({ observedAt });
    const blockedTrust = {
      ...trust,
      blocker: "m3_control_disabled"
    };
    return {
      ...base,
      status: "blocked",
      sourceKind: "BLOCKED",
      summary: "M3 状态聚合未启用；前端不能绕过 cloud-api 直连 gateway/box-simu。",
      gateways,
      boxes,
      patchPanel,
      io: m3StatusIoSummary({ boxes, patchPanel, hardwareLive: false, observedAt }),
      operation: m3StatusOperationSummary(blockedTrust),
      trace: m3StatusTraceSummary(blockedTrust),
      audit: m3StatusAuditSummary(blockedTrust),
      runtimeDurable: m3StatusRuntimeDurableSummary(blockedTrust),
      trust: blockedTrust,
      blocker
    };
  }

  const [sourceGateway, targetGateway, patchStatus, patchWiring] = await Promise.all([
    readGatewaySession(config.gateway1Url, {
      gatewayRole: "source",
      expectedGatewayId: M3_IO_CHAIN.sourceGatewayId,
      expectedGatewaySessionId: M3_IO_CHAIN.sourceGatewaySessionId,
      requestJson,
      timeoutMs: config.timeoutMs
    }),
    readGatewaySession(config.gateway2Url, {
      gatewayRole: "target",
      expectedGatewayId: M3_IO_CHAIN.targetGatewayId,
      expectedGatewaySessionId: M3_IO_CHAIN.targetGatewaySessionId,
      requestJson,
      timeoutMs: config.timeoutMs
    }),
    requestRuntimeJson(new URL("/status", ensureTrailingSlash(config.patchPanelUrl)).toString(), {
      method: "GET",
      requestJson,
      timeoutMs: config.timeoutMs
    }),
    requestRuntimeJson(new URL("/wiring", ensureTrailingSlash(config.patchPanelUrl)).toString(), {
      method: "GET",
      requestJson,
      timeoutMs: config.timeoutMs
    })
  ]);

  const sourceCommand = normalizeCommand("do.write", { value: false });
  const targetCommand = normalizeCommand("di.read", {});
  const sourceBox = sourceGateway.available ? findGatewayBox(sourceGateway.status, sourceCommand) : null;
  const targetBox = targetGateway.available ? findGatewayBox(targetGateway.status, targetCommand) : null;
  const [sourceDoRead, targetDiRead] = await Promise.all([
    sourceGateway.available && sourceBox
      ? readM3StatusPort({
          gateway: sourceGateway,
          command: sourceCommand,
          requestJson,
          timeoutMs: config.timeoutMs,
          observedAt,
          port: M3_IO_CHAIN.sourcePort,
          direction: "output"
        })
      : Promise.resolve(unverifiedPortRead({
          port: M3_IO_CHAIN.sourcePort,
          direction: "output",
          source: "gateway-simu",
          reason: sourceGateway.reason ?? `${M3_IO_CHAIN.sourceResourceId} 未在 source gateway registry 中可见`,
          observedAt
        })),
    targetGateway.available && targetBox
      ? readM3StatusPort({
          gateway: targetGateway,
          command: targetCommand,
          requestJson,
          timeoutMs: config.timeoutMs,
          observedAt,
          port: M3_IO_CHAIN.targetPort,
          direction: "input"
        })
      : Promise.resolve(unverifiedPortRead({
          port: M3_IO_CHAIN.targetPort,
          direction: "input",
          source: "gateway-simu",
          reason: targetGateway.reason ?? `${M3_IO_CHAIN.targetResourceId} 未在 target gateway registry 中可见`,
          observedAt
        }))
  ]);

  const gateways = [
    gatewayStatusEntry(sourceGateway, {
      id: M3_IO_CHAIN.sourceGatewayId,
      sessionId: M3_IO_CHAIN.sourceGatewaySessionId,
      role: "source",
      observedAt
    }),
    gatewayStatusEntry(targetGateway, {
      id: M3_IO_CHAIN.targetGatewayId,
      sessionId: M3_IO_CHAIN.targetGatewaySessionId,
      role: "target",
      observedAt
    })
  ];
  const boxes = [
    boxStatusEntry({
      gateway: sourceGateway,
      box: sourceBox,
      boxId: M3_IO_CHAIN.sourceBoxId,
      resourceId: M3_IO_CHAIN.sourceResourceId,
      gatewayId: M3_IO_CHAIN.sourceGatewayId,
      gatewaySessionId: M3_IO_CHAIN.sourceGatewaySessionId,
      observedAt,
      ports: {
        [M3_IO_CHAIN.sourcePort]: sourceDoRead.port
      }
    }),
    boxStatusEntry({
      gateway: targetGateway,
      box: targetBox,
      boxId: M3_IO_CHAIN.targetBoxId,
      resourceId: M3_IO_CHAIN.targetResourceId,
      gatewayId: M3_IO_CHAIN.targetGatewayId,
      gatewaySessionId: M3_IO_CHAIN.targetGatewaySessionId,
      observedAt,
      ports: {
        [M3_IO_CHAIN.targetPort]: targetDiRead.port
      }
    })
  ];
  const patchPanel = patchPanelStatusEntry({
    statusResponse: patchStatus,
    wiringResponse: patchWiring,
    observedAt
  });
  const blockers = [
    ...gateways.filter((gateway) => !gateway.online).map((gateway) => ({
      code: gateway.blocker,
      layer: gateway.id,
      zh: gateway.lastError
    })),
    ...boxes.filter((box) => !box.online).map((box) => ({
      code: M3_IO_BLOCKER_CODES.boxUnavailable,
      layer: box.resourceId,
      zh: `${box.resourceId} 未在 gateway-simu registry 中可见`
    })),
    patchPanel.connectionActive ? null : {
      code: patchPanel.observable === false ? M3_IO_BLOCKER_CODES.patchPanelUnavailable : M3_IO_BLOCKER_CODES.wiringMissing,
      layer: M3_IO_CHAIN.patchPanelServiceId,
      zh: patchPanel.lastError ?? `hwlab-patch-panel 未确认 active ${M3_IO_CHAIN.sourceResourceId}:${M3_IO_CHAIN.sourcePort} -> ${M3_IO_CHAIN.targetResourceId}:${M3_IO_CHAIN.targetPort} 接线`
    },
    sourceDoRead.error ? {
      code: M3_IO_BLOCKER_CODES.dispatchFailed,
      layer: M3_IO_CHAIN.sourceResourceId,
      zh: sourceDoRead.error
    } : null,
    targetDiRead.error ? {
      code: M3_IO_BLOCKER_CODES.dispatchFailed,
      layer: M3_IO_CHAIN.targetResourceId,
      zh: targetDiRead.error
    } : null
  ].filter(Boolean);

  const hardwareLive =
    blockers.length === 0 &&
    sourceDoRead.port.sourceKind === "DEV-LIVE" &&
    targetDiRead.port.sourceKind === "DEV-LIVE";
  const trustGreen = trust.durableStatus === "green" && trust.evidenceId && trust.auditId && trust.operationId && trust.traceId;
  const status = blockers.length > 0
    ? blockers.some((blocker) => blocker.code === M3_IO_BLOCKER_CODES.dispatchFailed || blocker.code === M3_IO_BLOCKER_CODES.patchPanelUnavailable) ? "error" : "blocked"
    : trustGreen ? "live" : hardwareLive ? "blocked" : "unverified";
  const sourceKind = hardwareLive ? "DEV-LIVE" : "UNVERIFIED";
  const blocker = blockers[0] ?? (trust.blocker ? {
    code: trust.blocker,
    layer: "runtime-durable",
    zh: `runtime durable 仍 blocked：${trust.blocker}`
  } : null);

  return {
    ...base,
    status,
    sourceKind,
    summary: status === "live"
      ? "M3 硬件状态与可信持久化均为 DEV-LIVE。"
      : hardwareLive
        ? "M3 DO1/DI1 实况读数来自 cloud-api 聚合入口；可信持久化仍保持 blocked，不得标为 trusted green。"
        : "M3 硬件状态尚未完整验证；SOURCE 拓扑只能作为未验证 fallback。",
    gateways,
    boxes,
    patchPanel,
    io: m3StatusIoSummary({ boxes, patchPanel, hardwareLive, observedAt }),
    operation: m3StatusOperationSummary(trust),
    trace: m3StatusTraceSummary(trust),
    audit: m3StatusAuditSummary(trust),
    runtimeDurable: m3StatusRuntimeDurableSummary(trust),
    trust,
    blocker
  };
}

export async function handleM3IoControl(params = {}, context = {}) {
  const env = context.env ?? process.env;
  const now = context.now?.() ?? new Date().toISOString();
  const config = buildM3IoConfig(env);
  const action = normalizeAction(params.action ?? params.operation);
  const meta = buildRequestMeta(params, context);
  const runtimeBefore = await safeRuntimeSummary(context.runtimeStore);

  if (!config.enabled) {
    return blockedResult({
      action,
      meta,
      runtime: runtimeBefore,
      code: "m3_control_disabled",
      reason: "M3 IO 控制未启用；前端不能绕过 cloud-api 直连 gateway/box-simu。",
      httpStatus: 503,
      now
    });
  }

  if (!ACTIONS.has(action)) {
    throw new HwlabProtocolError("M3 IO action must be do.write or di.read", {
      code: ERROR_CODES.invalidParams,
      data: {
        action,
        allowedActions: [...ACTIONS]
      }
    });
  }

  const command = normalizeCommand(action, params);
  const directionIssue = validateM3Command(command);
  if (directionIssue) {
    return blockedResult({
      action,
      meta,
      runtime: runtimeBefore,
      code: M3_IO_BLOCKER_CODES.portDirectionInvalid,
      reason: directionIssue,
      httpStatus: 400,
      now
    });
  }

  const gatewayUrl = action === "do.write" ? config.gateway1Url : config.gateway2Url;
  const gatewayRole = action === "do.write" ? "source" : "target";
  const gateway = await readGatewaySession(gatewayUrl, {
    gatewayRole,
    expectedGatewayId: command.gatewayId,
    expectedGatewaySessionId: command.gatewaySessionId,
    requestJson: context.requestJson,
    timeoutMs: config.timeoutMs
  });
  if (!gateway.available) {
    return blockedResult({
      action,
      command,
      meta,
      runtime: runtimeBefore,
      code: gateway.code ?? M3_IO_BLOCKER_CODES.gatewayUnavailable,
      reason: gateway.reason,
      httpStatus: 200,
      now,
      details: {
        gateway
      }
    });
  }

  const box = findGatewayBox(gateway.status, command);
  if (!box) {
    return blockedResult({
      action,
      command,
      meta,
      runtime: runtimeBefore,
      code: M3_IO_BLOCKER_CODES.boxUnavailable,
      reason: `${gatewayRole} gateway 已响应，但目标 box resource 未注册/不可用：${command.resourceId}`,
      httpStatus: 200,
      now,
      details: {
        gateway: publicGatewayStatus(gateway.status)
      }
    });
  }

  const capability = capabilityFor(command, box);
  const operation = createOperationRecord({
    action,
    command,
    capability,
    gatewaySessionId: gateway.gatewaySessionId,
    meta,
    now
  });
  const registration = await persistRuntimeRegistrations({
    runtimeStore: context.runtimeStore,
    gatewayStatus: gateway.status,
    box,
    capability,
    command,
    meta
  });
  const operationWrite = await tryRuntimeWrite(
    "hardware.operation.request",
    () => context.runtimeStore?.requestHardwareOperation?.(operation, meta)
  );

  if (action === "di.read") {
    return finalizeControlResult(await readDi({
      config,
      command,
      capability,
      gateway,
      operation,
      meta,
      runtimeStore: context.runtimeStore,
      runtimeBefore,
      registration,
      operationWrite,
      requestJson: context.requestJson,
      now
    }));
  }

  return finalizeControlResult(await writeDo({
    config,
    command,
    capability,
    gateway,
    operation,
    meta,
    runtimeStore: context.runtimeStore,
    runtimeBefore,
    registration,
    operationWrite,
    requestJson: context.requestJson,
    now
  }));
}

export async function buildM3IoReadiness({ config, requestJson, now } = {}) {
  const observedAt = now?.() ?? new Date().toISOString();
  const checks = [];

  if (!config?.enabled) {
    return blockedReadiness({
      checks,
      code: "m3_control_disabled",
      layer: "cloud-api",
      reason: "M3 IO 控制未启用；前端不能绕过 cloud-api 直连 gateway/box-simu。",
      observedAt
    });
  }

  const sourceGateway = await readGatewaySession(config.gateway1Url, {
    gatewayRole: "source",
    expectedGatewayId: M3_IO_CHAIN.sourceGatewayId,
    expectedGatewaySessionId: M3_IO_CHAIN.sourceGatewaySessionId,
    requestJson,
    timeoutMs: config.timeoutMs
  });
  checks.push(readinessCheckFromGateway("source-gateway", sourceGateway));
  if (!sourceGateway.available) {
    return blockedReadiness({
      checks,
      code: sourceGateway.code ?? M3_IO_BLOCKER_CODES.gatewayUnavailable,
      layer: "gateway-simu-1",
      reason: sourceGateway.reason,
      observedAt
    });
  }

  const sourceCommand = normalizeCommand("do.write", {});
  const sourceBox = findGatewayBox(sourceGateway.status, sourceCommand);
  checks.push(readinessCheckFromBox("source-box", sourceBox, sourceCommand));
  if (!sourceBox) {
    return blockedReadiness({
      checks,
      code: M3_IO_BLOCKER_CODES.boxUnavailable,
      layer: "box-simu-1",
      reason: `gateway-simu-1 已响应，但 ${M3_IO_CHAIN.sourceResourceId} 未注册/不可用；控制面板保持阻塞。`,
      observedAt
    });
  }

  const targetGateway = await readGatewaySession(config.gateway2Url, {
    gatewayRole: "target",
    expectedGatewayId: M3_IO_CHAIN.targetGatewayId,
    expectedGatewaySessionId: M3_IO_CHAIN.targetGatewaySessionId,
    requestJson,
    timeoutMs: config.timeoutMs
  });
  checks.push(readinessCheckFromGateway("target-gateway", targetGateway));
  if (!targetGateway.available) {
    return blockedReadiness({
      checks,
      code: targetGateway.code ?? M3_IO_BLOCKER_CODES.gatewayUnavailable,
      layer: "gateway-simu-2",
      reason: targetGateway.reason,
      observedAt
    });
  }

  const targetCommand = normalizeCommand("di.read", {});
  const targetBox = findGatewayBox(targetGateway.status, targetCommand);
  checks.push(readinessCheckFromBox("target-box", targetBox, targetCommand));
  if (!targetBox) {
    return blockedReadiness({
      checks,
      code: M3_IO_BLOCKER_CODES.boxUnavailable,
      layer: "box-simu-2",
      reason: `gateway-simu-2 已响应，但 ${M3_IO_CHAIN.targetResourceId} 未注册/不可用；控制面板保持阻塞。`,
      observedAt
    });
  }

  const patchStatus = await requestRuntimeJson(new URL("/status", ensureTrailingSlash(config.patchPanelUrl)).toString(), {
    method: "GET",
    requestJson,
    timeoutMs: config.timeoutMs
  });
  checks.push(readinessCheckFromPatchPanelStatus(patchStatus));
  if (!patchStatus.ok) {
    return blockedReadiness({
      checks,
      code: M3_IO_BLOCKER_CODES.patchPanelUnavailable,
      layer: "hwlab-patch-panel",
      reason: `hwlab-patch-panel 不可用：${patchStatus.error ?? `HTTP ${patchStatus.status}`}`,
      observedAt
    });
  }

  const wiring = await requestRuntimeJson(new URL("/wiring", ensureTrailingSlash(config.patchPanelUrl)).toString(), {
    method: "GET",
    requestJson,
    timeoutMs: config.timeoutMs
  });
  checks.push(readinessCheckFromPatchPanelWiring(wiring));
  if (!wiring.ok) {
    return blockedReadiness({
      checks,
      code: M3_IO_BLOCKER_CODES.wiringMissing,
      layer: "hwlab-patch-panel",
      reason: `hwlab-patch-panel 接线读取失败：${wiring.error ?? `HTTP ${wiring.status}`}`,
      observedAt
    });
  }

  if (!hasExpectedPatchPanelWiring(patchStatus.body) || !hasExpectedPatchPanelWiring(wiring.body)) {
    return blockedReadiness({
      checks,
      code: M3_IO_BLOCKER_CODES.wiringMissing,
      layer: "hwlab-patch-panel",
      reason: `hwlab-patch-panel 未确认 active ${M3_IO_CHAIN.sourceResourceId}:${M3_IO_CHAIN.sourcePort} -> ${M3_IO_CHAIN.targetResourceId}:${M3_IO_CHAIN.targetPort} 接线；控制面板保持阻塞。`,
      observedAt
    });
  }

  return {
    status: "ready",
    controlReady: true,
    capabilityLevel: HWLAB_M3_IO_CAPABILITY_LEVELS.ready,
    sourceKind: "DEV-LIVE",
    evidenceLevel: "DEV-LIVE",
    observedAt,
    summary: "cloud-api 只读 readiness 已确认 gateway-simu、box-simu 与 hwlab-patch-panel 目标接线；这只表示控制面板可执行受控操作，不等于 M3 验收通过。",
    chain: M3_IO_CHAIN,
    checks,
    blocker: null
  };
}

export async function handleM3IoRpc(action, params = {}, envelope = {}, context = {}) {
  return handleM3IoControl(
    {
      ...params,
      action,
      traceId: envelope.meta?.traceId ?? params.traceId,
      requestId: envelope.id ?? params.requestId,
      actorId: envelope.meta?.actorId ?? params.actorId
    },
    {
      ...context,
      requestJson: context.requestJson ?? context.m3IoRequestJson,
      traceId: envelope.meta?.traceId ?? context.traceId,
      requestId: envelope.id ?? context.requestId,
      actorId: envelope.meta?.actorId ?? context.actorId
    }
  );
}

export function buildM3IoConfig(env = process.env) {
  const enabledValue = String(env.HWLAB_M3_IO_CONTROL_ENABLED ?? env.HWLAB_M3_CONTROL_ENABLED ?? "false").toLowerCase();
  return {
    enabled: !["0", "false", "no", "disabled"].includes(enabledValue),
    gateway1Url: trimUrl(env.HWLAB_M3_GATEWAY_SIMU_1_URL) ?? DEV_SERVICE_ENDPOINTS.gateway1,
    gateway2Url: trimUrl(env.HWLAB_M3_GATEWAY_SIMU_2_URL) ?? DEV_SERVICE_ENDPOINTS.gateway2,
    patchPanelUrl: trimUrl(env.HWLAB_M3_PATCH_PANEL_URL) ?? DEV_SERVICE_ENDPOINTS.patchPanel,
    timeoutMs: parseTimeout(env.HWLAB_M3_CONTROL_TIMEOUT_MS)
  };
}

function blockedReadiness({ checks, code, layer, reason, observedAt }) {
  return {
    status: "blocked",
    controlReady: false,
    capabilityLevel: HWLAB_M3_IO_CAPABILITY_LEVELS.blocked,
    sourceKind: "BLOCKED",
    evidenceLevel: "BLOCKED",
    observedAt,
    summary: `M3 IO 只读 readiness 未 green：${reason}`,
    chain: M3_IO_CHAIN,
    blocker: {
      code,
      layer,
      zh: reason
    },
    capabilityBlocker: {
      code,
      layer,
      zh: reason,
      category: blockerCategory(code)
    },
    checks
  };
}

function buildM3IoTrustReadiness(runtime, { observedAt, controlReady } = {}) {
  const durable = durableStatus(runtime);
  const ready = isDurableRuntimeReady(runtime);
  const blocker = ready
    ? null
    : {
        code: durable.blocker ?? M3_IO_BLOCKER_CODES.runtimeDurableBlocked,
        layer: "runtime-durable",
        zh: `runtime durable 未 green：${durable.blocker ?? M3_IO_BLOCKER_CODES.runtimeDurableBlocked}`,
        category: "runtime_durable"
      };
  return {
    status: ready ? "ready" : "blocked",
    controlReady: Boolean(controlReady),
    trustedEvidenceReady: ready,
    capabilityLevel: controlReady
      ? HWLAB_M3_IO_CAPABILITY_LEVELS.ready
      : HWLAB_M3_IO_CAPABILITY_LEVELS.blocked,
    evidenceLevel: ready ? "DEV-LIVE" : "BLOCKED",
    sourceKind: ready ? "DEV-LIVE" : "BLOCKED",
    observedAt,
    durableStatus: durable,
    blocker,
    summary: ready
      ? "runtime durable 已 green；operation/audit/evidence 可作为可信记录写入。"
      : "受控硬件路径 readiness 与可信持久化分离；runtime durable 未 green 时不得宣称完整硬件控制验收。"
  };
}

async function buildM3StatusTrust({ runtimeStore, runtime }) {
  const durable = durableStatus(runtime);
  const base = {
    operationId: null,
    traceId: null,
    auditId: null,
    evidenceId: null,
    durableStatus: isDurableRuntimeReady(runtime) ? "green" : "blocked",
    runtime: durable,
    blocker: isDurableRuntimeReady(runtime) ? null : durable.blocker
  };

  const [audit, evidence] = await Promise.all([
    safeRuntimeRead(() => runtimeStore?.queryAuditEvents?.({ projectId: M3_IO_CHAIN.projectId, limit: 8 })),
    safeRuntimeRead(() => runtimeStore?.queryEvidenceRecords?.({ projectId: M3_IO_CHAIN.projectId, limit: 8 }))
  ]);
  const latestAudit = latestM3AuditEvent(audit.value?.events ?? []);
  const latestEvidence = latestM3EvidenceRecord(evidence.value?.records ?? []);
  const operationId = latestEvidence?.operationId ?? latestAudit?.operationId ?? null;
  const traceId = latestEvidence?.traceId ?? latestEvidence?.metadata?.traceId ?? latestAudit?.traceId ?? null;

  return {
    ...base,
    operationId,
    traceId,
    auditId: latestAudit?.auditId ?? null,
    evidenceId: latestEvidence?.evidenceId ?? null,
    durableStatus: base.durableStatus,
    blocker: base.blocker ?? audit.error?.code ?? evidence.error?.code ?? null,
    readStatus: {
      audit: audit.ok ? "read" : "blocked",
      evidence: evidence.ok ? "read" : "blocked",
      auditError: audit.error?.message ?? null,
      evidenceError: evidence.error?.message ?? null
    }
  };
}

function m3StatusIoSummary({ boxes, patchPanel, hardwareLive, observedAt }) {
  const sourceBox = boxes.find((box) => box.resourceId === M3_IO_CHAIN.sourceResourceId) ?? null;
  const targetBox = boxes.find((box) => box.resourceId === M3_IO_CHAIN.targetResourceId) ?? null;
  const do1 = sourceBox?.ports?.[M3_IO_CHAIN.sourcePort] ?? null;
  const di1 = targetBox?.ports?.[M3_IO_CHAIN.targetPort] ?? null;
  const blocker = !sourceBox?.online
    ? M3_IO_BLOCKER_CODES.boxUnavailable
    : !targetBox?.online
      ? M3_IO_BLOCKER_CODES.boxUnavailable
      : patchPanel?.connectionActive !== true
        ? M3_IO_BLOCKER_CODES.wiringMissing
        : do1?.sourceKind !== "DEV-LIVE" || di1?.sourceKind !== "DEV-LIVE"
          ? "m3_io_unverified"
          : null;
  return {
    status: hardwareLive ? "live" : blocker ? "blocked" : "unverified",
    sourceKind: hardwareLive ? "DEV-LIVE" : "UNVERIFIED",
    observedAt,
    chain: {
      from: `${M3_IO_CHAIN.sourceResourceId}:${M3_IO_CHAIN.sourcePort}`,
      via: M3_IO_CHAIN.patchPanelServiceId,
      to: `${M3_IO_CHAIN.targetResourceId}:${M3_IO_CHAIN.targetPort}`
    },
    do1: m3StatusPortSummary(sourceBox, M3_IO_CHAIN.sourcePort, do1),
    di1: m3StatusPortSummary(targetBox, M3_IO_CHAIN.targetPort, di1),
    patchPanel: {
      serviceId: patchPanel?.serviceId ?? M3_IO_CHAIN.patchPanelServiceId,
      connectionActive: patchPanel?.connectionActive === true,
      sourceKind: patchPanel?.sourceKind ?? "UNVERIFIED",
      observedAt: patchPanel?.lastSyncAt ?? observedAt,
      blocker: patchPanel?.connectionActive === true ? null : patchPanel?.lastError ?? M3_IO_BLOCKER_CODES.wiringMissing
    },
    blocker
  };
}

function m3StatusPortSummary(box, port, state) {
  return {
    resourceId: box?.resourceId ?? null,
    boxId: box?.id ?? null,
    gatewayId: box?.gatewayId ?? null,
    gatewaySessionId: box?.gatewaySessionId ?? null,
    port,
    direction: state?.direction ?? null,
    value: state?.value ?? null,
    source: state?.source ?? "unverified",
    sourceKind: state?.sourceKind ?? "UNVERIFIED",
    observedAt: state?.observedAt ?? box?.observedAt ?? null,
    blocker: state?.lastError ?? (box?.online ? null : M3_IO_BLOCKER_CODES.boxUnavailable)
  };
}

function m3StatusOperationSummary(trust) {
  return {
    status: trust.operationId ? "read" : "blocked",
    operationId: trust.operationId ?? null,
    source: trust.operationId ? "runtime-store audit/evidence" : "runtime-store",
    blocker: trust.operationId ? null : trust.blocker ?? "operation_not_persisted",
    readStatus: {
      audit: trust.readStatus?.audit ?? "blocked",
      evidence: trust.readStatus?.evidence ?? "blocked"
    }
  };
}

function m3StatusTraceSummary(trust) {
  return {
    status: trust.traceId ? "read" : "blocked",
    traceId: trust.traceId ?? null,
    source: trust.traceId ? "runtime-store audit/evidence" : "runtime-store",
    blocker: trust.traceId ? null : trust.blocker ?? "trace_not_persisted"
  };
}

function m3StatusAuditSummary(trust) {
  return {
    status: trust.auditId ? "read" : "blocked",
    auditId: trust.auditId ?? null,
    readStatus: trust.readStatus?.audit ?? "blocked",
    source: trust.auditId ? "runtime-store audit.event.query" : "runtime-store",
    blocker: trust.auditId ? null : trust.readStatus?.auditError ?? trust.blocker ?? "audit_not_persisted"
  };
}

function m3StatusRuntimeDurableSummary(trust) {
  return {
    status: trust.durableStatus === "green" ? "green" : "blocked",
    green: trust.durableStatus === "green",
    blocker: trust.blocker ?? null,
    runtime: trust.runtime,
    readStatus: trust.readStatus ?? {
      audit: "blocked",
      evidence: "blocked"
    },
    auditReady: trust.readStatus?.audit === "read",
    evidenceReady: trust.readStatus?.evidence === "read"
  };
}

async function safeRuntimeRead(read) {
  if (typeof read !== "function") {
    return {
      ok: false,
      value: null,
      error: {
        code: "runtime_read_unavailable",
        message: "runtime read method is unavailable"
      }
    };
  }
  try {
    const value = await read();
    if (value === undefined || value === null) {
      return {
        ok: false,
        value: null,
        error: {
          code: "runtime_read_unavailable",
          message: "runtime read returned no value"
        }
      };
    }
    return {
      ok: true,
      value,
      error: null
    };
  } catch (error) {
    return {
      ok: false,
      value: null,
      error: {
        code: error?.code ?? "runtime_read_blocked",
        message: error instanceof Error ? redactBlockerMessage(error.message) : String(error)
      }
    };
  }
}

function latestM3AuditEvent(events) {
  return [...events].reverse().find((event) =>
    event?.operationId &&
    event?.traceId &&
    (event.action === "m3.io.do.write" || event.action === "m3.io.di.read" || String(event.metadata?.route ?? "").includes("res_boxsimu_1:DO1"))
  ) ?? null;
}

function latestM3EvidenceRecord(records) {
  return [...records].reverse().find((record) =>
    record?.operationId &&
    (record.serviceId === CLOUD_API_SERVICE_ID || String(record.metadata?.route ?? "").includes("res_boxsimu_1:DO1"))
  ) ?? null;
}

function defaultM3Gateways({ sourceKind, observedAt }) {
  return [
    {
      id: M3_IO_CHAIN.sourceGatewayId,
      role: "source",
      online: false,
      observable: false,
      sessionId: M3_IO_CHAIN.sourceGatewaySessionId,
      lastSeenAt: observedAt,
      sourceKind,
      lastError: "未验证"
    },
    {
      id: M3_IO_CHAIN.targetGatewayId,
      role: "target",
      online: false,
      observable: false,
      sessionId: M3_IO_CHAIN.targetGatewaySessionId,
      lastSeenAt: observedAt,
      sourceKind,
      lastError: "未验证"
    }
  ];
}

function defaultM3Boxes({ sourceKind, observedAt }) {
  return [
    {
      id: M3_IO_CHAIN.sourceBoxId,
      resourceId: M3_IO_CHAIN.sourceResourceId,
      gatewayId: M3_IO_CHAIN.sourceGatewayId,
      gatewaySessionId: M3_IO_CHAIN.sourceGatewaySessionId,
      online: false,
      observable: false,
      sourceKind,
      observedAt,
      ports: {
        [M3_IO_CHAIN.sourcePort]: {
          value: null,
          direction: "output",
          source: "未验证",
          sourceKind,
          observedAt
        }
      }
    },
    {
      id: M3_IO_CHAIN.targetBoxId,
      resourceId: M3_IO_CHAIN.targetResourceId,
      gatewayId: M3_IO_CHAIN.targetGatewayId,
      gatewaySessionId: M3_IO_CHAIN.targetGatewaySessionId,
      online: false,
      observable: false,
      sourceKind,
      observedAt,
      ports: {
        [M3_IO_CHAIN.targetPort]: {
          value: null,
          direction: "input",
          source: "未验证",
          sourceKind,
          observedAt
        }
      }
    }
  ];
}

function defaultM3PatchPanel({ observedAt }) {
  return {
    serviceId: M3_IO_CHAIN.patchPanelServiceId,
    observable: false,
    connectionActive: false,
    connectionVersion: null,
    wiringConfigId: null,
    lastSyncAt: observedAt,
    lastError: "未验证",
    sourceKind: "UNVERIFIED"
  };
}

async function readM3StatusPort({ gateway, command, requestJson, timeoutMs, observedAt, port, direction }) {
  const capability = capabilityFor({ ...command, action: "di.read", port }, {
    boxId: command.boxId,
    resourceId: command.resourceId
  });
  const dispatch = await invokeGateway(gateway.url, {
    operation: "hardware.port.read",
    projectId: M3_IO_CHAIN.projectId,
    gatewaySessionId: gateway.gatewaySessionId,
    resourceId: command.resourceId,
    boxId: command.boxId,
    capabilityId: capability.capabilityId,
    port,
    operationId: `op_m3_status_read_${port.toLowerCase()}_${randomUUID()}`,
    traceId: `trc_m3_status_read_${port.toLowerCase()}_${randomUUID()}`,
    actorType: "service",
    actorId: `svc_${CLOUD_API_SERVICE_ID}`,
    source: "cloud-api-m3-status"
  }, { requestJson, timeoutMs });

  if (!dispatch.ok || dispatch.body?.accepted !== true) {
    return unverifiedPortRead({
      port,
      direction,
      source: "gateway-simu",
      reason: `gateway-simu ${port} 读取失败：${dispatch.error ?? dispatch.body?.error?.message ?? "unknown"}`,
      observedAt
    });
  }

  return {
    port: {
      value: readValueFromGatewayResult(dispatch.body),
      direction,
      source: port === M3_IO_CHAIN.targetPort ? "gateway-simu/patch-panel" : "gateway-simu",
      sourceKind: "DEV-LIVE",
      observedAt: dispatch.body?.result?.state?.updatedAt ?? dispatch.body?.state?.updatedAt ?? observedAt,
      auditId: dispatch.body?.auditId ?? dispatch.body?.result?.auditId ?? null,
      evidenceId: dispatch.body?.evidenceId ?? dispatch.body?.result?.evidenceId ?? null,
      state: dispatch.body?.result?.state ?? dispatch.body?.state ?? null
    },
    error: null
  };
}

function unverifiedPortRead({ port, direction, source, reason, observedAt }) {
  return {
    port: {
      value: null,
      direction,
      source,
      sourceKind: "UNVERIFIED",
      observedAt,
      lastError: reason
    },
    error: reason
  };
}

function gatewayStatusEntry(gateway, { id, sessionId, role, observedAt }) {
  return {
    id: gateway.gatewayId ?? id,
    role,
    online: gateway.available === true,
    observable: gateway.available === true,
    sessionId: gateway.gatewaySessionId ?? sessionId,
    lastSeenAt: gateway.status?.lastHeartbeatAt ?? gateway.status?.updatedAt ?? gateway.status?.session?.lastSeenAt ?? observedAt,
    sourceKind: gateway.available === true ? "DEV-LIVE" : "UNVERIFIED",
    lastError: gateway.available === true ? null : gateway.reason,
    blocker: gateway.available === true ? null : gateway.code ?? M3_IO_BLOCKER_CODES.gatewayUnavailable
  };
}

function boxStatusEntry({ gateway, box, boxId, resourceId, gatewayId, gatewaySessionId, observedAt, ports }) {
  return {
    id: box?.boxId ?? boxId,
    resourceId: box?.resourceId ?? resourceId,
    gatewayId: gateway.gatewayId ?? gatewayId,
    gatewaySessionId: gateway.gatewaySessionId ?? gatewaySessionId,
    online: gateway.available === true && Boolean(box),
    observable: gateway.available === true && Boolean(box),
    sourceKind: gateway.available === true && box ? "DEV-LIVE" : "UNVERIFIED",
    observedAt,
    ports
  };
}

function patchPanelStatusEntry({ statusResponse, wiringResponse, observedAt }) {
  const status = statusResponse.body ?? {};
  const wiring = wiringResponse.body ?? {};
  const connectionActive = statusResponse.ok && wiringResponse.ok && hasExpectedPatchPanelWiring(status) && hasExpectedPatchPanelWiring(wiring);
  const lastError = !statusResponse.ok
    ? `hwlab-patch-panel status 不可读：${statusResponse.error ?? `HTTP ${statusResponse.status}`}`
    : !wiringResponse.ok
      ? `hwlab-patch-panel wiring 不可读：${wiringResponse.error ?? `HTTP ${wiringResponse.status}`}`
      : connectionActive
        ? status.metadata?.evidenceSummary?.diagnostics?.errorCount > 0
          ? "hwlab-patch-panel diagnostics reports errors"
          : null
        : `未确认 active ${M3_IO_CHAIN.sourceResourceId}:${M3_IO_CHAIN.sourcePort} -> ${M3_IO_CHAIN.targetResourceId}:${M3_IO_CHAIN.targetPort}`;
  return {
    serviceId: status.serviceId ?? M3_IO_CHAIN.patchPanelServiceId,
    observable: statusResponse.ok,
    connectionActive,
    connectionVersion: status.metadata?.evidenceSummary?.tickCount ?? status.metadata?.evidenceSummary?.routeCount ?? null,
    wiringConfigId: status.wiringConfigId ?? wiring.wiringConfigId ?? null,
    lastSyncAt: status.metadata?.evidenceSummary?.lastSyncAt ?? status.observedAt ?? wiring.updatedAt ?? observedAt,
    lastError,
    sourceKind: connectionActive ? "DEV-LIVE" : statusResponse.ok ? "UNVERIFIED" : "BLOCKED",
    activeConnections: status.activeConnections ?? [],
    diagnostics: status.metadata?.diagnostics ?? status.metadata?.evidenceSummary?.diagnostics ?? null
  };
}

function readinessCheckFromGateway(id, gateway) {
  return {
    id,
    status: gateway.available ? "pass" : "blocked",
    layer: gateway.role,
    sourceKind: gateway.available ? "DEV-LIVE" : "BLOCKED",
    summary: gateway.available
      ? `${gateway.role} gateway ${gateway.gatewayId}/${gateway.gatewaySessionId} 只读状态可用。`
      : gateway.reason,
    gatewayId: gateway.gatewayId ?? null,
    gatewaySessionId: gateway.gatewaySessionId ?? null,
    url: gateway.url ? redactUrl(gateway.url) : null,
    blocker: gateway.available ? null : gateway.code ?? M3_IO_BLOCKER_CODES.gatewayUnavailable
  };
}

function readinessCheckFromBox(id, box, command) {
  return {
    id,
    status: box ? "pass" : "blocked",
    sourceKind: box ? "DEV-LIVE" : "BLOCKED",
    summary: box
      ? `${command.resourceId}/${command.port} 已在 gateway registry 中可见。`
      : `${command.resourceId}/${command.port} 未在 gateway registry 中可见。`,
    resourceId: box?.resourceId ?? command.resourceId,
    boxId: box?.boxId ?? command.boxId,
    port: command.port,
    blocker: box ? null : M3_IO_BLOCKER_CODES.boxUnavailable
  };
}

function readinessCheckFromPatchPanelStatus(response) {
  const expected = response.ok && hasExpectedPatchPanelWiring(response.body);
  return {
    id: "patch-panel-status",
    status: expected ? "pass" : "blocked",
    sourceKind: expected ? "DEV-LIVE" : "BLOCKED",
    summary: expected
      ? "hwlab-patch-panel status 包含目标 active 接线。"
      : response.ok
        ? "hwlab-patch-panel status 未包含目标 active 接线。"
        : `hwlab-patch-panel status 不可读：${response.error ?? `HTTP ${response.status}`}`,
    serviceId: response.body?.serviceId ?? null,
    state: response.body?.state ?? null,
    blocker: expected ? null : M3_IO_BLOCKER_CODES.wiringMissing
  };
}

function readinessCheckFromPatchPanelWiring(response) {
  const expected = response.ok && hasExpectedPatchPanelWiring(response.body);
  return {
    id: "patch-panel-wiring",
    status: expected ? "pass" : "blocked",
    sourceKind: expected ? "DEV-LIVE" : "BLOCKED",
    summary: expected
      ? "hwlab-patch-panel /wiring 包含目标 active 接线。"
      : response.ok
        ? "hwlab-patch-panel /wiring 未包含目标 active 接线。"
        : `hwlab-patch-panel /wiring 不可读：${response.error ?? `HTTP ${response.status}`}`,
    wiringConfigId: response.body?.wiringConfigId ?? null,
    wiringStatus: response.body?.status ?? null,
    blocker: expected ? null : M3_IO_BLOCKER_CODES.wiringMissing
  };
}

async function writeDo({
  config,
  command,
  capability,
  gateway,
  operation,
  meta,
  runtimeStore,
  runtimeBefore,
  registration,
  operationWrite,
  requestJson,
  now
}) {
  const dispatch = await invokeGateway(gateway.url, {
    operation: "hardware.port.write",
    projectId: M3_IO_CHAIN.projectId,
    gatewaySessionId: gateway.gatewaySessionId,
    resourceId: command.resourceId,
    boxId: command.boxId,
    capabilityId: capability.capabilityId,
    port: command.port,
    value: command.value,
    operationId: operation.operationId,
    traceId: meta.traceId,
    requestId: meta.requestId,
    actorType: "service",
    actorId: `svc_${CLOUD_API_SERVICE_ID}`,
    requestedBy: meta.actorId,
    source: "cloud-api-m3-io-control"
  }, { requestJson, timeoutMs: config.timeoutMs });

  if (!dispatch.ok || dispatch.body?.accepted !== true) {
    return blockedControlResult({
      action: command.action,
      command,
      operation,
      meta,
      runtimeStore,
      runtimeBefore,
      registration,
      operationWrite,
      code: M3_IO_BLOCKER_CODES.dispatchFailed,
      reason: `gateway-simu DO 写入失败：${dispatch.error ?? dispatch.body?.error?.message ?? "unknown"}`,
      gateway,
      dispatch,
      now
    });
  }

  const patchPanel = await tickPatchPanel(config.patchPanelUrl, {
    signals: [
      {
        fromResourceId: M3_IO_CHAIN.sourceResourceId,
        fromPort: M3_IO_CHAIN.sourcePort,
        value: command.value,
        operationId: operation.operationId,
        traceId: meta.traceId
      }
    ]
  }, { requestJson, timeoutMs: config.timeoutMs });
  const patchIssue = classifyPatchPanelResult(patchPanel);
  if (patchIssue) {
    return blockedControlResult({
      action: command.action,
      command,
      operation,
      meta,
      runtimeStore,
      runtimeBefore,
      registration,
      operationWrite,
      code: patchIssue.code,
      reason: patchIssue.reason,
      gateway,
      dispatch,
      patchPanel,
      now
    });
  }

  const targetReadback = await readTargetDiAfterWrite({
    config,
    operation,
    meta,
    requestJson
  });
  if (targetReadback.status !== "succeeded" || targetReadback.value !== command.value) {
    return blockedControlResult({
      action: command.action,
      command,
      operation,
      meta,
      runtimeStore,
      runtimeBefore,
      registration,
      operationWrite,
      code: targetReadback.code ?? M3_IO_BLOCKER_CODES.wiringMissing,
      reason: targetReadback.reason ?? `DI1 回读值 ${String(targetReadback.value)} 与 DO1 写入值 ${String(command.value)} 不一致；不能证明 M3 DO->DI 闭环。`,
      gateway,
      dispatch,
      patchPanel,
      targetReadback,
      now
    });
  }
  const tracePayload = {
    action: command.action,
    command,
    gateway: gatewayResultSummary(gateway),
    dispatch: dispatch.body,
    patchPanel: patchPanel.body,
    targetReadback
  };

  return successControlResult({
    action: command.action,
    command,
    operation,
    meta,
    runtimeStore,
    runtimeBefore,
    registration,
    operationWrite,
    gateway,
    dispatch,
    patchPanel,
    targetReadback,
    tracePayload,
    now
  });
}

async function readDi({
  config,
  command,
  capability,
  gateway,
  operation,
  meta,
  runtimeStore,
  runtimeBefore,
  registration,
  operationWrite,
  requestJson,
  now
}) {
  const dispatch = await invokeGateway(gateway.url, {
    operation: "hardware.port.read",
    projectId: M3_IO_CHAIN.projectId,
    gatewaySessionId: gateway.gatewaySessionId,
    resourceId: command.resourceId,
    boxId: command.boxId,
    capabilityId: capability.capabilityId,
    port: command.port,
    operationId: operation.operationId,
    traceId: meta.traceId,
    requestId: meta.requestId,
    actorType: "service",
    actorId: `svc_${CLOUD_API_SERVICE_ID}`,
    requestedBy: meta.actorId,
    source: "cloud-api-m3-io-control"
  }, { requestJson, timeoutMs: config.timeoutMs });

  if (!dispatch.ok || dispatch.body?.accepted !== true) {
    return blockedControlResult({
      action: command.action,
      command,
      operation,
      meta,
      runtimeStore,
      runtimeBefore,
      registration,
      operationWrite,
      code: M3_IO_BLOCKER_CODES.dispatchFailed,
      reason: `gateway-simu DI 读取失败：${dispatch.error ?? dispatch.body?.error?.message ?? "unknown"}`,
      gateway,
      dispatch,
      now
    });
  }

  const tracePayload = {
    action: command.action,
    command,
    gateway: gatewayResultSummary(gateway),
    dispatch: dispatch.body,
    value: readValueFromGatewayResult(dispatch.body)
  };

  return successControlResult({
    action: command.action,
    command,
    operation,
    meta,
    runtimeStore,
    runtimeBefore,
    registration,
    operationWrite,
    gateway,
    dispatch,
    tracePayload,
    now
  });
}

async function readTargetDiAfterWrite({ config, operation, meta, requestJson }) {
  const gateway = await readGatewaySession(config.gateway2Url, {
    gatewayRole: "target-readback",
    expectedGatewayId: M3_IO_CHAIN.targetGatewayId,
    expectedGatewaySessionId: M3_IO_CHAIN.targetGatewaySessionId,
    requestJson,
    timeoutMs: config.timeoutMs
  });
  if (!gateway.available) {
    return {
      status: "blocked",
      code: gateway.code ?? M3_IO_BLOCKER_CODES.gatewayUnavailable,
      reason: gateway.reason
    };
  }
  const command = normalizeCommand("di.read", {});
  const box = findGatewayBox(gateway.status, command);
  if (!box) {
    return {
      status: "blocked",
      code: M3_IO_BLOCKER_CODES.boxUnavailable,
      reason: `target gateway 已响应，但 ${M3_IO_CHAIN.targetResourceId} 未注册/不可用`
    };
  }
  const capability = capabilityFor(command, box);
  const dispatch = await invokeGateway(gateway.url, {
    operation: "hardware.port.read",
    projectId: M3_IO_CHAIN.projectId,
    gatewaySessionId: gateway.gatewaySessionId,
    resourceId: command.resourceId,
    boxId: command.boxId,
    capabilityId: capability.capabilityId,
    port: command.port,
    operationId: operation.operationId,
    traceId: meta.traceId,
    requestId: meta.requestId,
    actorType: "service",
    actorId: `svc_${CLOUD_API_SERVICE_ID}`,
    requestedBy: meta.actorId,
    source: "cloud-api-m3-io-control"
  }, { requestJson, timeoutMs: config.timeoutMs });

  return dispatch.ok && dispatch.body?.accepted === true
    ? {
        status: "succeeded",
        value: readValueFromGatewayResult(dispatch.body),
        gatewaySessionId: gateway.gatewaySessionId,
        resourceId: command.resourceId,
        port: command.port,
        result: dispatch.body
      }
    : {
        status: "blocked",
        code: M3_IO_BLOCKER_CODES.dispatchFailed,
        reason: dispatch.error ?? dispatch.body?.error?.message ?? "target DI readback failed"
      };
}

function normalizeAction(action) {
  return String(action ?? "").trim().toLowerCase();
}

function normalizeCommand(action, params = {}) {
  if (action === "do.write") {
    return {
      action,
      gatewayId: params.gatewayId ?? params.sourceGatewayId ?? M3_IO_CHAIN.sourceGatewayId,
      gatewaySessionId: params.gatewaySessionId ?? M3_IO_CHAIN.sourceGatewaySessionId,
      resourceId: params.resourceId ?? M3_IO_CHAIN.sourceResourceId,
      boxId: params.boxId ?? M3_IO_CHAIN.sourceBoxId,
      port: normalizePort(params.port ?? M3_IO_CHAIN.sourcePort),
      value: normalizeBoolean(params.value)
    };
  }

  return {
    action,
    gatewayId: params.gatewayId ?? params.targetGatewayId ?? M3_IO_CHAIN.targetGatewayId,
    gatewaySessionId: params.gatewaySessionId ?? M3_IO_CHAIN.targetGatewaySessionId,
    resourceId: params.resourceId ?? M3_IO_CHAIN.targetResourceId,
    boxId: params.boxId ?? M3_IO_CHAIN.targetBoxId,
    port: normalizePort(params.port ?? M3_IO_CHAIN.targetPort)
  };
}

function validateM3Command(command) {
  if (command.action === "do.write") {
    if (typeof command.value !== "boolean") {
      return "M3 DO 写入值必须是 boolean。";
    }
  }
  return null;
}

function normalizePort(port) {
  return String(port ?? "").trim().toUpperCase();
}

function normalizeBoolean(value) {
  if (typeof value === "boolean") return value;
  if (value === "true") return true;
  if (value === "false") return false;
  return value;
}

function buildRequestMeta(params = {}, context = {}) {
  return {
    traceId: params.traceId || context.traceId || `trc_${randomUUID()}`,
    requestId: params.requestId || context.requestId || `req_${randomUUID()}`,
    actorType: "user",
    actorId: params.actorId || context.actorId || "usr_hwlab_cloud_web",
    serviceId: CLOUD_API_SERVICE_ID,
    environment: ENVIRONMENT_DEV
  };
}

function createOperationRecord({ action, command, capability, gatewaySessionId, meta, now }) {
  const operationId = command.operationId || `op_m3_${action.replace(".", "_")}_${randomUUID()}`;
  const operation = {
    operationId,
    projectId: M3_IO_CHAIN.projectId,
    gatewaySessionId,
    resourceId: command.resourceId,
    capabilityId: capability.capabilityId,
    requestedBy: meta.actorId,
    input: {
      action,
      port: command.port,
      ...(Object.hasOwn(command, "value") ? { value: command.value } : {}),
      route: {
        from: `${M3_IO_CHAIN.sourceResourceId}:${M3_IO_CHAIN.sourcePort}`,
        via: M3_IO_CHAIN.patchPanelServiceId,
        to: `${M3_IO_CHAIN.targetResourceId}:${M3_IO_CHAIN.targetPort}`
      },
      frontendBypass: false
    },
    status: "accepted",
    environment: ENVIRONMENT_DEV,
    requestedAt: now,
    updatedAt: now
  };
  assertProtocolRecord("hardwareOperation", operation);
  return operation;
}

function capabilityFor(command, box = {}) {
  const suffix = command.action === "do.write" ? "write" : "read";
  const portPart = command.port.toLowerCase();
  const boxId = box.boxId ?? command.boxId;
  return {
    capabilityId: `cap_${boxId}_${portPart}_${suffix}`,
    resourceId: command.resourceId,
    projectId: M3_IO_CHAIN.projectId,
    name: command.action,
    description: `M3 ${command.action} ${command.resourceId}:${command.port}`,
    direction: command.action === "do.write" ? "output" : "input",
    valueType: "boolean",
    constraints: {
      port: command.port,
      portFamily: command.action === "do.write" ? "DO" : "DI",
      patchPanelOnlyPropagation: true,
      frontendBypassAllowed: false
    },
    mutatesState: command.action === "do.write",
    createdAt: new Date().toISOString(),
    updatedAt: new Date().toISOString()
  };
}

async function readGatewaySession(url, { gatewayRole, expectedGatewayId, expectedGatewaySessionId, requestJson, timeoutMs }) {
  if (!url) {
    return {
      available: false,
      role: gatewayRole,
      code: M3_IO_BLOCKER_CODES.gatewayUnavailable,
      reason: "gateway 未注册/不可用：cloud-api 未配置 gateway-simu endpoint"
    };
  }
  const response = await requestRuntimeJson(new URL("/status", ensureTrailingSlash(url)).toString(), {
    method: "GET",
    requestJson,
    timeoutMs
  });
  if (!response.ok) {
    return {
      available: false,
      role: gatewayRole,
      url: redactUrl(url),
      code: M3_IO_BLOCKER_CODES.gatewayUnavailable,
      reason: `gateway 未注册/不可用：${response.error ?? `HTTP ${response.status}`}`
    };
  }
  const status = response.body ?? {};
  const gatewaySessionId = status.session?.gatewaySessionId ?? status.gatewaySessionId ?? status.registry?.gatewaySessionId;
  const gatewayId = status.gatewayId ?? status.session?.gatewayId ?? status.registry?.gatewayId;
  if (!gatewaySessionId || !gatewayId) {
    return {
      available: false,
      role: gatewayRole,
      url: redactUrl(url),
      status,
      code: M3_IO_BLOCKER_CODES.gatewayUnavailable,
      reason: "gateway 未注册/不可用：/status 未返回 gatewaySessionId/gatewayId"
    };
  }
  return {
    available: true,
    role: gatewayRole,
    url,
    gatewayId,
    gatewaySessionId,
    status
  };
}

function findGatewayBox(gatewayStatus, command) {
  const boxes = [
    ...(Array.isArray(gatewayStatus?.registry?.boxes) ? gatewayStatus.registry.boxes : []),
    ...(Array.isArray(gatewayStatus?.boxes)
      ? gatewayStatus.boxes.map((item) => typeof item === "string" ? { resourceId: item } : item)
      : [])
  ];
  return boxes.find((box) => box?.resourceId === command.resourceId || box?.boxId === command.boxId) ?? null;
}

async function invokeGateway(gatewayUrl, body, options) {
  return requestRuntimeJson(new URL("/invoke", ensureTrailingSlash(gatewayUrl)).toString(), {
    method: "POST",
    body,
    requestJson: options.requestJson,
    timeoutMs: options.timeoutMs
  });
}

async function tickPatchPanel(patchPanelUrl, body, options) {
  if (!patchPanelUrl) {
    return {
      ok: false,
      status: 0,
      error: "patch-panel endpoint is not configured"
    };
  }
  return requestRuntimeJson(new URL("/sync/tick", ensureTrailingSlash(patchPanelUrl)).toString(), {
    method: "POST",
    body,
    requestJson: options.requestJson,
    timeoutMs: options.timeoutMs
  });
}

function classifyPatchPanelResult(patchPanel) {
  if (!patchPanel.ok) {
    return {
      code: M3_IO_BLOCKER_CODES.patchPanelUnavailable,
      reason: `hwlab-patch-panel 不可用：${patchPanel.error ?? `HTTP ${patchPanel.status}`}`
    };
  }
  if (patchPanel.body?.accepted !== true) {
    const diagnostic = firstDiagnostic(patchPanel.body);
    return {
      code: diagnostic?.code === "target_endpoint_missing" ? M3_IO_BLOCKER_CODES.boxUnavailable : M3_IO_BLOCKER_CODES.patchPanelUnavailable,
      reason: diagnostic?.message ?? "hwlab-patch-panel 拒绝同步；请检查 wiring 和 endpointMap。"
    };
  }
  if (!hasExpectedPatchDelivery(patchPanel.body)) {
    return {
      code: M3_IO_BLOCKER_CODES.wiringMissing,
      reason: `hwlab-patch-panel 未路由 ${M3_IO_CHAIN.sourceResourceId}:${M3_IO_CHAIN.sourcePort} -> ${M3_IO_CHAIN.targetResourceId}:${M3_IO_CHAIN.targetPort}；wiring 不存在或不是 active。`
    };
  }
  return null;
}

function hasExpectedPatchDelivery(body) {
  const routes = Array.isArray(body?.routes) ? body.routes : [];
  return routes.some((route) =>
    (route.deliveries ?? []).some((delivery) =>
      delivery.sourceResourceId === M3_IO_CHAIN.sourceResourceId &&
      delivery.sourcePort === M3_IO_CHAIN.sourcePort &&
      delivery.resourceId === M3_IO_CHAIN.targetResourceId &&
      delivery.port === M3_IO_CHAIN.targetPort &&
      delivery.deliveryStatus !== "failed"
    )
  );
}

function hasExpectedPatchPanelWiring(body) {
  const connections = [
    ...(Array.isArray(body?.activeConnections) ? body.activeConnections : []),
    ...(Array.isArray(body?.connections) ? body.connections : []),
    ...(Array.isArray(body?.wiringConfig?.connections) ? body.wiringConfig.connections : [])
  ];
  const active = body?.state === undefined || !["faulted", "blocked", "disabled"].includes(String(body.state).toLowerCase());
  const wiringActive = body?.status === undefined || String(body.status).toLowerCase() === "active";
  return active && wiringActive && connections.some((connection) => {
    const from = connection.from ?? connection;
    const to = connection.to ?? connection;
    return String(from.resourceId ?? connection.fromResourceId ?? "") === M3_IO_CHAIN.sourceResourceId &&
      normalizePort(from.port ?? connection.fromPort) === M3_IO_CHAIN.sourcePort &&
      String(to.resourceId ?? connection.toResourceId ?? "") === M3_IO_CHAIN.targetResourceId &&
      normalizePort(to.port ?? connection.toPort) === M3_IO_CHAIN.targetPort;
  });
}

function firstDiagnostic(body) {
  return [
    ...(Array.isArray(body?.diagnostics) ? body.diagnostics : []),
    ...(Array.isArray(body?.routes) ? body.routes.flatMap((route) => route.diagnostics ?? []) : [])
  ][0] ?? null;
}
