import { describe, it, expect } from "bun:test";
import app from "../app";

// RFC 7591 Dynamic Client Registration — POST /oauth/register.
// The validation paths (redirect_uris, auth method) run BEFORE any DB access,
// so they're deterministic without a test DB. The happy path needs a DB, so it
// returns 201 with a DB or 503 without one — we assert both shapes.

async function register(body: unknown) {
  return app.request("/oauth/register", {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: typeof body === "string" ? body : JSON.stringify(body),
  });
}

describe("POST /oauth/register — validation (RFC 7591)", () => {
  it("rejects a non-JSON body", async () => {
    const res = await register("{not json");
    expect(res.status).toBe(400);
    expect((await res.json()).error).toBe("invalid_client_metadata");
  });

  it("requires a non-empty redirect_uris array", async () => {
    const res = await register({ client_name: "X" });
    expect(res.status).toBe(400);
    expect((await res.json()).error).toBe("invalid_redirect_uri");
  });

  it("rejects a wildcard redirect URI", async () => {
    const res = await register({
      redirect_uris: ["https://evil.example.com/*"],
    });
    expect(res.status).toBe(400);
    expect((await res.json()).error).toBe("invalid_redirect_uri");
  });

  it("rejects a non-https remote redirect URI", async () => {
    const res = await register({
      redirect_uris: ["http://evil.example.com/cb"],
    });
    expect(res.status).toBe(400);
    expect((await res.json()).error).toBe("invalid_redirect_uri");
  });

  it("rejects too many redirect URIs", async () => {
    const res = await register({
      redirect_uris: Array.from({ length: 9 }, (_, i) => `https://a${i}.example.com/cb`),
    });
    expect(res.status).toBe(400);
    expect((await res.json()).error).toBe("invalid_redirect_uri");
  });

  it("rejects an unsupported token_endpoint_auth_method", async () => {
    const res = await register({
      redirect_uris: ["https://claude.ai/cb"],
      token_endpoint_auth_method: "private_key_jwt",
    });
    expect(res.status).toBe(400);
    expect((await res.json()).error).toBe("invalid_client_metadata");
  });

  it("accepts a valid public-client registration (201) or degrades to 503", async () => {
    const res = await register({
      client_name: "Claude (test)",
      redirect_uris: ["https://claude.ai/api/mcp/callback"],
      token_endpoint_auth_method: "none",
      scope: "read:repo write:pr bogus:scope",
    });
    expect([201, 503]).toContain(res.status);
    if (res.status === 201) {
      const doc = await res.json();
      expect(doc.client_id).toMatch(/^glc_app_/);
      // Public client (auth method "none") gets NO client_secret.
      expect(doc.client_secret).toBeUndefined();
      expect(doc.token_endpoint_auth_method).toBe("none");
      expect(doc.grant_types).toContain("authorization_code");
      expect(doc.client_secret_expires_at).toBe(0);
      expect(doc.registration_access_token).toBeTruthy();
      // Unknown scope dropped; known scopes preserved.
      expect(doc.scope).toContain("read:repo");
      expect(doc.scope).not.toContain("bogus:scope");
    }
  });

  it("accepts a confidential-client registration and returns a secret (201) or 503", async () => {
    const res = await register({
      client_name: "Confidential (test)",
      redirect_uris: ["https://app.example.com/callback"],
      token_endpoint_auth_method: "client_secret_basic",
    });
    expect([201, 503]).toContain(res.status);
    if (res.status === 201) {
      const doc = await res.json();
      expect(doc.client_secret).toMatch(/^glcs_/);
    }
  });
});
