import { afterAll, describe, expect, test } from "bun:test";
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { loadAppAuthzCatalog } from "../src/authz-boot.ts";

// Fixtures must live inside the repo tree, not os.tmpdir(). A scaffolded file
// under app/authz importing "@wrnexus/authz" by bare specifier resolves via
// the root tsconfig.json `paths` map, walked from the *imported file's*
// location — an out-of-tree path (os.tmpdir(), often a different drive on
// Windows) never reaches it and fails to resolve the module.
const scratchRoot = join(import.meta.dir, ".tmp-authz-boot");
mkdirSync(scratchRoot, { recursive: true });

function scaffold(body: string): string {
  const root = mkdtempSync(join(scratchRoot, "app-"));
  mkdirSync(join(root, "app", "authz"), { recursive: true });
  mkdirSync(join(root, "app", "pages"), { recursive: true });
  writeFileSync(join(root, "app", "authz", "main.ts"), body, "utf8");
  return join(root, "app");
}

afterAll(() => {
  rmSync(scratchRoot, { recursive: true, force: true });
});

describe("loadAppAuthzCatalog", () => {
  test("loads declarations from app/authz", async () => {
    const appDir = scaffold(
      `import { defineAuthz } from "@wrnexus/authz";
export default defineAuthz({ permissions: { "post:read": {} } });`,
    );
    const catalog = await loadAppAuthzCatalog(appDir);
    expect(catalog.permissions.has("post:read")).toBe(true);
  });

  test("an app with no declarations gets an empty catalog rather than an error", async () => {
    const root = mkdtempSync(join(scratchRoot, "empty-"));
    mkdirSync(join(root, "app", "pages"), { recursive: true });
    const catalog = await loadAppAuthzCatalog(join(root, "app"));
    expect(catalog.permissions.size).toBe(0);
  });

  test("a conflicting declaration fails the boot loudly", async () => {
    const appDir = scaffold(
      `import { defineAuthz } from "@wrnexus/authz";
export default defineAuthz({ permissions: { "post:read": { risk: "low" } } });`,
    );
    writeFileSync(
      join(appDir, "authz", "other.ts"),
      `import { defineAuthz } from "@wrnexus/authz";
export default defineAuthz({ permissions: { "post:read": { risk: "high" } } });`,
      "utf8",
    );
    await expect(loadAppAuthzCatalog(appDir)).rejects.toThrow(/WRN-AUTHZ-CONFLICT/);
  });

  test("a declaration with no default export is skipped, not fatal", async () => {
    const appDir = scaffold(`export const notDefault = 1;`);
    const catalog = await loadAppAuthzCatalog(appDir);
    expect(catalog.permissions.size).toBe(0);
  });
});
