import assert from "node:assert/strict";
import { readFile, readdir } from "node:fs/promises";
import path from "node:path";
import test from "node:test";

const packageRoot = path.resolve(import.meta.dirname, "..");
const sourceRoot = path.join(packageRoot, "src");

test("OpenClaw plugin imports Remnic core through package exports", async () => {
  const violations: string[] = [];
  const forbiddenCoreSourcePattern = new RegExp(
    String.raw`(?:^|['"])` + String.raw`(?:\.\./)+` + String.raw`(?:packages/)?remnic-core/src(?:/|\.|['"])`
  );

  for (const filePath of await listTypeScriptFiles(sourceRoot)) {
    const source = await readFile(filePath, "utf8");
    if (forbiddenCoreSourcePattern.test(source)) {
      violations.push(path.relative(packageRoot, filePath));
    }
  }

  assert.deepEqual(violations, []);
});

test("OpenClaw plugin type-check config resolves @remnic/core to one source surface", async () => {
  const tsconfig = await readFile(path.join(packageRoot, "tsconfig.check.json"), "utf8");
  const config = JSON.parse(tsconfig) as {
    compilerOptions?: { paths?: Record<string, unknown> };
  };
  const paths = config.compilerOptions?.paths ?? {};
  assert.deepEqual(paths["@remnic/core"], ["packages/remnic-core/src/index.ts"]);
  assert.deepEqual(paths["@remnic/core/*"], ["packages/remnic-core/src/*"]);

  for (const [key, value] of Object.entries(paths)) {
    if (key === "@remnic/core" || key.startsWith("@remnic/core/")) {
      assert.ok(
        Array.isArray(value) &&
          value.every((entry) => typeof entry === "string" && entry.startsWith("packages/remnic-core/src/"))
      );
    }
  }
});

test("OpenClaw plugin source manifest keeps @remnic/core workspace-linked", async () => {
  const manifest = JSON.parse(await readFile(path.join(packageRoot, "package.json"), "utf8")) as {
    dependencies?: Record<string, string>;
    devDependencies?: Record<string, string>;
    exports?: Record<string, unknown>;
  };

  assert.equal(manifest.dependencies?.["@remnic/core"], "workspace:^");
  assert.equal(manifest.devDependencies?.["@remnic/core"], "workspace:^");
  assert.deepEqual(manifest.exports?.["./support-passport-model-route"], {
    types: "./dist/support-passport-model-route.d.ts",
    "remnic-source": "./src/support-passport-model-route.ts",
    import: "./dist/support-passport-model-route.js",
  });
});

test("OpenClaw plugin declares runtime schema dependencies", async () => {
  const manifest = JSON.parse(await readFile(path.join(packageRoot, "package.json"), "utf8")) as {
    dependencies?: Record<string, string>;
    devDependencies?: Record<string, string>;
  };
  const shapesSource = await readFile(path.join(sourceRoot, "openclaw-tools", "shapes.ts"), "utf8");

  assert.match(shapesSource, /from "@sinclair\/typebox"/);
  assert.equal(manifest.dependencies?.["@sinclair/typebox"], "^0.34.0");
  assert.equal(manifest.devDependencies?.["@sinclair/typebox"], undefined);
});

async function listTypeScriptFiles(directory: string): Promise<string[]> {
  const entries = await readdir(directory, { withFileTypes: true });
  const files = await Promise.all(
    entries.map(async (entry) => {
      const entryPath = path.join(directory, entry.name);
      if (entry.isDirectory()) {
        return listTypeScriptFiles(entryPath);
      }
      if (entry.isFile() && entry.name.endsWith(".ts")) {
        return [entryPath];
      }
      return [];
    })
  );
  return files.flat().sort();
}
