import { afterEach, describe, expect, test } from "bun:test";
import { mkdir, rm, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { explainBuildDecision } from "../src/explain.ts";

const roots: string[] = [];
afterEach(async () =>
  Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true }))),
);

async function fixture(): Promise<string> {
  const root = join(tmpdir(), `wrnexus-explain-${crypto.randomUUID()}`);
  roots.push(root);
  await mkdir(join(root, "dist"), { recursive: true });
  await writeFile(
    join(root, "dist", "build-report.json"),
    JSON.stringify({
      frameworkVersion: "0.8.0",
      adapter: "edge",
      measurements: { routeJsBytes: 12 },
      budgetViolations: [],
      assets: [{ file: "server.js", bytes: 12 }],
      routes: [
        {
          kind: "page",
          path: "/users/[id]",
          source: "app/pages/users/[id].wrn",
          execution: "authenticated-ssr",
          canPrerender: false,
          needsClientRuntime: true,
          needsServerRuntime: true,
          hydrationStrategy: "visible",
          reasons: ["client interactivity", "authentication required"],
          cachePolicy: { strategy: "stale-while-revalidate", ttl: "30s" },
          requiredPermission: "users.read",
        },
      ],
    }),
  );
  return root;
}

describe("causal build explanations", () => {
  test("explains route execution and hydration from persisted compiler evidence", async () => {
    const root = await fixture();
    const route = explainBuildDecision(root, "route", "/users/[id]");
    expect(route.summary).toContain("authenticated-ssr");
    expect(route.reasons).toContain("authentication required");
    expect(explainBuildDecision(root, "hydration", "users/[id]").summary).toContain("visible");
  });

  test("explains build and bundle measurements", async () => {
    const root = await fixture();
    expect(explainBuildDecision(root, "build").reasons).toContain(
      "all configured performance budgets pass",
    );
    expect(explainBuildDecision(root, "bundle").reasons[0]).toBe("server.js: 12 bytes");
  });

  test("explains cache and permission decisions", async () => {
    const root = await fixture();
    expect(explainBuildDecision(root, "cache", "/users/[id]").summary).toContain(
      "stale-while-revalidate",
    );
    expect(explainBuildDecision(root, "permission", "users.read").reasons[0]).toContain(
      "security.permission",
    );
  });

  test("uses stable diagnostics for missing evidence", () => {
    expect(() => explainBuildDecision("missing", "build")).toThrow("WRN-EXPLAIN-NO-BUILD");
  });
});
