#!/usr/bin/env bun
import assert from "node:assert/strict";
import { createServer } from "node:net";

import { createCloudApiServer } from "../internal/cloud/server.ts";
import { ENVIRONMENT_DEV } from "../internal/protocol/index.mjs";

const previousEnv = {
  HWLAB_CLOUD_DB_URL: process.env.HWLAB_CLOUD_DB_URL,
  HWLAB_CLOUD_DB_SSL_MODE: process.env.HWLAB_CLOUD_DB_SSL_MODE
};

function logOk(name) {
  process.stdout.write(`[cloud-api-runtime-smoke] ok ${name}\n`);
}

async function freePort() {
  const server = createServer();
  await new Promise((resolve, reject) => {
    server.once("error", reject);
    server.listen(0, "127.0.0.1", resolve);
  });
  const address = server.address();
  const port = typeof address === "object" && address ? address.port : null;
  await new Promise((resolve, reject) => {
    server.close((error) => (error ? reject(error) : resolve()));
  });
  return port;
}

async function requestJson(url, options = {}) {
  const response = await fetch(url, {
    ...options,
    headers: {
      "content-type": "application/json",
      ...(options.headers ?? {})
    }
  });
  const text = await response.text();
  return {
    response,
    body: text ? JSON.parse(text) : null
  };
}

function rpcBody(id, method, params = {}) {
  return JSON.stringify({
    jsonrpc: "2.0",
    id,
    method,
    params,
    meta: {
      traceId: `trc_${id}`,
      actorId: "usr_cloud_api_runtime_smoke",
      serviceId: "hwlab-cloud-api",
      environment: ENVIRONMENT_DEV
    }
  });
}

async function rpc(baseUrl, id, method, params = {}) {
  const { response, body } = await requestJson(`${baseUrl}/rpc`, {
    method: "POST",
    body: rpcBody(id, method, params)
  });
  assert.equal(response.status, 200, `${method} http status`);
  assert.equal(body.jsonrpc, "2.0", `${method} JSON-RPC version`);
  assert.equal(Object.hasOwn(body, "error"), false, body.error?.message);
  return body.result;
}

async function run() {
  delete process.env.HWLAB_CLOUD_DB_URL;
  delete process.env.HWLAB_CLOUD_DB_SSL_MODE;

  const port = await freePort();
  const server = createCloudApiServer();
  await new Promise((resolve) => server.listen(port, "127.0.0.1", resolve));
  const baseUrl = `http://127.0.0.1:${port}`;

  try {
    const health = await requestJson(`${baseUrl}/health`);
    assert.equal(health.response.status, 200);
    assert.equal(health.body.serviceId, "hwlab-cloud-api");
    assert.equal(health.body.environment, ENVIRONMENT_DEV);
    assert.equal(health.body.status, "degraded");
    assert.equal(health.body.db.status, "blocked");
    assert.equal(health.body.db.connected, false);
    assert.equal(health.body.db.liveConnected, false);
    assert.equal(health.body.db.liveDbEvidence, false);
    assert.equal(health.body.db.connectionAttempted, false);
    assert.equal(health.body.db.connectionResult, "not_attempted_missing_env");
    assert.equal(health.body.db.endpointSource, "secret-url-host");
    assert.equal(health.body.db.optionalPublicDnsAlias.source, "optional-public-dns-alias");
    assert.equal(health.body.db.optionalPublicDnsAlias.requiredForReadiness, false);
    assert.equal(health.body.db.optionalPublicDnsAlias.usedForProbe, false);
    assert.equal(health.body.db.ready, false);
    assert.deepEqual(health.body.db.missingEnv, ["HWLAB_CLOUD_DB_URL", "HWLAB_CLOUD_DB_SSL_MODE"]);
    assert.equal(health.body.runtime.adapter, "memory");
    assert.equal(health.body.runtime.durable, false);
    assert.equal(health.body.runtime.status, "degraded");
    assert.match(health.body.runtime.reason, /process-local/u);
    assert.equal(health.body.readiness.components.db, "blocked");
    assert.equal(health.body.readiness.components.runtime, "waiting_for_db");
    assert.ok(health.body.blockerCodes.includes("db_runtime_config_blocked"));
    assert.ok(health.body.blockerCodes.includes("runtime_durable_adapter_missing"));
    logOk("health degraded DB contract");

    const v1 = await requestJson(`${baseUrl}/v1`);
    assert.equal(v1.response.status, 200);
    assert.equal(v1.body.status, "degraded");
    for (const method of [
      "gateway.session.register",
      "box.resource.register",
      "box.capability.report",
      "hardware.operation.request",
      "hardware.invoke.shell",
      "audit.event.query"
    ]) {
      assert.ok(v1.body.methods.includes(method), `missing ${method}`);
    }
    logOk("REST adapter method contract");

    const projectId = "prj_cloud-api-runtime-smoke";
    const gatewaySessionId = "gws_cloud-api-runtime-smoke";
    const resourceId = "res_cloud-api-runtime-smoke";
    const capabilityId = "cap_cloud-api-runtime-smoke";

    const gateway = await rpc(baseUrl, "req_cloud_api_runtime_gateway", "gateway.session.register", {
      projectId,
      gatewaySessionId,
      gatewayId: "gtw_cloud-api-runtime-smoke",
      serviceId: "hwlab-gateway",
      endpoint: "http://127.0.0.1:7101"
    });
    assert.equal(gateway.registered, true);
    assert.equal(gateway.gatewaySession.gatewaySessionId, gatewaySessionId);
    assert.equal(gateway.auditEvent.action, "gateway.session.register");

    const resource = await rpc(baseUrl, "req_cloud_api_runtime_box", "box.resource.register", {
      projectId,
      gatewaySessionId,
      resourceId,
      boxId: "box_cloud-api-runtime-smoke",
      resourceType: "board",
      state: "available",
      metadata: {
        ports: ["shell0"]
      }
    });
    assert.equal(resource.registered, true);
    assert.equal(resource.resource.resourceId, resourceId);

    const capability = await rpc(baseUrl, "req_cloud_api_runtime_capability", "box.capability.report", {
      capabilityId,
      resourceId,
      projectId,
      name: "shell.exec",
      direction: "bidirectional",
      valueType: "object",
      mutatesState: true
    });
    assert.equal(capability.reported, true);
    assert.equal(capability.capabilities[0].capabilityId, capabilityId);
    logOk("gateway/box/capability write path");

    const invoke = await rpc(baseUrl, "req_cloud_api_runtime_shell", "hardware.invoke.shell", {
      projectId,
      gatewaySessionId,
      resourceId,
      capabilityId,
      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");
    logOk("hardware invoke shell degraded dispatch");

    const audit = await rpc(baseUrl, "req_cloud_api_runtime_audit_query", "audit.event.query", {
      projectId
    });
    assert.ok(audit.count >= 5);
    assert.ok(audit.events.every((event) => event.serviceId === "hwlab-cloud-api"));
    assert.ok(audit.events.every((event) => event.environment === ENVIRONMENT_DEV));
    assert.ok(audit.events.some((event) => event.action === "hardware.operation.request"));
    assert.ok(audit.events.some((event) => event.action === "hardware.invoke.shell"));
    logOk("audit query shape");

    process.stdout.write("[cloud-api-runtime-smoke] passed\n");
  } finally {
    await new Promise((resolve, reject) => {
      server.close((error) => (error ? reject(error) : resolve()));
    });
    restoreEnv();
  }
}

function restoreEnv() {
  for (const [name, value] of Object.entries(previousEnv)) {
    if (value === undefined) {
      delete process.env[name];
    } else {
      process.env[name] = value;
    }
  }
}

await run();
