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

import { AUDIT_FIELD_NAMES } from "../audit/index.mjs";
import { ERROR_CODES, validateResponse } from "../protocol/index.mjs";
import { createCloudRuntimeStore } from "../db/runtime-store.ts";
import { handleJsonRpcRequest } from "./json-rpc.ts";

test("unknown JSON-RPC method returns an error envelope with audit fields", async () => {
  const response = await handleJsonRpcRequest({
    jsonrpc: "2.0",
    id: "req_01J00000000000000000000000",
    method: "hardware.operation.cancel",
    params: {
      operationId: "op_01J00000000000000000000000"
    },
    meta: {
      traceId: "trc_01J00000000000000000000000",
      actorId: "usr_01J00000000000000000000000",
      serviceId: "hwlab-cloud-web",
      environment: "dev"
    }
  });

  validateResponse(response);
  assert.equal(response.error.code, ERROR_CODES.methodNotFound);
  assert.equal(Object.hasOwn(response, "result"), false);

  const audit = response.error.data.audit;
  for (const field of AUDIT_FIELD_NAMES) {
    assert.ok(Object.hasOwn(audit, field), `expected audit.${field}`);
  }
  assert.equal(audit.requestId, "req_01J00000000000000000000000");
  assert.equal(audit.actor.id, "usr_01J00000000000000000000000");
  assert.equal(audit.operation, "hardware.operation.cancel");
  assert.equal(audit.target.type, "rpc_method");
  assert.equal(audit.result, "rejected");
});

test("system.health includes the redacted DEV DB env contract", async () => {
  const originalUrl = process.env.HWLAB_CLOUD_DB_URL;
  const originalSslMode = process.env.HWLAB_CLOUD_DB_SSL_MODE;
  delete process.env.HWLAB_CLOUD_DB_URL;
  delete process.env.HWLAB_CLOUD_DB_SSL_MODE;

  try {
    const response = await handleJsonRpcRequest({
      jsonrpc: "2.0",
      id: "req_01J00000000000000000000002",
      method: "system.health",
      params: {},
      meta: {
        traceId: "trc_01J00000000000000000000002",
        serviceId: "hwlab-cloud-web",
        environment: "dev"
      }
    });

    validateResponse(response);
    assert.equal(response.result.status, "degraded");
    assert.equal(response.result.db.status, "blocked");
    assert.equal(response.result.db.connected, false);
    assert.equal(response.result.db.liveConnected, false);
    assert.equal(response.result.db.liveDbEvidence, false);
    assert.equal(response.result.db.connectionAttempted, false);
    assert.equal(response.result.db.connectionResult, "not_attempted_missing_env");
    assert.equal(response.result.db.endpointSource, "secret-url-host");
    assert.equal(response.result.db.optionalPublicDnsAlias.source, "optional-public-dns-alias");
    assert.equal(response.result.db.optionalPublicDnsAlias.requiredForReadiness, false);
    assert.equal(response.result.db.optionalPublicDnsAlias.usedForProbe, false);
    assert.equal(response.result.db.redaction.valuesRedacted, true);
    assert.equal(response.result.db.redaction.secretMaterialRead, false);
    assert.equal(response.result.db.safety.liveDbEvidence, false);
    assert.equal(response.result.runtime.durable, false);
    assert.equal(response.result.runtime.durabilityContract.dbLiveEvidenceIsDurabilityEvidence, false);
    assert.equal(response.result.readiness.contractVersion, "v3");
    assert.equal(response.result.readiness.durability.status, "blocked");
    assert.equal(response.result.readiness.durability.dbLiveEvidenceObserved, false);
    assert.equal(response.result.readiness.durability.dbLiveEvidenceIsDurabilityEvidence, false);
    assert.equal(response.result.readiness.durability.blockedLayer, "adapter");
    assert.ok(response.result.db.missingEnv.includes("HWLAB_CLOUD_DB_URL"));
    assert.equal(response.result.db.secretRefs[0].secretName, "hwlab-cloud-api-dev-db");
    assert.equal(response.result.db.secretRefs[0].secretKey, "database-url");
    assert.equal(response.result.db.secretRefs[0].redacted, true);
  } finally {
    if (originalUrl === undefined) {
      delete process.env.HWLAB_CLOUD_DB_URL;
    } else {
      process.env.HWLAB_CLOUD_DB_URL = originalUrl;
    }
    if (originalSslMode === undefined) {
      delete process.env.HWLAB_CLOUD_DB_SSL_MODE;
    } else {
      process.env.HWLAB_CLOUD_DB_SSL_MODE = originalSslMode;
    }
  }
});

test("JSON-RPC invalid meta reports unknown serviceId reason without leaking secret material", async () => {
  const response = await handleJsonRpcRequest({
    jsonrpc: "2.0",
    id: "req_01J00000000000000000000003",
    method: "hardware.invoke.shell",
    params: {
      projectId: "prj_mvp_topology",
      gatewaySessionId: "gws_DESKTOP-1MHOD9I",
      resourceId: "res_windows_host",
      capabilityId: "cap_windows_cmd_exec",
      input: {
        command: "cmd /c echo should-not-dispatch"
      }
    },
    meta: {
      traceId: "trc_01J00000000000000000000003",
      actorId: "usr_01J00000000000000000000003",
      serviceId: "hwlab-code-agent-hotfix",
      environment: "dev"
    }
  });

  validateResponse(response);
  assert.equal(response.error.code, ERROR_CODES.invalidRequest);
  assert.equal(response.error.message, "Invalid JSON-RPC request");
  assert.match(response.error.data.reason, /unknown serviceId "hwlab-code-agent-hotfix"/u);
  assert.equal(JSON.stringify(response).includes("should-not-dispatch"), false);
  assert.equal(JSON.stringify(response).includes("sk-"), false);
});

test("hardware.invoke.shell dispatch timeout honors requested gateway shell timeout with grace", async () => {
  let observedTimeoutMs = null;
  const response = await handleJsonRpcRequest({
    jsonrpc: "2.0",
    id: "req_01J00000000000000000000004",
    method: "hardware.invoke.shell",
    params: {
      projectId: "prj_mvp_topology",
      operationId: "op_gateway_timeout_requested",
      gatewaySessionId: "gws_DESKTOP-1MHOD9I",
      resourceId: "res_windows_host",
      capabilityId: "cap_windows_cmd_exec",
      input: {
        command: "powershell -NoProfile -Command Start-Sleep -Seconds 45",
        timeoutMs: 180000
      }
    },
    meta: {
      traceId: "trc_01J00000000000000000000004",
      actorId: "usr_01J00000000000000000000004",
      serviceId: "hwlab-cloud-web",
      environment: "dev"
    }
  }, {
    env: {
      HWLAB_GATEWAY_DISPATCH_TIMEOUT_MS: "30000",
      HWLAB_GATEWAY_DISPATCH_TIMEOUT_GRACE_MS: "5000"
    },
    gatewayRegistry: {
      isOnline: (gatewaySessionId) => gatewaySessionId === "gws_DESKTOP-1MHOD9I",
      enqueue: async ({ timeoutMs }) => {
        observedTimeoutMs = timeoutMs;
        return {
          ok: false,
          status: "timed_out",
          error: "forced timeout"
        };
      }
    }
  });

  validateResponse(response);
  assert.equal(observedTimeoutMs, 185000);
  assert.equal(response.result.status, "timed_out");
  assert.equal(response.result.operationId, "op_gateway_timeout_requested");
  assert.equal(response.result.dispatch.dispatchStatus, "timed_out");
  assert.equal(response.result.dispatch.shellExecuted, false);
});

test("cloud-api runtime accepts register/report/invoke and exposes audit/evidence records", async () => {
  const runtimeStore = createCloudRuntimeStore({
    now: () => "2026-05-21T00:00:00.000Z"
  });
  const context = { runtimeStore };
  const meta = {
    traceId: "trc_01J00000000000000000000010",
    actorId: "usr_01J00000000000000000000010",
    serviceId: "hwlab-cloud-web",
    environment: "dev"
  };

  async function rpc(id, method, params = {}) {
    const response = await handleJsonRpcRequest(
      {
        jsonrpc: "2.0",
        id,
        method,
        params,
        meta
      },
      context
    );
    validateResponse(response);
    assert.equal(Object.hasOwn(response, "error"), false, response.error?.message);
    return response.result;
  }

  const gateway = await rpc("req_01J00000000000000000000010", "gateway.session.register", {
    projectId: "prj_01J00000000000000000000010",
    gatewaySessionId: "gws_01J00000000000000000000010",
    gatewayId: "gtw_01J00000000000000000000010",
    serviceId: "hwlab-gateway",
    endpoint: "http://127.0.0.1:7101"
  });
  assert.equal(gateway.registered, true);
  assert.equal(gateway.gatewaySession.status, "connected");
  assert.equal(gateway.auditEvent.action, "gateway.session.register");

  const resource = await rpc("req_01J00000000000000000000011", "box.resource.register", {
    projectId: "prj_01J00000000000000000000010",
    gatewaySessionId: "gws_01J00000000000000000000010",
    resourceId: "res_01J00000000000000000000010",
    boxId: "box_01J00000000000000000000010",
    resourceType: "board",
    state: "available"
  });
  assert.equal(resource.registered, true);
  assert.equal(resource.resource.environment, "dev");

  const capability = await rpc("req_01J00000000000000000000012", "box.capability.report", {
    capabilityId: "cap_01J00000000000000000000010",
    resourceId: "res_01J00000000000000000000010",
    projectId: "prj_01J00000000000000000000010",
    name: "shell.exec",
    direction: "bidirectional",
    valueType: "object",
    mutatesState: true
  });
  assert.equal(capability.reported, true);
  assert.equal(capability.capabilities[0].name, "shell.exec");

  const invoke = await rpc("req_01J00000000000000000000013", "hardware.invoke.shell", {
    projectId: "prj_01J00000000000000000000010",
    gatewaySessionId: "gws_01J00000000000000000000010",
    resourceId: "res_01J00000000000000000000010",
    capabilityId: "cap_01J00000000000000000000010",
    input: {
      command: "echo hwlab"
    }
  });
  assert.equal(invoke.accepted, true);
  assert.equal(invoke.operation.status, "accepted");
  assert.equal(invoke.dispatch.shellExecuted, false);
  assert.equal(invoke.dispatch.dispatchStatus, "not_connected");
  assert.equal(invoke.auditEvent.action, "hardware.invoke.shell");
  assert.equal(invoke.evidenceRecord.kind, "trace");
  assert.match(invoke.evidenceRecord.sha256, /^[a-f0-9]{64}$/);

  const audit = await rpc("req_01J00000000000000000000014", "audit.event.query", {
    projectId: "prj_01J00000000000000000000010"
  });
  assert.equal(audit.count, 5);
  assert.ok(audit.events.some((event) => event.action === "hardware.operation.request"));
  assert.ok(audit.events.some((event) => event.action === "hardware.invoke.shell"));

  const evidence = await rpc("req_01J00000000000000000000015", "evidence.record.query", {
    projectId: "prj_01J00000000000000000000010"
  });
  assert.equal(evidence.count, 1);
  assert.equal(evidence.records[0].operationId, invoke.operationId);
  assert.equal(evidence.records[0].serviceId, "hwlab-cloud-api");
});

test("hardware.operation.request rejects missing registration instead of faking dispatch", async () => {
  const response = await handleJsonRpcRequest({
    jsonrpc: "2.0",
    id: "req_01J00000000000000000000020",
    method: "hardware.operation.request",
    params: {
      projectId: "prj_01J00000000000000000000020",
      gatewaySessionId: "gws_01J00000000000000000000020",
      resourceId: "res_01J00000000000000000000020",
      capabilityId: "cap_01J00000000000000000000020"
    },
    meta: {
      traceId: "trc_01J00000000000000000000020",
      actorId: "usr_01J00000000000000000000020",
      serviceId: "hwlab-cloud-web",
      environment: "dev"
    }
  });

  validateResponse(response);
  assert.equal(response.error.code, ERROR_CODES.sessionNotFound);
  assert.equal(response.error.data.audit.operation, "hardware.operation.request");
  assert.equal(response.error.data.audit.result, "rejected");
});
