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

import { createDeviceGatewayRuntime } from "../src/runtime.mjs";

test("baseline health exposes no public ingress and no command transport", async () => {
  const runtime = createDeviceGatewayRuntime({
    healthPort: 0,
    listenEnabled: false,
  });
  const addresses = await runtime.start();
  try {
    const response = await fetch(
      `http://127.0.0.1:${addresses.healthAddress.port}/healthz`,
    );
    assert.equal(response.status, 200);
    const body = await response.json();
    assert.equal(body.publicIngress, "disabled");
    assert.equal(body.commandTransport, "disabled");
    assert.equal(body.tcpListener, "disabled");
    assert.equal(addresses.tcpAddress, null);
  } finally {
    await runtime.stop();
  }
});

test("loopback evidence listener emits no acknowledgement or command bytes", async () => {
  const captured = [];
  const runtime = createDeviceGatewayRuntime({
    healthPort: 0,
    tcpPort: 0,
    listenEnabled: true,
    onEvidence: (value) => captured.push(value),
  });
  const addresses = await runtime.start();
  try {
    const received = await sendAndCollect(
      addresses.tcpAddress.port,
      Buffer.from(
        "unverified-frame-with-fake-identifier-000000000000001",
        "utf8",
      ),
    );
    assert.equal(received.length, 0);
    assert.equal(captured.length, 1);
    assert.equal(captured[0].evidence.identifierExtracted, false);
    assert.equal(
      JSON.stringify(captured).includes("000000000000001"),
      false,
    );
    assert.equal(runtime.status().totalEvidence, 1);
    assert.equal(runtime.status().commandTransport, "disabled");
  } finally {
    await runtime.stop();
  }
});

test("baseline rejects non-loopback binding", () => {
  assert.throws(
    () => createDeviceGatewayRuntime({
      listenEnabled: true,
      tcpHost: "0.0.0.0",
    }),
    /device_gateway_baseline_loopback_only/,
  );
});

test("container health may bind all interfaces while TCP stays loopback-only", async () => {
  const runtime = createDeviceGatewayRuntime({
    healthHost: "0.0.0.0",
    healthPort: 0,
    listenEnabled: false,
  });
  const addresses = await runtime.start();
  try {
    assert.equal(addresses.healthAddress.address, "0.0.0.0");
    assert.equal(addresses.tcpAddress, null);
    assert.equal(runtime.status().publicIngress, "disabled");
  } finally {
    await runtime.stop();
  }
});

function sendAndCollect(port, payload) {
  return new Promise((resolve, reject) => {
    const chunks = [];
    const socket = connect({ host: "127.0.0.1", port }, () => {
      socket.end(payload);
    });
    socket.on("data", (chunk) => chunks.push(chunk));
    socket.on("end", () => resolve(Buffer.concat(chunks)));
    socket.on("error", reject);
  });
}
