import assert from "node:assert/strict";
import { test } from "bun:test";

import { createCloudRuntimeStore } from "../db/runtime-store.ts";
import { createBoxState, createGatewayState } from "../sim/model.mjs";
import { writeBoxPort } from "../sim/l2-runtime.mjs";
import { validateResponse } from "../protocol/index.mjs";
import { handleJsonRpcRequest, SUPPORTED_RPC_METHODS } from "./json-rpc.ts";
import { HWLAB_M3_IO_CAPABILITY_LEVELS } from "../../skills/hwlab-agent-runtime/scripts/src/m3-io-skill-client.mjs";
import {
  M3_IO_BLOCKER_CODES,
  M3_IO_CHAIN,
  M3_IO_RPC_METHODS,
  M3_STATUS_ROUTE,
  describeM3IoControl,
  describeM3IoControlLive,
  describeM3StatusLive,
  handleM3IoControl
} from "./m3-io-control.ts";

const fixedNow = "2026-05-23T00:00:00.000Z";

test("M3 IO control describes a cloud-api-only control surface contract", () => {
  const contract = describeM3IoControl({
    env: {
      HWLAB_M3_IO_CONTROL_ENABLED: "true"
    }
  });

  assert.equal(contract.status, "available");
  assert.equal(contract.route, "/v1/m3/io");
  assert.equal(contract.boundaries.frontendCallsOnly, "/v1/m3/io");
  assert.equal(contract.boundaries.cloudApiDispatchesToGateway, true);
  assert.equal(contract.boundaries.patchPanelOwnsPropagation, true);
  assert.equal(contract.boundaries.directFrontendGatewayOrBoxAccess, false);
  assert.equal(contract.boundaries.genericHardwareRpcExposedToFrontend, false);
  assert.deepEqual(contract.chain, M3_IO_CHAIN);
});

test("M3 IO live descriptor enables controls only after read-only backend readiness is green", async () => {
  const fixture = createM3ControlFixture();
  const contract = await describeM3IoControlLive({
    env: {
      HWLAB_M3_IO_CONTROL_ENABLED: "true",
      HWLAB_M3_GATEWAY_SIMU_1_URL: "http://gateway-1",
      HWLAB_M3_GATEWAY_SIMU_2_URL: "http://gateway-2",
      HWLAB_M3_PATCH_PANEL_URL: "http://patch-panel"
    },
    now: () => fixedNow,
    m3IoRequestJson: fixture.requestJson
  });

  assert.equal(contract.status, "available");
  assert.equal(contract.controlReady, true);
  assert.equal(contract.capabilityLevel, HWLAB_M3_IO_CAPABILITY_LEVELS.ready);
  assert.equal(contract.readiness.capabilityLevel, HWLAB_M3_IO_CAPABILITY_LEVELS.ready);
  assert.equal(contract.readiness.status, "ready");
  assert.equal(contract.readiness.sourceKind, "DEV-LIVE");
  assert.equal(contract.trustReadiness.status, "blocked");
  assert.equal(contract.trustReadiness.blocker.code, "runtime_store_missing");
  assert.equal(contract.blockedReason, null);
  assert.deepEqual(fixture.calls.map((call) => call.path), [
    "/status",
    "/status",
    "/status",
    "/wiring"
  ]);
});

test("M3 IO live descriptor blocks controls with Chinese reason when readiness is not green", async () => {
  const fixture = createM3ControlFixture({
    patchPanelStatusBody: {
      serviceId: "hwlab-patch-panel",
      state: "active",
      activeConnections: []
    },
    patchPanelWiringBody: {
      wiringConfigId: "wir_wrong",
      status: "active",
      connections: []
    }
  });
  const contract = await describeM3IoControlLive({
    env: {
      HWLAB_M3_IO_CONTROL_ENABLED: "true",
      HWLAB_M3_GATEWAY_SIMU_1_URL: "http://gateway-1",
      HWLAB_M3_GATEWAY_SIMU_2_URL: "http://gateway-2",
      HWLAB_M3_PATCH_PANEL_URL: "http://patch-panel"
    },
    now: () => fixedNow,
    m3IoRequestJson: fixture.requestJson
  });

  assert.equal(contract.status, "blocked");
  assert.equal(contract.controlReady, false);
  assert.equal(contract.capabilityLevel, HWLAB_M3_IO_CAPABILITY_LEVELS.blocked);
  assert.equal(contract.readiness.capabilityLevel, HWLAB_M3_IO_CAPABILITY_LEVELS.blocked);
  assert.equal(contract.readiness.status, "blocked");
  assert.equal(contract.readiness.sourceKind, "BLOCKED");
  assert.match(contract.blockedReason, /未确认 active|控制面板保持阻塞/u);
  assert.equal(contract.readiness.blocker.code, M3_IO_BLOCKER_CODES.wiringMissing);
  assert.equal(contract.readiness.capabilityBlocker.category, "patch_panel_wiring");
});

test("M3 IO live descriptor classifies gateway unavailable and patch-panel unavailable blockers", async () => {
  const gatewayBlocked = await describeM3IoControlLive({
    env: {
      HWLAB_M3_IO_CONTROL_ENABLED: "true",
      HWLAB_M3_GATEWAY_SIMU_1_URL: "http://gateway-1",
      HWLAB_M3_GATEWAY_SIMU_2_URL: "http://gateway-2",
      HWLAB_M3_PATCH_PANEL_URL: "http://patch-panel"
    },
    now: () => fixedNow,
    m3IoRequestJson: async (url) => {
      if (url.startsWith("http://gateway-1")) {
        return {
          ok: false,
          status: 503,
          body: { error: { message: "gateway unavailable" } }
        };
      }
      throw new Error("readiness must stop at the first gateway blocker");
    }
  });

  assert.equal(gatewayBlocked.status, "blocked");
  assert.equal(gatewayBlocked.controlReady, false);
  assert.equal(gatewayBlocked.capabilityLevel, HWLAB_M3_IO_CAPABILITY_LEVELS.blocked);
  assert.equal(gatewayBlocked.readiness.blocker.code, M3_IO_BLOCKER_CODES.gatewayUnavailable);
  assert.equal(gatewayBlocked.readiness.capabilityBlocker.category, "gateway_session");
  assert.match(gatewayBlocked.blockedReason, /gateway 未注册\/不可用/u);

  const fixture = createM3ControlFixture();
  const patchPanelBlocked = await describeM3IoControlLive({
    env: {
      HWLAB_M3_IO_CONTROL_ENABLED: "true",
      HWLAB_M3_GATEWAY_SIMU_1_URL: "http://gateway-1",
      HWLAB_M3_GATEWAY_SIMU_2_URL: "http://gateway-2",
      HWLAB_M3_PATCH_PANEL_URL: "http://patch-panel"
    },
    now: () => fixedNow,
    m3IoRequestJson: async (url, request) => {
      if (url === "http://patch-panel/status") {
        return {
          ok: false,
          status: 503,
          body: { error: { message: "patch-panel unavailable" } }
        };
      }
      return fixture.requestJson(url, request);
    }
  });

  assert.equal(patchPanelBlocked.status, "blocked");
  assert.equal(patchPanelBlocked.capabilityLevel, HWLAB_M3_IO_CAPABILITY_LEVELS.blocked);
  assert.equal(patchPanelBlocked.readiness.blocker.code, M3_IO_BLOCKER_CODES.patchPanelUnavailable);
  assert.equal(patchPanelBlocked.readiness.capabilityBlocker.category, "patch_panel");
  assert.match(patchPanelBlocked.blockedReason, /hwlab-patch-panel 不可用/u);
});

test("M3 status live descriptor aggregates gateways, boxes, patch-panel, IO values, and blocked trust", async () => {
  const fixture = createM3ControlFixture();
  writeBoxPort({
    state: fixture.box1,
    port: "DO1",
    value: true,
    source: "gateway-simu"
  });
  writeBoxPort({
    state: fixture.box2,
    port: "DI1",
    value: true,
    source: "patch-panel",
    sourceResourceId: "res_boxsimu_1",
    sourcePort: "DO1",
    propagatedBy: "hwlab-patch-panel"
  });

  const status = await describeM3StatusLive({
    runtimeStore: createBlockedDurableRuntimeStore({ blocker: "runtime_durable_adapter_auth_blocked" }),
    env: {
      HWLAB_M3_IO_CONTROL_ENABLED: "true",
      HWLAB_M3_GATEWAY_SIMU_1_URL: "http://gateway-1",
      HWLAB_M3_GATEWAY_SIMU_2_URL: "http://gateway-2",
      HWLAB_M3_PATCH_PANEL_URL: "http://patch-panel"
    },
    now: () => fixedNow,
    m3IoRequestJson: fixture.requestJson
  });

  assert.equal(status.route, M3_STATUS_ROUTE);
  assert.equal(status.status, "blocked");
  assert.equal(status.sourceKind, "DEV-LIVE");
  assert.equal(status.chain.sourceResourceId, "res_boxsimu_1");
  assert.equal(status.boundaries.frontendCallsOnly, "/v1/m3/status");
  assert.equal(status.boundaries.directFrontendGatewayOrBoxAccess, false);
  assert.deepEqual(status.gateways.map((gateway) => [gateway.id, gateway.online, gateway.sessionId, gateway.sourceKind]), [
    ["gwsimu_1", true, "gws_gwsimu_1", "DEV-LIVE"],
    ["gwsimu_2", true, "gws_gwsimu_2", "DEV-LIVE"]
  ]);
  const box1 = status.boxes.find((box) => box.resourceId === "res_boxsimu_1");
  const box2 = status.boxes.find((box) => box.resourceId === "res_boxsimu_2");
  assert.equal(box1.online, true);
  assert.equal(box1.ports.DO1.value, true);
  assert.equal(box1.ports.DO1.sourceKind, "DEV-LIVE");
  assert.equal(box2.online, true);
  assert.equal(box2.ports.DI1.value, true);
  assert.equal(box2.ports.DI1.sourceKind, "DEV-LIVE");
  assert.equal(box2.ports.DI1.source, "gateway-simu/patch-panel");
  assert.equal(status.patchPanel.serviceId, "hwlab-patch-panel");
  assert.equal(status.patchPanel.connectionActive, true);
  assert.equal(status.patchPanel.wiringConfigId, "wir_m3_do1_di1");
  assert.equal(status.patchPanel.lastError, null);
  assert.equal(status.io.status, "live");
  assert.equal(status.io.sourceKind, "DEV-LIVE");
  assert.equal(status.io.do1.resourceId, "res_boxsimu_1");
  assert.equal(status.io.do1.port, "DO1");
  assert.equal(status.io.do1.value, true);
  assert.equal(status.io.do1.sourceKind, "DEV-LIVE");
  assert.equal(status.io.di1.resourceId, "res_boxsimu_2");
  assert.equal(status.io.di1.port, "DI1");
  assert.equal(status.io.di1.value, true);
  assert.equal(status.io.di1.sourceKind, "DEV-LIVE");
  assert.equal(status.io.patchPanel.serviceId, "hwlab-patch-panel");
  assert.equal(status.io.patchPanel.connectionActive, true);
  assert.equal(status.operation.status, "blocked");
  assert.equal(status.operation.operationId, null);
  assert.equal(status.operation.blocker, "runtime_durable_adapter_auth_blocked");
  assert.equal(status.trace.status, "blocked");
  assert.equal(status.trace.traceId, null);
  assert.equal(status.trace.blocker, "runtime_durable_adapter_auth_blocked");
  assert.equal(status.audit.status, "blocked");
  assert.equal(status.audit.auditId, null);
  assert.equal(status.audit.readStatus, "blocked");
  assert.equal(status.runtimeDurable.status, "blocked");
  assert.equal(status.runtimeDurable.green, false);
  assert.equal(status.runtimeDurable.blocker, "runtime_durable_adapter_auth_blocked");
  assert.equal(status.trust.durableStatus, "blocked");
  assert.equal(status.trust.blocker, "runtime_durable_adapter_auth_blocked");
  assert.equal(status.trust.runtime.blocker, "runtime_durable_adapter_auth_blocked");
  assert.equal(status.trust.evidenceId, null);
  assert.equal(status.blocker.code, "runtime_durable_adapter_auth_blocked");
  assert.deepEqual(fixture.calls.map((call) => call.path), [
    "/status",
    "/status",
    "/status",
    "/wiring",
    "/invoke",
    "/invoke"
  ]);
});

test("M3 status clears nested runtime blocker when trusted durable evidence is green", async () => {
  const fixture = createM3ControlFixture();
  const status = await describeM3StatusLive({
    runtimeStore: createGreenDurableRuntimeStore({ blocker: M3_IO_BLOCKER_CODES.runtimeDurableBlocked }),
    env: {
      HWLAB_M3_IO_CONTROL_ENABLED: "true",
      HWLAB_M3_GATEWAY_SIMU_1_URL: "http://gateway-1",
      HWLAB_M3_GATEWAY_SIMU_2_URL: "http://gateway-2",
      HWLAB_M3_PATCH_PANEL_URL: "http://patch-panel"
    },
    now: () => fixedNow,
    m3IoRequestJson: fixture.requestJson
  });

  assert.equal(status.status, "live");
  assert.equal(status.sourceKind, "DEV-LIVE");
  assert.equal(status.patchPanel.connectionActive, true);
  assert.equal(status.trust.durableStatus, "green");
  assert.equal(status.trust.blocker, null);
  assert.equal(status.trust.runtime.status, "ready");
  assert.equal(status.trust.runtime.durable, true);
  assert.equal(status.trust.runtime.ready, true);
  assert.equal(status.trust.runtime.liveRuntimeEvidence, true);
  assert.equal(status.trust.runtime.blocker, null);
  assert.equal(status.blocker, null);
  assert.equal(status.trust.operationId, "op_m3_di_read_75fc664f-e94f-41e8-816e-aff9fdb3666f");
  assert.equal(status.trust.traceId, "trc_hotfix_307_verify_1779527747043_read_false");
  assert.equal(status.trust.auditId, "aud_m3_di_read_d5afd84b-3a9d-475f-894f-dff8ea5f49ff_succeeded");
  assert.equal(status.trust.evidenceId, "evd_m3_di_read_75fc664f-e94f-41e8-816e-aff9fdb3666f_succeeded");
  assert.equal(status.operation.status, "read");
  assert.equal(status.operation.operationId, "op_m3_di_read_75fc664f-e94f-41e8-816e-aff9fdb3666f");
  assert.equal(status.operation.blocker, null);
  assert.equal(status.trace.status, "read");
  assert.equal(status.trace.traceId, "trc_hotfix_307_verify_1779527747043_read_false");
  assert.equal(status.trace.blocker, null);
  assert.equal(status.audit.status, "read");
  assert.equal(status.audit.auditId, "aud_m3_di_read_d5afd84b-3a9d-475f-894f-dff8ea5f49ff_succeeded");
  assert.equal(status.audit.blocker, null);
  assert.equal(status.runtimeDurable.status, "green");
  assert.equal(status.runtimeDurable.green, true);
  assert.equal(status.runtimeDurable.blocker, null);
  assert.equal(status.runtimeDurable.auditReady, true);
  assert.equal(status.runtimeDurable.evidenceReady, true);
});

test("M3 status live descriptor keeps SOURCE fallback unverified when patch-panel route is missing", async () => {
  const fixture = createM3ControlFixture({
    patchPanelStatusBody: {
      serviceId: "hwlab-patch-panel",
      state: "active",
      activeConnections: []
    },
    patchPanelWiringBody: {
      wiringConfigId: "wir_wrong",
      status: "active",
      connections: []
    }
  });

  const status = await describeM3StatusLive({
    runtimeStore: createBlockedDurableRuntimeStore(),
    env: {
      HWLAB_M3_IO_CONTROL_ENABLED: "true",
      HWLAB_M3_GATEWAY_SIMU_1_URL: "http://gateway-1",
      HWLAB_M3_GATEWAY_SIMU_2_URL: "http://gateway-2",
      HWLAB_M3_PATCH_PANEL_URL: "http://patch-panel"
    },
    now: () => fixedNow,
    m3IoRequestJson: fixture.requestJson
  });

  assert.equal(status.status, "blocked");
  assert.equal(status.patchPanel.connectionActive, false);
  assert.equal(status.patchPanel.sourceKind, "UNVERIFIED");
  assert.match(status.patchPanel.lastError, /未确认 active/u);
  assert.equal(status.blocker.code, M3_IO_BLOCKER_CODES.wiringMissing);
  assert.equal(status.boxes[0].ports.DO1.sourceKind, "DEV-LIVE");
  assert.equal(status.boxes[1].ports.DI1.sourceKind, "DEV-LIVE");
  assert.notEqual(status.sourceKind, "SOURCE");
});

test("M3 DO write dispatches through gateway-simu, ticks patch-panel, reads DI, and records audit/evidence fields", async () => {
  const fixture = createM3ControlFixture();
  const runtimeStore = createCloudRuntimeStore({
    now: () => fixedNow
  });

  const result = await handleM3IoControl(
    {
      action: "do.write",
      gatewayId: "gwsimu_1",
      resourceId: "res_boxsimu_1",
      boxId: "boxsimu_1",
      port: "DO1",
      value: true,
      traceId: "trc_m3_control_write_true",
      requestId: "req_m3_control_write_true",
      actorId: "usr_m3_operator"
    },
    {
      runtimeStore,
      now: () => fixedNow,
      env: {
        HWLAB_M3_IO_CONTROL_ENABLED: "true",
        HWLAB_M3_GATEWAY_SIMU_1_URL: "http://gateway-1",
        HWLAB_M3_GATEWAY_SIMU_2_URL: "http://gateway-2",
        HWLAB_M3_PATCH_PANEL_URL: "http://patch-panel"
      },
      requestJson: fixture.requestJson
    }
  );

  assert.equal(result.status, "succeeded");
  assert.equal(result.accepted, true);
  assert.equal(result.route, "/v1/m3/io");
  assert.equal(result.method, "POST");
  assert.equal(result.capabilityLevel, HWLAB_M3_IO_CAPABILITY_LEVELS.ready);
  assert.equal(result.controlReady, true);
  assert.equal(result.action, "do.write");
  assert.equal(result.command.port, "DO1");
  assert.equal(result.command.value, true);
  assert.equal(result.gatewayId, "gwsimu_1");
  assert.equal(result.gatewaySessionId, "gws_gwsimu_1");
  assert.equal(result.resourceId, "res_boxsimu_1");
  assert.equal(result.boxId, "boxsimu_1");
  assert.equal(result.port, "DO1");
  assert.equal(result.value, true);
  assert.deepEqual(result.target, {
    gatewayId: "gwsimu_1",
    gatewaySessionId: "gws_gwsimu_1",
    resourceId: "res_boxsimu_1",
    boxId: "boxsimu_1",
    port: "DO1",
    value: true
  });
  assert.match(result.operationId, /^op_m3_do_write_/u);
  assert.equal(result.traceId, "trc_m3_control_write_true");
  assert.match(result.auditId, /^aud_m3_do_write_/u);
  assert.match(result.evidenceId, /^evd_m3_do_write_/u);
  assert.equal(result.audit.auditId, result.auditId);
  assert.equal(result.audit.status, "written_non_durable");
  assert.equal(result.evidence.evidenceId, result.evidenceId);
  assert.equal(result.evidence.status, "blocked");
  assert.equal(result.readback.value, true);
  assert.equal(result.operationState.status, "succeeded");
  assert.equal(result.auditState.status, "written_non_durable");
  assert.equal(result.auditState.durableStatus.durable, false);
  assert.equal(result.durableStatus.status, "degraded");
  assert.equal(result.durableStatus.redacted, true);
  assert.equal(result.trustBlocker.code, result.durableStatus.blocker);
  assert.equal(result.blockerClassification.category, "runtime_durable");
  assert.equal(result.blockerClassification.redacted, true);
  assert.equal(result.controlPath.cloudApi, true);
  assert.equal(result.controlPath.gatewaySimu, true);
  assert.equal(result.controlPath.boxSimu, true);
  assert.equal(result.controlPath.patchPanel, true);
  assert.equal(result.controlPath.frontendBypass, false);
  assert.equal(result.result.value, true);
  assert.equal(result.result.targetReadback.value, true);
  assert.equal(result.evidenceState.status, "blocked");
  assert.equal(result.evidenceState.sourceKind, "BLOCKED");
  assert.equal(result.evidenceState.writeStatus, "written_non_durable");
  assert.match(result.evidenceState.reason, /runtime durable 未 green/u);
  assert.equal(fixture.box1.ports.DO1.value, true);
  assert.equal(fixture.box2.ports.DI1.value, true);
  assert.deepEqual(fixture.calls.map((call) => call.path), [
    "/status",
    "/invoke",
    "/sync/tick",
    "/status",
    "/invoke"
  ]);
  assert.equal(fixture.calls.every((call) => call.url.startsWith("http://gateway-") || call.url.startsWith("http://patch-panel")), true);
  assert.equal(fixture.calls[1].body.operation, "hardware.port.write");
  assert.equal(fixture.calls[1].body.gatewaySessionId, "gws_gwsimu_1");
  assert.equal(fixture.calls[1].body.resourceId, "res_boxsimu_1");
  assert.equal(fixture.calls[1].body.port, "DO1");
  assert.equal(fixture.calls[1].body.value, true);
  assert.equal(fixture.calls[2].body.signals[0].fromResourceId, "res_boxsimu_1");
  assert.equal(fixture.calls[4].body.operation, "hardware.port.read");
  assert.equal(fixture.calls[4].body.resourceId, "res_boxsimu_2");
  assert.equal(fixture.calls[4].body.port, "DI1");

  const audit = runtimeStore.queryAuditEvents({ projectId: M3_IO_CHAIN.projectId });
  assert.ok(audit.events.some((event) => event.action === "m3.io.do.write"));
  const evidence = runtimeStore.queryEvidenceRecords({ projectId: M3_IO_CHAIN.projectId });
  assert.equal(evidence.count, 1);
  assert.equal(evidence.records[0].operationId, result.operationId);
});

test("M3 DO write can prove control reachability while durable trusted persistence stays blocked", async () => {
  const fixture = createM3ControlFixture();
  const result = await handleM3IoControl(
    {
      action: "do.write",
      gatewayId: "gwsimu_1",
      resourceId: "res_boxsimu_1",
      boxId: "boxsimu_1",
      port: "DO1",
      value: true,
      traceId: "trc_m3_control_durable_blocked",
      requestId: "req_m3_control_durable_blocked",
      actorId: "usr_m3_operator"
    },
    {
      runtimeStore: createBlockedDurableRuntimeStore(),
      now: () => fixedNow,
      env: {
        HWLAB_M3_IO_CONTROL_ENABLED: "true",
        HWLAB_M3_GATEWAY_SIMU_1_URL: "http://gateway-1",
        HWLAB_M3_GATEWAY_SIMU_2_URL: "http://gateway-2",
        HWLAB_M3_PATCH_PANEL_URL: "http://patch-panel"
      },
      requestJson: fixture.requestJson
    }
  );

  assert.equal(result.status, "succeeded");
  assert.equal(result.accepted, true);
  assert.equal(result.capabilityLevel, HWLAB_M3_IO_CAPABILITY_LEVELS.ready);
  assert.equal(result.trustBlocker.code, "runtime_durable_adapter_query_blocked");
  assert.equal(result.result.value, true);
  assert.equal(result.result.targetReadback.value, true);
  assert.equal(result.controlPath.status, "succeeded");
  assert.equal(result.controlPath.patchPanel, true);
  assert.equal(result.evidenceState.status, "blocked");
  assert.equal(result.evidenceState.sourceKind, "BLOCKED");
  assert.equal(result.evidenceState.durable, false);
  assert.equal(result.evidenceState.blocker, "runtime_durable_adapter_query_blocked");
  assert.equal(result.durableStatus.blocker, "runtime_durable_adapter_query_blocked");
  assert.equal(result.blockerClassification.code, "runtime_durable_adapter_query_blocked");
  assert.equal(result.blockerClassification.category, "runtime_durable");
  assert.equal(result.persistence.runtime.status, "blocked");
  assert.equal(result.persistence.runtime.liveRuntimeEvidence, false);
  assert.equal(result.persistence.writes.every((write) => write.written !== true), true);
  assert.match(result.evidenceState.reason, /操作链路可达性与可信持久化分离/u);
});

test("M3 DI read returns structured value through gateway-simu without patch-panel mutation", async () => {
  const fixture = createM3ControlFixture();
  writeBoxPort({
    state: fixture.box2,
    port: "DI1",
    value: true,
    source: "patch-panel"
  });

  const result = await handleM3IoControl(
    {
      action: "di.read",
      gatewayId: "gwsimu_2",
      resourceId: "res_boxsimu_2",
      boxId: "boxsimu_2",
      port: "DI1",
      traceId: "trc_m3_control_read_di",
      requestId: "req_m3_control_read_di",
      actorId: "usr_m3_operator"
    },
    {
      runtimeStore: createCloudRuntimeStore({ now: () => fixedNow }),
      now: () => fixedNow,
      env: {
        HWLAB_M3_IO_CONTROL_ENABLED: "true",
        HWLAB_M3_GATEWAY_SIMU_1_URL: "http://gateway-1",
        HWLAB_M3_GATEWAY_SIMU_2_URL: "http://gateway-2",
        HWLAB_M3_PATCH_PANEL_URL: "http://patch-panel"
      },
      requestJson: fixture.requestJson
    }
  );

  assert.equal(result.status, "succeeded");
  assert.equal(result.accepted, true);
  assert.equal(result.route, "/v1/m3/io");
  assert.equal(result.method, "POST");
  assert.equal(result.action, "di.read");
  assert.equal(result.command.port, "DI1");
  assert.equal(result.gatewayId, "gwsimu_2");
  assert.equal(result.gatewaySessionId, "gws_gwsimu_2");
  assert.equal(result.resourceId, "res_boxsimu_2");
  assert.equal(result.boxId, "boxsimu_2");
  assert.equal(result.port, "DI1");
  assert.equal(result.value, null);
  assert.equal(result.operationState.status, "succeeded");
  assert.equal(result.auditState.status, "written_non_durable");
  assert.equal(result.result.value, true);
  assert.equal(result.readback.status, "succeeded");
  assert.equal(result.readback.value, true);
  assert.equal(result.readback.resourceId, "res_boxsimu_2");
  assert.equal(result.readback.port, "DI1");
  assert.equal(result.controlPath.patchPanel, false);
  assert.deepEqual(fixture.calls.map((call) => call.path), ["/status", "/invoke"]);
});

test("M3 IO control blocks with Chinese reason when gateway session is unavailable", async () => {
  const result = await handleM3IoControl(
    {
      action: "do.write",
      value: true,
      traceId: "trc_m3_gateway_missing",
      requestId: "req_m3_gateway_missing",
      actorId: "usr_m3_operator"
    },
    {
      runtimeStore: createCloudRuntimeStore({ now: () => fixedNow }),
      now: () => fixedNow,
      env: {
        HWLAB_M3_IO_CONTROL_ENABLED: "true",
        HWLAB_M3_GATEWAY_SIMU_1_URL: "http://gateway-1",
        HWLAB_M3_GATEWAY_SIMU_2_URL: "http://gateway-2",
        HWLAB_M3_PATCH_PANEL_URL: "http://patch-panel"
      },
      requestJson: async () => ({
        ok: false,
        status: 503,
        body: {
          error: {
            message: "unreachable"
          }
        }
      })
    }
  );

  assert.equal(result.status, "blocked");
  assert.equal(result.accepted, false);
  assert.equal(result.route, "/v1/m3/io");
  assert.equal(result.method, "POST");
  assert.equal(result.capabilityLevel, HWLAB_M3_IO_CAPABILITY_LEVELS.blocked);
  assert.equal(result.controlReady, false);
  assert.equal(result.blocker.code, M3_IO_BLOCKER_CODES.gatewayUnavailable);
  assert.equal(result.blockerClassification.category, "gateway_session");
  assert.equal(result.audit.auditId, null);
  assert.equal(result.audit.status, "not_written");
  assert.equal(result.evidence.evidenceId, null);
  assert.equal(result.evidence.status, "blocked");
  assert.equal(result.readback, null);
  assert.equal(result.auditState.status, "not_written");
  assert.equal(result.durableStatus.redacted, true);
  assert.match(result.blocker.zh, /gateway 未注册\/不可用/u);
  assert.equal(result.controlPath.cloudApi, true);
  assert.equal(result.controlPath.frontendBypass, false);
});

test("M3 IO control blocks when patch-panel wiring does not deliver DO1 to DI1", async () => {
  const fixture = createM3ControlFixture({
    patchPanelBody: {
      accepted: true,
      routes: [
        {
          accepted: true,
          deliveries: []
        }
      ]
    }
  });

  const result = await handleM3IoControl(
    {
      action: "do.write",
      value: false,
      traceId: "trc_m3_wiring_missing",
      requestId: "req_m3_wiring_missing",
      actorId: "usr_m3_operator"
    },
    {
      runtimeStore: createCloudRuntimeStore({ now: () => fixedNow }),
      now: () => fixedNow,
      env: {
        HWLAB_M3_IO_CONTROL_ENABLED: "true",
        HWLAB_M3_GATEWAY_SIMU_1_URL: "http://gateway-1",
        HWLAB_M3_GATEWAY_SIMU_2_URL: "http://gateway-2",
        HWLAB_M3_PATCH_PANEL_URL: "http://patch-panel"
      },
      requestJson: fixture.requestJson
    }
  );

  assert.equal(result.status, "blocked");
  assert.equal(result.accepted, false);
  assert.equal(result.blocker.code, M3_IO_BLOCKER_CODES.wiringMissing);
  assert.equal(result.blockerClassification.category, "patch_panel_wiring");
  assert.match(result.blocker.zh, /wiring 不存在|未路由/u);
});

test("M3 IO control blocks when patch-panel reports missing target box", async () => {
  const fixture = createM3ControlFixture({
    patchPanelBody: {
      accepted: false,
      diagnostics: [
        {
          code: "target_endpoint_missing",
          message: "target endpoint missing for res_boxsimu_2"
        }
      ],
      routes: []
    }
  });

  const result = await handleM3IoControl(
    {
      action: "do.write",
      value: true,
      traceId: "trc_m3_target_box_missing",
      requestId: "req_m3_target_box_missing",
      actorId: "usr_m3_operator"
    },
    {
      runtimeStore: createCloudRuntimeStore({ now: () => fixedNow }),
      now: () => fixedNow,
      env: {
        HWLAB_M3_IO_CONTROL_ENABLED: "true",
        HWLAB_M3_GATEWAY_SIMU_1_URL: "http://gateway-1",
        HWLAB_M3_GATEWAY_SIMU_2_URL: "http://gateway-2",
        HWLAB_M3_PATCH_PANEL_URL: "http://patch-panel"
      },
      requestJson: fixture.requestJson
    }
  );

  assert.equal(result.status, "blocked");
  assert.equal(result.blocker.code, M3_IO_BLOCKER_CODES.boxUnavailable);
  assert.equal(result.blockerClassification.category, "box_resource");
  assert.match(result.blocker.zh, /target endpoint missing/u);
  assert.equal(result.controlPath.cloudApi, true);
  assert.equal(result.controlPath.frontendBypass, false);
});

test("M3 IO JSON-RPC methods route do.write and di.read through the cloud-api handler", async () => {
  const fixture = createM3ControlFixture();
  const runtimeStore = createCloudRuntimeStore({
    now: () => fixedNow
  });
  const context = {
    runtimeStore,
    now: () => fixedNow,
    env: {
      HWLAB_M3_IO_CONTROL_ENABLED: "true",
      HWLAB_M3_GATEWAY_SIMU_1_URL: "http://gateway-1",
      HWLAB_M3_GATEWAY_SIMU_2_URL: "http://gateway-2",
      HWLAB_M3_PATCH_PANEL_URL: "http://patch-panel"
    },
    m3IoRequestJson: fixture.requestJson
  };

  assert.ok(SUPPORTED_RPC_METHODS.includes(M3_IO_RPC_METHODS.doWrite));
  assert.ok(SUPPORTED_RPC_METHODS.includes(M3_IO_RPC_METHODS.diRead));

  const write = await handleJsonRpcRequest(
    {
      jsonrpc: "2.0",
      id: "req_m3_rpc_write_true",
      method: M3_IO_RPC_METHODS.doWrite,
      params: {
        resourceId: "res_boxsimu_1",
        boxId: "boxsimu_1",
        port: "DO1",
        value: true
      },
      meta: {
        traceId: "trc_m3_rpc_write_true",
        actorId: "usr_m3_rpc",
        serviceId: "hwlab-cloud-api",
        environment: "dev"
      }
    },
    context
  );

  validateResponse(write);
  assert.equal(Object.hasOwn(write, "error"), false, write.error?.message);
  assert.equal(write.result.status, "succeeded");
  assert.equal(write.result.action, "do.write");
  assert.equal(write.result.traceId, "trc_m3_rpc_write_true");
  assert.equal(write.result.resourceId, "res_boxsimu_1");
  assert.equal(write.result.gatewaySessionId, "gws_gwsimu_1");
  assert.equal(write.result.port, "DO1");
  assert.equal(write.result.value, true);
  assert.match(write.result.operationId, /^op_m3_do_write_/u);
  assert.match(write.result.auditId, /^aud_m3_do_write_/u);
  assert.match(write.result.evidenceId, /^evd_m3_do_write_/u);
  assert.equal(write.result.controlPath.frontendBypass, false);
  assert.equal(write.result.evidenceState.sourceKind, "BLOCKED");

  const read = await handleJsonRpcRequest(
    {
      jsonrpc: "2.0",
      id: "req_m3_rpc_read_di",
      method: M3_IO_RPC_METHODS.diRead,
      params: {
        resourceId: "res_boxsimu_2",
        boxId: "boxsimu_2",
        port: "DI1"
      },
      meta: {
        traceId: "trc_m3_rpc_read_di",
        actorId: "usr_m3_rpc",
        serviceId: "hwlab-cloud-api",
        environment: "dev"
      }
    },
    context
  );

  validateResponse(read);
  assert.equal(Object.hasOwn(read, "error"), false, read.error?.message);
  assert.equal(read.result.status, "succeeded");
  assert.equal(read.result.action, "di.read");
  assert.equal(read.result.traceId, "trc_m3_rpc_read_di");
  assert.equal(read.result.gatewayId, "gwsimu_2");
  assert.equal(read.result.gatewaySessionId, "gws_gwsimu_2");
  assert.equal(read.result.resourceId, "res_boxsimu_2");
  assert.equal(read.result.port, "DI1");
  assert.equal(read.result.result.value, true);
  assert.equal(read.result.controlPath.patchPanel, false);
  assert.equal(read.result.evidenceState.sourceKind, "BLOCKED");
  assert.equal(read.result.durableStatus.durable, false);
});

test("M3 IO fixture success cannot be promoted to DEV-LIVE without durable evidence", async () => {
  const fixture = createM3ControlFixture();
  const result = await handleM3IoControl(
    {
      action: "do.write",
      value: true,
      traceId: "trc_m3_fixture_not_dev_live",
      requestId: "req_m3_fixture_not_dev_live",
      actorId: "usr_m3_operator"
    },
    {
      runtimeStore: createCloudRuntimeStore({ now: () => fixedNow }),
      now: () => fixedNow,
      env: {
        HWLAB_M3_IO_CONTROL_ENABLED: "true",
        HWLAB_M3_GATEWAY_SIMU_1_URL: "http://gateway-1",
        HWLAB_M3_GATEWAY_SIMU_2_URL: "http://gateway-2",
        HWLAB_M3_PATCH_PANEL_URL: "http://patch-panel"
      },
      requestJson: fixture.requestJson
    }
  );

  assert.equal(result.status, "succeeded");
  assert.equal(result.result.targetReadback.value, true);
  assert.equal(result.evidenceState.status, "blocked");
  assert.equal(result.evidenceState.sourceKind, "BLOCKED");
  assert.equal(result.durableStatus.durable, false);
  assert.notEqual(result.evidenceState.sourceKind, "DEV-LIVE");
  assert.notEqual(result.auditState.sourceKind, "DEV-LIVE");
});

function createBlockedDurableRuntimeStore({ blocker = "runtime_durable_adapter_query_blocked" } = {}) {
  return {
    async readiness() {
      return {
        adapter: "postgres",
        durable: false,
        durableRequested: true,
        durableCapable: false,
        ready: false,
        status: "blocked",
        blocker,
        reason: "Postgres runtime adapter could not complete a live runtime schema query",
        liveRuntimeEvidence: false,
        fixtureEvidence: false
      };
    }
  };
}

function createGreenDurableRuntimeStore({ blocker = null } = {}) {
  return {
    async readiness() {
      return {
        adapter: "postgres",
        durable: true,
        durableRequested: true,
        durableCapable: true,
        ready: true,
        status: "ready",
        blocker,
        reason: "Postgres durable runtime adapter completed live schema, migration, and read readiness queries",
        liveRuntimeEvidence: true,
        fixtureEvidence: false
      };
    },
    async queryAuditEvents() {
      return {
        events: [
          {
            auditId: "aud_m3_di_read_d5afd84b-3a9d-475f-894f-dff8ea5f49ff_succeeded",
            traceId: "trc_hotfix_307_verify_1779527747043_read_false",
            actorType: "user",
            actorId: "usr_m3_operator",
            action: "m3.io.di.read",
            targetType: "hardware_operation",
            targetId: "op_m3_di_read_75fc664f-e94f-41e8-816e-aff9fdb3666f",
            projectId: M3_IO_CHAIN.projectId,
            gatewaySessionId: M3_IO_CHAIN.targetGatewaySessionId,
            operationId: "op_m3_di_read_75fc664f-e94f-41e8-816e-aff9fdb3666f",
            serviceId: "hwlab-cloud-api",
            outcome: "succeeded",
            metadata: {
              route: "res_boxsimu_1:DO1->hwlab-patch-panel->res_boxsimu_2:DI1"
            },
            environment: "dev",
            occurredAt: fixedNow
          }
        ],
        count: 1
      };
    },
    async queryEvidenceRecords() {
      return {
        records: [
          {
            evidenceId: "evd_m3_di_read_75fc664f-e94f-41e8-816e-aff9fdb3666f_succeeded",
            projectId: M3_IO_CHAIN.projectId,
            operationId: "op_m3_di_read_75fc664f-e94f-41e8-816e-aff9fdb3666f",
            kind: "trace",
            uri: "memory://hwlab-cloud-api/op_m3_di_read_75fc664f-e94f-41e8-816e-aff9fdb3666f/m3-io-control.json",
            sha256: "0".repeat(64),
            serviceId: "hwlab-cloud-api",
            environment: "dev",
            metadata: {
              traceId: "trc_hotfix_307_verify_1779527747043_read_false",
              route: "res_boxsimu_1:DO1->hwlab-patch-panel->res_boxsimu_2:DI1"
            },
            createdAt: fixedNow
          }
        ],
        count: 1
      };
    }
  };
}

function createM3ControlFixture({ patchPanelBody, patchPanelStatusBody, patchPanelWiringBody, gateway1Id = "gwsimu_1", gateway2Id = "gwsimu_2" } = {}) {
  const calls = [];
  const box1 = createBoxState({
    boxId: "boxsimu_1",
    gatewaySessionId: "gws_gwsimu_1",
    ports: ["DO1", "DI1"],
    now: fixedNow
  });
  box1.resource.resourceId = "res_boxsimu_1";
  box1.capabilities = box1.capabilities.map((capability) => ({
    ...capability,
    resourceId: "res_boxsimu_1",
    projectId: M3_IO_CHAIN.projectId
  }));
  box1.projectId = M3_IO_CHAIN.projectId;
  box1.resource.projectId = M3_IO_CHAIN.projectId;

  const box2 = createBoxState({
    boxId: "boxsimu_2",
    gatewaySessionId: "gws_gwsimu_2",
    ports: ["DO1", "DI1"],
    now: fixedNow
  });
  box2.resource.resourceId = "res_boxsimu_2";
  box2.capabilities = box2.capabilities.map((capability) => ({
    ...capability,
    resourceId: "res_boxsimu_2",
    projectId: M3_IO_CHAIN.projectId
  }));
  box2.projectId = M3_IO_CHAIN.projectId;
  box2.resource.projectId = M3_IO_CHAIN.projectId;

  const gateway1 = createGatewayStatus(gateway1Id, "http://gateway-1", [box1]);
  const gateway2 = createGatewayStatus(gateway2Id, "http://gateway-2", [box2]);

  async function requestJson(url, { method = "GET", body } = {}) {
    const parsed = new URL(url);
    calls.push({
      url,
      method,
      path: parsed.pathname,
      body
    });

    if (url.startsWith("http://gateway-1") && parsed.pathname === "/status") {
      return { ok: true, status: 200, body: gateway1 };
    }
    if (url.startsWith("http://gateway-2") && parsed.pathname === "/status") {
      return { ok: true, status: 200, body: gateway2 };
    }
    if (url.startsWith("http://gateway-1") && parsed.pathname === "/invoke") {
      if (body.operation === "hardware.port.read") {
        const state = box1.ports[body.port];
        return {
          ok: true,
          status: 200,
          body: {
            accepted: true,
            operationId: body.operationId,
            traceId: body.traceId,
            boxId: box1.boxId,
            resourceId: box1.resource.resourceId,
            capabilityId: body.capabilityId,
            port: body.port,
            value: state.value,
            state,
            auditId: "aud_box1_read",
            evidenceId: "evd_box1_read"
          }
        };
      }
      return {
        ok: true,
        status: 200,
        body: writeBoxPort({
          state: box1,
          port: body.port,
          capabilityId: body.capabilityId,
          value: body.value,
          source: "gateway-simu"
        })
      };
    }
    if (url.startsWith("http://gateway-2") && parsed.pathname === "/invoke") {
      return {
        ok: true,
        status: 200,
        body: {
          accepted: true,
          operationId: body.operationId,
          traceId: body.traceId,
          boxId: box2.boxId,
          resourceId: box2.resource.resourceId,
          capabilityId: body.capabilityId,
          port: body.port,
          value: box2.ports[body.port].value,
          state: box2.ports[body.port],
          auditId: "aud_box2_read",
          evidenceId: "evd_box2_read"
        }
      };
    }
    if (url.startsWith("http://patch-panel") && parsed.pathname === "/status") {
      return {
        ok: true,
        status: 200,
        body: patchPanelStatusBody ?? {
          serviceId: "hwlab-patch-panel",
          state: "active",
          activeConnections: [
            {
              fromResourceId: "res_boxsimu_1",
              fromPort: "DO1",
              toResourceId: "res_boxsimu_2",
              toPort: "DI1"
            }
          ]
        }
      };
    }
    if (url.startsWith("http://patch-panel") && parsed.pathname === "/wiring") {
      return {
        ok: true,
        status: 200,
        body: patchPanelWiringBody ?? {
          wiringConfigId: "wir_m3_do1_di1",
          status: "active",
          connections: [
            {
              from: {
                resourceId: "res_boxsimu_1",
                port: "DO1"
              },
              to: {
                resourceId: "res_boxsimu_2",
                port: "DI1"
              },
              mode: "exclusive"
            }
          ]
        }
      };
    }
    if (url.startsWith("http://patch-panel") && parsed.pathname === "/sync/tick") {
      if (patchPanelBody) {
        return { ok: true, status: 200, body: patchPanelBody };
      }
      box2.ports.DI1 = {
        ...box2.ports.DI1,
        value: body.signals[0].value,
        source: "patch-panel",
        sourceResourceId: "res_boxsimu_1",
        sourcePort: "DO1",
        propagatedBy: "hwlab-patch-panel",
        updatedAt: fixedNow
      };
      return {
        ok: true,
        status: 200,
        body: {
          accepted: true,
          propagatedBy: "hwlab-patch-panel",
          routeCount: 1,
          deliveryCount: 1,
          routes: [
            {
              accepted: true,
              deliveryCount: 1,
              deliveries: [
                {
                  resourceId: "res_boxsimu_2",
                  port: "DI1",
                  value: body.signals[0].value,
                  sourceResourceId: "res_boxsimu_1",
                  sourcePort: "DO1",
                  deliveryStatus: "applied"
                }
              ]
            }
          ]
        }
      };
    }

    throw new Error(`unexpected request ${method} ${url}`);
  }

  return {
    calls,
    box1,
    box2,
    requestJson
  };
}

function createGatewayStatus(gatewayId, endpoint, boxes) {
  const state = createGatewayState({
    gatewayId,
    endpoint,
    boxes: boxes.map((box) => box.resource.resourceId),
    now: fixedNow
  });
  state.projectId = M3_IO_CHAIN.projectId;
  state.session.projectId = M3_IO_CHAIN.projectId;
  state.registry = {
    gatewayId,
    gatewaySessionId: state.session.gatewaySessionId,
    endpoint,
    boxes: boxes.map((box) => ({
      boxId: box.boxId,
      resourceId: box.resource.resourceId,
      url: `http://${box.boxId}`,
      state: "registered",
      capabilities: box.capabilities
    }))
  };
  return state;
}
