import test from "node:test";
import assert from "node:assert/strict";
import { safeChangeFiles, safeChangePaths, safeWorkspaceRelativePath } from "./safe-change-paths.js";

test("keeps only workspace-relative changed file paths", () => {
  assert.deepEqual(safeChangePaths([
    { path: "apps/pwa/src/App.jsx" },
    { path: "C:\\Project\\Codex-remote\\README.md" },
    { path: "C:\\Private\\secret.txt" },
    { path: "..\\outside.txt" }
  ], "C:\\Project\\Codex-remote", "C:\\Project\\Codex-remote"), [
    "apps/pwa/src/App.jsx",
    "README.md"
  ]);
});

test("keeps safe file-change diffs with normalized relative paths", () => {
  assert.deepEqual(safeChangeFiles([
    { path: "apps/pwa/src/App.jsx", diff: "diff --git a/apps/pwa/src/App.jsx b/apps/pwa/src/App.jsx\n+ok" },
    { path: "C:\\Project\\Codex-remote\\README.md", patch: "+readme" },
    { path: "C:\\Private\\secret.txt", diff: "+secret" },
    "apps/backend/src/index.js"
  ], "C:\\Project\\Codex-remote", "C:\\Project\\Codex-remote"), [
    { path: "apps/pwa/src/App.jsx", diff: "diff --git a/apps/pwa/src/App.jsx b/apps/pwa/src/App.jsx\n+ok" },
    { path: "README.md", diff: "+readme" },
    { path: "apps/backend/src/index.js" }
  ]);
});

test("replaces oversized and binary activity diffs with safe reasons", () => {
  assert.deepEqual(safeChangeFiles([
    { path: "src/large.js", diff: `+${"x".repeat(600 * 1024)}` },
    { path: "public/image.png", diff: "Binary files a/public/image.png and b/public/image.png differ" }
  ], "C:\\Project\\Codex-remote", "C:\\Project\\Codex-remote"), [
    { path: "src/large.js", unavailableReason: "diff_too_large" },
    { path: "public/image.png", unavailableReason: "binary_file" }
  ]);
});

test("rejects paths whose resolved filesystem location leaves the workspace", () => {
  const fileSystem = fakeFileSystem({
    "C:\\Project\\Codex-remote": "C:\\Project\\Codex-remote",
    "C:\\Project\\Codex-remote\\src": "C:\\Outside\\linked-src",
    "C:\\Project\\Codex-remote\\README.md": "C:\\Project\\Codex-remote\\README.md"
  });

  assert.deepEqual(safeChangePaths([
    { path: "README.md" },
    { path: "src\\secret.txt" }
  ], "C:\\Project\\Codex-remote", "C:\\Project\\Codex-remote", fileSystem), [
    "README.md"
  ]);
});

test("checks the nearest existing parent for missing files", () => {
  const fileSystem = fakeFileSystem({
    "C:\\Project\\Codex-remote": "C:\\Project\\Codex-remote",
    "C:\\Project\\Codex-remote\\linked": "C:\\Outside\\linked"
  });

  assert.equal(
    safeWorkspaceRelativePath(
      "linked\\new-file.txt",
      "C:\\Project\\Codex-remote",
      "C:\\Project\\Codex-remote",
      fileSystem
    ),
    null
  );
});

function fakeFileSystem(realpaths) {
  const normalized = new Map(Object.entries(realpaths).map(([path, realpath]) => [
    path.toLowerCase(),
    realpath
  ]));
  return {
    existsSync(path) {
      return normalized.has(path.toLowerCase());
    },
    realpathSync(path) {
      const realpath = normalized.get(path.toLowerCase());
      if (!realpath) throw new Error("ENOENT");
      return realpath;
    }
  };
}
