import { describe, expect, test } from "bun:test";
import {
  createGovernance,
  createMachineIdentityManager,
  createSamlFederation,
  createScimHandler,
  discoverOidc,
  memoryScimStore,
  oidcAuthorizationUrl,
  syncDirectory,
} from "../src/index.ts";

describe("enterprise identity", () => {
  test("discovers OIDC safely and creates PKCE authorization URLs", async () => {
    const metadata = {
      issuer: "https://id.test",
      authorization_endpoint: "https://id.test/auth",
      token_endpoint: "https://id.test/token",
      jwks_uri: "https://id.test/jwks",
    };
    const discovered = await discoverOidc(metadata.issuer, {
      fetch: (async () => Response.json(metadata)) as unknown as typeof fetch,
    });
    const url = new URL(
      oidcAuthorizationUrl(discovered, {
        clientId: "app",
        redirectUri: "https://app.test/callback",
        state: "state",
        nonce: "nonce",
        codeChallenge: "challenge",
      }),
    );
    expect(url.searchParams.get("code_challenge_method")).toBe("S256");
  });

  test("validates signed SAML boundaries and prevents replay", async () => {
    const assertion = {
      id: "assertion-1",
      issuer: "https://id.test",
      audience: "app",
      recipient: "https://app.test/saml",
      expiresAt: 200,
      identity: { externalId: "u1", username: "u1", groups: [], active: true },
    };
    const federation = createSamlFederation({
      adapter: { createLoginRequest: () => "request", verifySignedResponse: async () => assertion },
      issuer: assertion.issuer,
      audience: assertion.audience,
      recipient: assertion.recipient,
      now: () => 100,
    });
    expect((await federation.callback("signed")).externalId).toBe("u1");
    await expect(federation.callback("signed")).rejects.toThrow("WRN-SAML-REPLAY");
  });

  test("syncs LDAP/AD adapters and provisions SCIM users", async () => {
    const values: string[] = [];
    expect(
      (
        await syncDirectory(
          {
            kind: "active-directory",
            search: async () => [
              { externalId: "u1", username: "user", groups: ["staff"], active: true },
            ],
          },
          {
            baseDn: "dc=test",
            upsert: (identity) => {
              values.push(identity.externalId);
            },
          },
        )
      ).synchronized,
    ).toBe(1);
    const handler = createScimHandler({ store: memoryScimStore(), bearerToken: "a".repeat(32) });
    const created = await handler(
      new Request("https://app.test/scim/v2/Users", {
        method: "POST",
        headers: { authorization: `Bearer ${"a".repeat(32)}` },
        body: JSON.stringify({ userName: "person@example.test", active: true }),
      }),
    );
    expect(created.status).toBe(201);
    expect(
      (
        await handler(
          new Request("https://app.test/scim/v2/Users", {
            headers: { authorization: `Bearer ${"a".repeat(32)}` },
          }),
        )
      ).status,
    ).toBe(200);
    expect(values).toEqual(["u1"]);
  });

  test("issues scoped machine identities without exposing hashes", async () => {
    const manager = createMachineIdentityManager(() => 100);
    const issued = await manager.issue({
      ownerId: "org",
      name: "deploy",
      kind: "service-account",
      scopes: ["deploy:write"],
    });
    expect(issued.credential.secretHash).toBe("[REDACTED]");
    expect((await manager.authenticate(issued.secret, "deploy:write"))?.kind).toBe(
      "service-account",
    );
    expect(await manager.authenticate(issued.secret, "admin")).toBeNull();
  });

  test("tracks consent, approvals, export/deletion, retention and audit", async () => {
    const audit: string[] = [];
    const deleted: string[] = [];
    const governance = createGovernance({
      now: () => 100,
      audit: (event) => {
        audit.push(event.type);
      },
      exportSubject: (id) => ({ id }),
      deleteSubject: (id) => {
        deleted.push(id);
      },
    });
    await governance.consent("u1", "analytics", true, "v2");
    const exportRequest = await governance.request("u1", "export");
    expect((await governance.decide(exportRequest.id, "admin", true)).result).toEqual({ id: "u1" });
    const deleteRequest = await governance.request("u1", "delete");
    await governance.decide(deleteRequest.id, "admin", true);
    expect(
      await governance.enforceRetention([{ subjectId: "u2", createdAt: 0 }], 50, (record) => {
        deleted.push(record.subjectId);
      }),
    ).toBe(1);
    expect(deleted).toEqual(["u1", "u2"]);
    expect(audit).toContain("consent.changed");
  });
});
