import { describe, expect, test } from "vitest";
import { PermissionQuery, buildQuery } from "./queries";
import { RBAC } from "./rbac";

describe("evaluating a query", () => {
  const testCases: {
    name: string;
    query: PermissionQuery;
    permissions: string[];
    valid: boolean;
  }[] = [
    {
      name: "Simple role check (Pass)",
      query: buildQuery(() => "admin"),
      permissions: ["admin", "user", "guest", "moderator", "editor", "viewer"],
      valid: true,
    },
    {
      name: "Simple role check (Fail)",
      query: buildQuery(() => "developer"),
      permissions: ["admin", "user", "guest", "moderator", "editor", "viewer"],
      valid: false,
    },
    {
      name: "'and' of two permissions (Pass)",
      query: buildQuery(({ and }) => and("admin", "user")),
      permissions: ["admin", "user", "guest", "moderator", "editor", "viewer"],
      valid: true,
    },
    {
      name: "'and' of two permissions (Fail)",
      query: buildQuery(({ and }) => and("admin", "developer")),
      permissions: ["admin", "user", "guest", "moderator", "editor", "viewer"],
      valid: false,
    },
    {
      name: "'or' of two permissions (Pass)",
      query: buildQuery(({ or }) => or("admin", "developer")),
      permissions: ["admin", "user", "guest", "moderator", "editor", "viewer"],
      valid: true,
    },
    {
      name: "or' of two permissions (Fail)",
      query: buildQuery(({ or }) => or("developer", "guest")),
      permissions: ["admin", "user", "moderator", "editor", "viewer"],
      valid: false,
    },
    {
      name: "and' and 'or' combination (Pass)",
      query: buildQuery(({ and, or }) => and("admin", or("user", "guest"))),
      permissions: ["admin", "user", "guest", "moderator", "editor", "viewer"],
      valid: true,
    },
    {
      name: "'and' and 'or' combination (Fail)",
      query: buildQuery(({ and, or }) => and("admin", or("developer", "editor"))),
      permissions: ["user", "guest", "moderator", "editor", "viewer"],
      valid: false,
    },
    {
      name: "Deep nesting of 'and'(Pass)",
      query: buildQuery(({ and }) => and("admin", and("user", and("guest", "moderator")))),
      permissions: ["admin", "user", "guest", "moderator", "editor", "viewer"],
      valid: true,
    },
    {
      name: "Deep nesting of 'and' (Fail)",
      query: buildQuery(({ and }) => and("admin", and("developer", "guest"))),
      permissions: ["admin", "user", "guest", "moderator", "editor", "viewer"],
      valid: false,
    },
    {
      name: "Deep nesting of 'or'(Pass)",
      query: buildQuery(({ or }) => or("admin", or("user", or("guest", "moderator")))),
      permissions: ["admin", "user", "guest", "moderator", "editor", "viewer"],
      valid: true,
    },
    {
      name: "Deep nesting of 'or' (Fail)",
      query: buildQuery(({ or }) => or("developer", or("editor", "viewer"))),
      permissions: ["admin", "user", "guest", "moderator"],
      valid: false,
    },
    {
      name: "Complex combination of 'and' and 'or'(Pass)",
      query: buildQuery(({ and, or }) => or(and("admin", "user"), and("guest", "moderator"))),
      permissions: ["admin", "user", "guest", "moderator", "editor", "viewer"],
      valid: true,
    },
    {
      name: "Complex combination of 'and' and 'or' (Fail)",
      query: buildQuery(({ and, or }) => or(and("admin", "developer"), and("editor", "viewer"))),
      permissions: ["admin", "user", "guest", "moderator", "viewer"],
      valid: false,
    },
    {
      name: "Multiple levels of nesting(Pass)",
      query: buildQuery(({ and, or }) =>
        or(and("admin", or("user", and("guest", "moderator"))), "editor"),
      ),
      permissions: ["admin", "user", "guest", "moderator", "editor", "viewer"],
      valid: true,
    },
    {
      name: "Multiple levels of nesting (Fail)",
      query: buildQuery(({ and, or }) =>
        or(and("admin", or("developer", and("guest", "moderator"))), "viewer"),
      ),
      permissions: ["user", "guest", "moderator", "editor"],
      valid: false,
    },
    {
      name: "Complex combination of 'and' and 'or' at different levels (Pass)",
      query: buildQuery(({ and, or }) =>
        or(and("admin", or("user", and("guest", "moderator"))), and("editor", "viewer")),
      ),
      permissions: ["admin", "user", "guest", "moderator", "editor", "viewer"],
      valid: true,
    },
    {
      name: "Complex combination of 'and' and 'or' at different levels (Fail)",
      query: buildQuery(({ and, or }) =>
        or(and("admin", or("developer", and("guest", "moderator"))), and("editor", "developer")),
      ),
      permissions: ["user", "guest", "moderator", "editor", "viewer"],
      valid: false,
    },
    {
      name: "Deep nesting of 'and' and 'or'(Pass)",
      query: buildQuery(({ and, or }) =>
        and("admin", or("user", and("guest", or("moderator", "editor")))),
      ),
      permissions: ["admin", "user", "guest", "moderator", "editor", "viewer"],
      valid: true,
    },
    {
      name: "Deep nesting of 'and' and 'or' (Fail)",
      query: buildQuery(({ and, or }) =>
        and("admin", or("developer", and("guest", or("moderator", "editor")))),
      ),
      permissions: ["user", "guest", "moderator", "editor", "viewer"],
      valid: false,
    },
  ];

  for (const tc of testCases) {
    test(tc.name, () => {
      const res = new RBAC().evaluatePermissions(tc.query, tc.permissions);
      expect(res.err).toBeUndefined();
      expect(res.val!.valid).toBe(tc.valid);
    });
  }
});

describe("bad queries", () => {
  test("catch empty {}", () => {
    const res = new RBAC().validateQuery({
      or: [
        "*",
        "api.*.read_key",
        // @ts-expect-error
        {},
      ],
    });
    expect(res.err).toBeDefined();
    expect(res.err!.message).toMatchInlineSnapshot(`"invalid_union: : Invalid input"`);
  });
});
