import assert from "node:assert/strict";
import path from "node:path";
import { describe, it } from "node:test";

import {
  SwitchboardPathError,
  SwitchboardPaths,
  sanitizePathSegment,
} from "../paths/SwitchboardPaths.ts";

describe("WP-01 SwitchboardPaths", () => {
  it("builds paths under persistDir/switchboard for normal ids", () => {
    const persistDir = path.join("D:", "proj", ".codeflowmu", "state");
    const paths = new SwitchboardPaths({ persistDir });
    assert.equal(paths.root, path.join(path.resolve(persistDir), "switchboard"));

    const host = paths.hostFile("host-local-a");
    assert.ok(host.endsWith(path.join("switchboard", "hosts", "host-local-a.json")));

    const binding = paths.bindingFile("worker-01", "20260804T120000Z", 3);
    assert.ok(
      binding.includes(path.join("bindings", "worker-01", "BINDING-20260804T120000Z-3.md")),
    );

    const mailbox = paths.mailboxDir("worker-01", "pending");
    assert.ok(mailbox.endsWith(path.join("mailboxes", "worker-01", "pending")));

    const run = paths.runFile("worker-01", "active", "run-9");
    assert.ok(run.endsWith(path.join("runs", "worker-01", "active", "run-9.json")));

    const claim = paths.claimFile("host-local-a", "sess-key");
    assert.ok(claim.endsWith(path.join("claims", "host-local-a", "sess-key.claim")));

    const proj = paths.projectionContextFile("run-9");
    assert.ok(proj.endsWith(path.join("projections", "run-9", "CONTEXT.md")));
  });

  it("rejects empty, traversal, separators, and absolute ids", () => {
    assert.throws(() => sanitizePathSegment(""), SwitchboardPathError);
    assert.throws(() => sanitizePathSegment("   "), SwitchboardPathError);
    assert.throws(() => sanitizePathSegment(".."), SwitchboardPathError);
    assert.throws(() => sanitizePathSegment("a/../b"), SwitchboardPathError);
    assert.throws(() => sanitizePathSegment("a/b"), SwitchboardPathError);
    assert.throws(() => sanitizePathSegment("a\\b"), SwitchboardPathError);
    assert.throws(() => sanitizePathSegment("/tmp/x"), SwitchboardPathError);
    assert.throws(() => sanitizePathSegment("C:\\\\abs"), SwitchboardPathError);
    assert.throws(() => sanitizePathSegment("D:/abs"), SwitchboardPathError);

    const paths = new SwitchboardPaths({
      persistDir: path.join("D:", "proj", ".codeflowmu", "state"),
    });
    assert.throws(() => paths.hostFile("../evil"), SwitchboardPathError);
    assert.throws(() => paths.bindingsDir(""), SwitchboardPathError);
  });

  it("encodes unsafe filename characters while keeping path under switchboard", () => {
    const paths = new SwitchboardPaths({
      persistDir: path.join("D:", "proj", ".codeflowmu", "state"),
    });
    const file = paths.hostFile("host:alpha beta");
    assert.ok(file.includes(`${path.sep}switchboard${path.sep}hosts${path.sep}`));
    assert.ok(!file.includes(" "));
    assert.ok(file.endsWith(".json"));
  });

  it("POSIX-style persistDir resolves with path.join semantics", () => {
    const persistDir = "/var/project/.codeflowmu/state";
    const paths = new SwitchboardPaths({ persistDir });
    const host = paths.hostFile("host-a");
    assert.ok(host.includes(`${path.sep}switchboard${path.sep}hosts${path.sep}host-a.json`));
  });
});
