import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
import { createM3Topology, createMvpTopology } from "./fixture.mjs";
import { createPatchPanel, createWiringConfig } from "./model.mjs";

test("MVP topology includes two box simulators, two gateway simulators, and one patch panel", () => {
  const topology = createMvpTopology();

  assert.equal(topology.services.boxes.length, 2);
  assert.equal(topology.services.gateways.length, 2);
  assert.equal(topology.services.patchPanel.serviceId, "hwlab-patch-panel");
  assert.equal(topology.constraints.patchPanelIsOnlyCrossDeviceChannel, true);

  for (const box of topology.services.boxes) {
    assert.equal(box.serviceId, "hwlab-box-simu");
    assert.equal(box.constraints.localLoopbackEnabled, false);
    assert.equal(box.constraints.crossDevicePropagation, "patch-panel-only");
  }
});

test("M3 topology is the default trusted DO1 to DI1 patch-panel route", () => {
  const topology = createM3Topology();

  assert.equal(topology.projectId, "prj_m3_hardware_loop");
  assert.equal(topology.services.boxes.length, 2);
  assert.equal(topology.services.gateways.length, 2);
  assert.deepEqual(
    topology.services.boxes.map((box) => box.resource.resourceId),
    ["res_boxsimu_1", "res_boxsimu_2"]
  );
  assert.deepEqual(
    topology.services.gateways.map((gateway) => gateway.gatewayId),
    ["gwsimu_1", "gwsimu_2"]
  );
  assert.deepEqual(topology.services.patchPanel.activeConnections, [
    {
      fromResourceId: "res_boxsimu_1",
      fromPort: "DO1",
      toResourceId: "res_boxsimu_2",
      toPort: "DI1"
    }
  ]);
  assert.deepEqual(topology.wiringConfig.connections, [
    {
      from: {
        resourceId: "res_boxsimu_1",
        port: "DO1"
      },
      to: {
        resourceId: "res_boxsimu_2",
        port: "DI1"
      },
      mode: "exclusive"
    }
  ]);
});

test("patch panel routes cross-device signals and records deliveries", () => {
  const topology = createMvpTopology();
  const patchPanel = createPatchPanel({
    wiringConfig: topology.wiringConfig,
    now: () => "2026-01-01T00:00:01.000Z"
  });

  const result = patchPanel.routeSignal({
    fromResourceId: "res_boxsim_alpha",
    fromPort: "uart0",
    value: "boot>"
  });

  assert.equal(result.accepted, true);
  assert.equal(result.propagatedBy, "hwlab-patch-panel");
  assert.deepEqual(result.deliveries, [
    {
      resourceId: "res_boxsim_beta",
      port: "uart0",
      value: "boot>",
      sourceResourceId: "res_boxsim_alpha",
      sourcePort: "uart0",
      observedAt: "2026-01-01T00:00:01.000Z"
    }
  ]);
});

test("static dry-run fixture matches generated MVP topology", async () => {
  const raw = await readFile(new URL("../../fixtures/mvp-topology/topology.json", import.meta.url), "utf8");
  assert.deepEqual(JSON.parse(raw), createMvpTopology());
});

test("patch panel validates typed DO/AO/FREQ wiring and reports readable wrong-wire diagnostics", () => {
  const wiringConfig = createWiringConfig({
    wiringConfigId: "wir_typed_signals",
    projectId: "prj_typed_signals",
    gatewaySessionId: "gws_typed_signals",
    connections: [
      {
        from: {
          resourceId: "res_alpha",
          port: "DO1"
        },
        to: {
          resourceId: "res_beta",
          port: "DI1"
        },
        mode: "exclusive"
      },
      {
        from: {
          resourceId: "res_alpha",
          port: "AO1"
        },
        to: {
          resourceId: "res_beta",
          port: "AI1"
        },
        mode: "exclusive"
      },
      {
        from: {
          resourceId: "res_alpha",
          port: "FREQ_OUT1"
        },
        to: {
          resourceId: "res_beta",
          port: "FREQ_IN1"
        },
        mode: "exclusive"
      }
    ],
    now: "2026-01-01T00:00:00.000Z"
  });
  const patchPanel = createPatchPanel({ wiringConfig });

  assert.equal(patchPanel.diagnosticSummary.errorCount, 0);
  assert.deepEqual(
    patchPanel.normalizedConnections.map((connection) => connection.syncKind),
    ["DO->DI", "AO->AI", "FREQ_OUT->FREQ_IN"]
  );

  const invalid = createPatchPanel({
    wiringConfig: createWiringConfig({
      wiringConfigId: "wir_invalid_signal",
      projectId: "prj_typed_signals",
      gatewaySessionId: "gws_typed_signals",
      connections: [
        {
          from: {
            resourceId: "res_alpha",
            port: "DI1"
          },
          to: {
            resourceId: "res_beta",
            port: "DO1"
          },
          mode: "exclusive"
        }
      ],
      now: "2026-01-01T00:00:00.000Z"
    })
  });

  assert.equal(invalid.status().state, "faulted");
  assert.ok(invalid.diagnostics.some((diagnostic) => diagnostic.code === "source_port_not_output"));
});
