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

import { createCloudApiServer } from "./server.ts";
import { createCloudRuntimeStore } from "../db/runtime-store.ts";
import { createGreenM3StatusRuntimeStore, m3ReadinessRequestJson } from "./server-test-helpers.ts";

test("cloud api /v1 exposes M3 IO control contract without generic frontend hardware RPC", async () => {
  const server = createCloudApiServer({
    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: m3ReadinessRequestJson
  });
  await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));

  try {
    const { port } = server.address();
    const index = await fetch(`http://127.0.0.1:${port}/v1`);
    assert.equal(index.status, 200);
    const indexPayload = await index.json();
    assert.equal(indexPayload.m3IoControl.route, "/v1/m3/io");
    assert.equal(indexPayload.m3IoControl.boundaries.directFrontendGatewayOrBoxAccess, false);
    assert.ok(indexPayload.methods.includes("m3.io.do.write"));
    assert.ok(indexPayload.methods.includes("m3.io.di.read"));

    const contract = await fetch(`http://127.0.0.1:${port}/v1/m3/io`);
    assert.equal(contract.status, 200);
    const payload = await contract.json();
    assert.equal(payload.status, "available");
    assert.equal(payload.controlReady, true);
    assert.equal(payload.readiness.status, "ready");
    assert.equal(payload.chain.sourceResourceId, "res_boxsimu_1");
    assert.equal(payload.chain.targetResourceId, "res_boxsimu_2");
    assert.equal(payload.boundaries.frontendCallsOnly, "/v1/m3/io");
    assert.equal(payload.boundaries.patchPanelOwnsPropagation, true);
    assert.equal(payload.boundaries.genericHardwareRpcExposedToFrontend, false);

    const statusResponse = await fetch(`http://127.0.0.1:${port}/v1/m3/status`);
    assert.equal(statusResponse.status, 200);
    const statusPayload = await statusResponse.json();
    assert.equal(statusPayload.route, "/v1/m3/status");
    assert.equal(statusPayload.chain.sourceResourceId, "res_boxsimu_1");
    assert.equal(statusPayload.chain.targetResourceId, "res_boxsimu_2");
    assert.equal(statusPayload.gateways.length, 2);
    assert.equal(statusPayload.gateways.every((gateway) => gateway.online === true), true);
    assert.equal(statusPayload.boxes.find((box) => box.resourceId === "res_boxsimu_1").ports.DO1.value, false);
    assert.equal(statusPayload.boxes.find((box) => box.resourceId === "res_boxsimu_2").ports.DI1.value, false);
    assert.equal(statusPayload.patchPanel.connectionActive, true);
    assert.equal(statusPayload.io.status, "live");
    assert.equal(statusPayload.io.do1.resourceId, "res_boxsimu_1");
    assert.equal(statusPayload.io.do1.port, "DO1");
    assert.equal(statusPayload.io.do1.value, false);
    assert.equal(statusPayload.io.do1.sourceKind, "DEV-LIVE");
    assert.equal(statusPayload.io.di1.resourceId, "res_boxsimu_2");
    assert.equal(statusPayload.io.di1.port, "DI1");
    assert.equal(statusPayload.io.di1.value, false);
    assert.equal(statusPayload.io.di1.sourceKind, "DEV-LIVE");
    assert.equal(statusPayload.io.patchPanel.serviceId, "hwlab-patch-panel");
    assert.equal(statusPayload.io.patchPanel.connectionActive, true);
    assert.equal(statusPayload.operation.status, "blocked");
    assert.equal(statusPayload.operation.operationId, null);
    assert.equal(statusPayload.operation.blocker, "runtime_durable_adapter_missing");
    assert.equal(statusPayload.trace.status, "blocked");
    assert.equal(statusPayload.trace.traceId, null);
    assert.equal(statusPayload.trace.blocker, "runtime_durable_adapter_missing");
    assert.equal(statusPayload.audit.status, "blocked");
    assert.equal(statusPayload.audit.auditId, null);
    assert.equal(statusPayload.audit.blocker, "runtime_durable_adapter_missing");
    assert.equal(statusPayload.runtimeDurable.status, "blocked");
    assert.equal(statusPayload.runtimeDurable.green, false);
    assert.equal(statusPayload.runtimeDurable.blocker, "runtime_durable_adapter_missing");
    assert.equal(statusPayload.runtimeDurable.readStatus.audit, "read");
    assert.equal(statusPayload.runtimeDurable.readStatus.evidence, "read");
    assert.equal(statusPayload.runtimeDurable.auditReady, true);
    assert.equal(statusPayload.runtimeDurable.evidenceReady, true);
    assert.equal(statusPayload.trust.durableStatus, "blocked");
    assert.notEqual(statusPayload.trust.durableStatus, "green");
    assert.equal(statusPayload.trust.runtime.blocker, "runtime_durable_adapter_missing");
    assert.equal(statusPayload.boundaries.frontendCallsOnly, "/v1/m3/status");
    assert.equal(statusPayload.boundaries.directFrontendGatewayOrBoxAccess, false);
  } finally {
    await new Promise((resolve, reject) => {
      server.close((error) => (error ? reject(error) : resolve()));
    });
  }
});

test("cloud api /v1/m3/status clears nested runtime blocker when trusted durable evidence is green", async () => {
  const server = createCloudApiServer({
    runtimeStore: createGreenM3StatusRuntimeStore({ blocker: "runtime_durable_not_green" }),
    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: m3ReadinessRequestJson
  });
  await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));

  try {
    const { port } = server.address();
    const statusResponse = await fetch(`http://127.0.0.1:${port}/v1/m3/status`);
    assert.equal(statusResponse.status, 200);
    const statusPayload = await statusResponse.json();

    assert.equal(statusPayload.contractVersion, "m3-status-v1");
    assert.equal(statusPayload.status, "live");
    assert.equal(statusPayload.sourceKind, "DEV-LIVE");
    assert.equal(statusPayload.patchPanel.connectionActive, true);
    assert.equal(statusPayload.trust.durableStatus, "green");
    assert.equal(statusPayload.trust.blocker, null);
    assert.equal(statusPayload.trust.runtime.status, "ready");
    assert.equal(statusPayload.trust.runtime.blocker, null);
    assert.equal(statusPayload.trust.operationId, "op_m3_di_read_75fc664f-e94f-41e8-816e-aff9fdb3666f");
    assert.equal(statusPayload.trust.traceId, "trc_hotfix_307_verify_1779527747043_read_false");
    assert.equal(statusPayload.trust.auditId, "aud_m3_di_read_d5afd84b-3a9d-475f-894f-dff8ea5f49ff_succeeded");
    assert.equal(statusPayload.trust.evidenceId, "evd_m3_di_read_75fc664f-e94f-41e8-816e-aff9fdb3666f_succeeded");
    assert.equal(statusPayload.operation.status, "read");
    assert.equal(statusPayload.operation.operationId, "op_m3_di_read_75fc664f-e94f-41e8-816e-aff9fdb3666f");
    assert.equal(statusPayload.operation.blocker, null);
    assert.equal(statusPayload.trace.status, "read");
    assert.equal(statusPayload.trace.traceId, "trc_hotfix_307_verify_1779527747043_read_false");
    assert.equal(statusPayload.trace.blocker, null);
    assert.equal(statusPayload.audit.status, "read");
    assert.equal(statusPayload.audit.auditId, "aud_m3_di_read_d5afd84b-3a9d-475f-894f-dff8ea5f49ff_succeeded");
    assert.equal(statusPayload.audit.blocker, null);
    assert.equal(statusPayload.runtimeDurable.status, "green");
    assert.equal(statusPayload.runtimeDurable.green, true);
    assert.equal(statusPayload.runtimeDurable.blocker, null);
    assert.equal(statusPayload.runtimeDurable.auditReady, true);
    assert.equal(statusPayload.runtimeDurable.evidenceReady, true);
  } finally {
    await new Promise((resolve, reject) => {
      server.close((error) => (error ? reject(error) : resolve()));
    });
  }
});

test("cloud api /v1/diagnostics/gate exposes Chinese single-table rows from live backend aggregation", async () => {
  const runtimeStore = createCloudRuntimeStore({
    now: () => "2026-05-23T00:00:00.000Z"
  });
  const server = createCloudApiServer({
    runtimeStore,
    env: {
      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",
      HWLAB_COMMIT_ID: "abc123456789",
      HWLAB_IMAGE_TAG: "abc1234"
    },
    m3IoRequestJson: async () => ({
      ok: false,
      status: 503,
      body: {
        error: {
          message: "gateway offline"
        }
      }
    })
  });
  await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));

  try {
    const { port } = server.address();
    const response = await fetch(`http://127.0.0.1:${port}/v1/diagnostics/gate`);
    assert.equal(response.status, 200);
    const payload = await response.json();
    assert.equal(payload.route, "/v1/diagnostics/gate");
    assert.equal(payload.contractVersion, "gate-diagnostics-table-v1");
    assert.equal(payload.sourceKind, "LIVE-BACKEND");
    assert.equal(payload.liveBackend, true);
    assert.deepEqual(payload.columns, ["类别", "检查项", "状态", "归因对象", "关键细节", "证据/trace", "更新时间", "下一步"]);
    assert.equal(payload.safety.frontendHardcodedRows, false);
    assert.equal(payload.safety.sourceFixturePromoted, false);
    assert.equal(payload.safety.blockedIsGreen, false);
    assert.equal(payload.safety.hardwareControlSurface, false);
    assert.equal(payload.safety.codeAgentConversationSurface, false);
    assert.ok(payload.rows.length >= 6);
    assert.ok(payload.rows.every((row) => row.sourceKind === "LIVE-BACKEND"));
    assert.ok(payload.rows.every((row) => ["通过", "阻塞", "失败", "待验证", "信息"].includes(row.status)));
    assert.ok(payload.rows.some((row) => row.category === "M3" && row.status === "阻塞"));
    assert.ok(payload.rows.some((row) => row.check === "/health/live readiness"));
    assert.ok(payload.rows.some((row) => row.check === "audit.event.query"));
    assert.ok(payload.rows.some((row) => row.check === "evidence.record.query"));
    assert.ok(payload.rows.some((row) => row.check === "Cloud API 实况身份" && row.status === "通过"));
    assert.equal(
      payload.rows.some((row) => row.status === "阻塞" && row.statusKey === "pass"),
      false,
      "blocked rows must not be mapped to pass"
    );
  } finally {
    await new Promise((resolve, reject) => {
      server.close((error) => (error ? reject(error) : resolve()));
    });
  }
});

test("cloud api /v1/m3/io returns structured blocked payload when gateway is unavailable", async () => {
  const runtimeStore = createCloudRuntimeStore({
    now: () => "2026-05-23T00:00:00.000Z"
  });
  const server = createCloudApiServer({
    runtimeStore,
    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: async () => ({
      ok: false,
      status: 503,
      body: {
        error: {
          message: "gateway offline"
        }
      }
    })
  });
  await new Promise((resolve) => server.listen(0, "127.0.0.1", resolve));

  try {
    const { port } = server.address();
    const response = await fetch(`http://127.0.0.1:${port}/v1/m3/io`, {
      method: "POST",
      headers: {
        "content-type": "application/json",
        "x-trace-id": "trc_http_m3_gateway_missing",
        "x-actor-id": "usr_http_m3"
      },
      body: JSON.stringify({
        action: "do.write",
        value: true
      })
    });

    assert.equal(response.status, 200);
    const payload = await response.json();
    assert.equal(payload.status, "blocked");
    assert.equal(payload.accepted, false);
    assert.equal(payload.route, "/v1/m3/io");
    assert.equal(payload.method, "POST");
    assert.equal(payload.traceId, "trc_http_m3_gateway_missing");
    assert.equal(payload.operationId, null);
    assert.equal(payload.auditId, null);
    assert.equal(payload.evidenceId, null);
    assert.equal(payload.audit.status, "not_written");
    assert.equal(payload.evidence.status, "blocked");
    assert.equal(payload.readback, null);
    assert.match(payload.blocker.zh, /gateway 未注册\/不可用/u);
    assert.equal(payload.controlPath.cloudApi, true);
    assert.equal(payload.controlPath.frontendBypass, false);
    assert.equal(payload.evidenceState.sourceKind, "BLOCKED");
  } finally {
    await new Promise((resolve, reject) => {
      server.close((error) => (error ? reject(error) : resolve()));
    });
  }
});
