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

import {
  createControlCoreApp,
} from "../../device-control-core/src/app.mjs";
import { createCoreDiscoveryClient } from "../src/core-client.mjs";
import { createDeviceGatewayRuntime } from "../src/runtime.mjs";

const gatewayToken = "test-only-gateway-token-with-32-bytes";
const identifierPepper = "test-only-identifier-pepper-with-32-bytes";
const specificationHeader = Buffer.from(
  "FF23E9EF782DE7120300",
  "hex",
);
const specificationPackage = Buffer.from(
  "5B01010000FBDEC251EC5D",
  "hex",
);

test("B2 HEADER2 becomes a persisted masked quarantine discovery before ACK", async () => {
  let stored;
  const core = createControlCoreApp({
    discoveryIngestEnabled: true,
    gatewayToken,
    identifierPepper,
    repository: {
      health: async () => "ready",
      upsertQuarantineDiscovery: async (value) => {
        stored = value;
        return {
          created: true,
          value: {
            ...value.safeView,
            discoveryRef: "discovery:integration-001",
          },
        };
      },
    },
  });
  await listen(core);
  const coreAddress = core.address();
  const observe = createCoreDiscoveryClient({
    coreUrl: `http://127.0.0.1:${coreAddress.port}`,
    gatewayToken,
  });
  const gateway = createDeviceGatewayRuntime({
    healthPort: 0,
    tcpHost: "0.0.0.0",
    tcpPort: 0,
    listenEnabled: true,
    publicIngressEnabled: true,
    now: () => new Date(0x52db95de * 1000),
    onDiscovery: observe,
  });
  const addresses = await gateway.start();
  try {
    const response = await exchange(
      addresses.tcpAddress.port,
      Buffer.concat([specificationHeader, specificationPackage]),
      13,
    );
    assert.equal(
      response.toString("hex").toUpperCase(),
      "7B0400A0DE95DB527D7B00017D",
    );
    assert.match(stored.identifierDigest, /^hmac-sha256:[a-f0-9]{64}$/);
    assert.equal(stored.safeView.lifecycleState, "quarantine");
    assert.equal(stored.safeView.identifier.masked, "***********7769");
    assert.equal(stored.safeView.commandTransport, "disabled");
    assert.equal(
      JSON.stringify(stored).includes("865209039777769"),
      false,
    );
  } finally {
    await gateway.stop();
    await close(core);
  }
});

function listen(server) {
  return new Promise((resolve, reject) => {
    server.once("error", reject);
    server.listen(0, "127.0.0.1", resolve);
  });
}

function close(server) {
  return new Promise((resolve, reject) => {
    server.close((error) => (error ? reject(error) : resolve()));
  });
}

function exchange(port, payload, expectedBytes) {
  return new Promise((resolve, reject) => {
    const chunks = [];
    let byteLength = 0;
    const socket = connect({ host: "127.0.0.1", port }, () => {
      socket.write(payload);
    });
    socket.on("data", (chunk) => {
      chunks.push(chunk);
      byteLength += chunk.length;
      if (byteLength >= expectedBytes) {
        socket.destroy();
        resolve(Buffer.concat(chunks, byteLength));
      }
    });
    socket.on("error", reject);
  });
}
