import { RouteHarness } from "@/pkg/testutil/route-harness";
import { newId } from "@aigxion/id";
import { PermissionQuery, buildQuery } from "@aigxion/rbac";
import { expect, test } from "vitest";
import { V1KeysVerifyKeyRequest, V1KeysVerifyKeyResponse } from "./v1_keys_verifyKey";

type TestCase = {
  name: string;
  roles: {
    name: string;
    permissions: string[];
  }[];
  query: PermissionQuery | undefined;
  expected: {
    status: number;
    valid: boolean;
  };
};

test.each<TestCase>([
  {
    name: "No Roles and no query",
    roles: [],
    query: undefined,
    expected: { status: 200, valid: true },
  },
  {
    name: "No query",
    roles: [
      {
        name: newId("test"),
        permissions: [],
      },
    ],
    query: undefined,
    expected: { status: 200, valid: true },
  },
  {
    name: "Single role, single permission",
    roles: [
      {
        name: newId("test"),
        permissions: ["p1"],
      },
    ],
    query: buildQuery(({ or }) => or("p1")),
    expected: { status: 200, valid: true },
  },
  {
    name: "No roles, but required",
    roles: [],
    query: buildQuery(({ or }) => or("p1")),
    expected: { status: 200, valid: false },
  },
  {
    name: "nested of 'and' and 'or'",
    roles: [
      {
        name: "r1",
        permissions: ["p1", "p2", "p3"],
      },
      {
        name: "r2",
        permissions: ["p1", "p4", "p5"],
      },
    ],
    query: buildQuery(({ or, and }) => and("p1", or("p2", "p6", and("p4", "p2")))),
    expected: { status: 200, valid: true },
  },
  {
    name: "Simple role check (Pass)",
    query: buildQuery(() => "p1"),
    roles: [
      {
        name: "r1",
        permissions: ["p1", "p2", "p3"],
      },
      { name: "r2", permissions: ["p4", "p5", "p6"] },
    ],
    expected: { status: 200, valid: true },
  },
  {
    name: "Simple role check (Fail)",
    query: buildQuery(() => "p7"),
    roles: [
      {
        name: "r1",
        permissions: ["p1", "p2", "p3", "p4", "p5", "p6"],
      },
    ],
    expected: { status: 200, valid: false },
  },
  {
    name: "'and' of two permissions (Pass)",
    query: buildQuery(({ and }) => and("p1", "p2")),
    roles: [{ name: "r1", permissions: ["p1", "p2", "p3", "p4", "p5", "p6"] }],
    expected: { status: 200, valid: true },
  },
  {
    name: "'and' of two permissions (Fail)",
    query: buildQuery(({ and }) => and("p1", "p7")),
    roles: [{ name: "r1", permissions: ["p1", "p2", "p3", "p4", "p5", "p6"] }],
    expected: { status: 200, valid: false },
  },
  {
    name: "'or' of two permissions (Pass)",
    query: buildQuery(({ or }) => or("p1", "p7")),
    roles: [{ name: "r1", permissions: ["p1", "p2", "p3", "p4", "p5", "p6"] }],
    expected: { status: 200, valid: true },
  },
  {
    name: "or' of two permissions (Fail)",
    query: buildQuery(({ or }) => or("p7", "p3")),
    roles: [
      {
        name: "r1",
        permissions: ["p1"],
      },
      {
        name: "r2",
        permissions: ["p2"],
      },
      {
        name: "r4",
        permissions: ["p4"],
      },
      {
        name: "r5",
        permissions: ["p5"],
      },
      {
        name: "r6",
        permissions: ["p6"],
      },
    ],
    expected: { status: 200, valid: false },
  },
  {
    name: "and' and 'or' combination (Pass)",
    query: buildQuery(({ and, or }) => and("p1", or("p2", "p3"))),
    roles: [{ name: "r1", permissions: ["p1", "p2", "p3", "p4", "p5", "p6"] }],
    expected: { status: 200, valid: true },
  },
  {
    name: "'and' and 'or' combination (Fail)",
    query: buildQuery(({ and, or }) => and("p1", or("p7", "p5"))),
    roles: [
      {
        name: "r1",
        permissions: ["p2", "p3"],
      },
      {
        name: "r2",
        permissions: ["p4"],
      },
      {
        name: "r3",
        permissions: ["p5", "p6"],
      },
    ],
    expected: { status: 200, valid: false },
  },
  {
    name: "Deep nesting of 'and'(Pass)",
    query: buildQuery(({ and }) => and("p1", and("p2", and("p3", "p4")))),
    roles: [{ name: "r1", permissions: ["p1", "p2", "p3", "p4", "p5", "p6"] }],
    expected: { status: 200, valid: true },
  },
  {
    name: "Deep nesting of 'and' (Fail)",
    query: buildQuery(({ and }) => and("p1", and("p7", "p3"))),
    roles: [{ name: "r1", permissions: ["p1", "p2", "p3", "p4", "p5", "p6"] }],
    expected: { status: 200, valid: false },
  },
  {
    name: "Deep nesting of 'or'(Pass)",
    query: buildQuery(({ or }) => or("p1", or("p2", or("p3", "p4")))),
    roles: [{ name: "r1", permissions: ["p1", "p2", "p3", "p4", "p5", "p6"] }],
    expected: { status: 200, valid: true },
  },
  {
    name: "Deep nesting of 'or' (Fail)",
    query: buildQuery(({ or }) => or("p7", or("p5", "p6"))),
    roles: [
      {
        name: "r1",
        permissions: ["p1", "p2", "p3"],
      },
      {
        name: "r2",
        permissions: ["p4"],
      },
    ],
    expected: { status: 200, valid: false },
  },
  {
    name: "Complex combination of 'and' and 'or'(Pass)",
    query: buildQuery(({ and, or }) => or(and("p1", "p2"), and("p3", "p4"))),
    roles: [{ name: "r1", permissions: ["p1", "p2", "p3", "p4", "p5", "p6"] }],
    expected: { status: 200, valid: true },
  },
  {
    name: "Complex combination of 'and' and 'or' (Fail)",
    query: buildQuery(({ and, or }) => or(and("p1", "p7"), and("p5", "p6"))),
    roles: [
      {
        name: "r1",
        permissions: ["p1", "p2", "p3", "p4", "p6"],
      },
    ],
    expected: { status: 200, valid: false },
  },
  {
    name: "Multiple levels of nesting(Pass)",
    query: buildQuery(({ and, or }) => or(and("p1", or("p2", and("p3", "p4"))), "p5")),
    roles: [{ name: "r1", permissions: ["p1", "p2", "p3", "p4", "p5", "p6"] }],
    expected: { status: 200, valid: true },
  },
  {
    name: "Multiple levels of nesting (Fail)",
    query: buildQuery(({ and, or }) => or(and("p1", or("p7", and("p3", "p4"))), "p6")),
    roles: [{ name: "r1", permissions: ["p2", "p3", "p4", "p5"] }],
    expected: { status: 200, valid: false },
  },
  {
    name: "Complex combination of 'and' and 'or' at different levels (Pass)",
    query: buildQuery(({ and, or }) => or(and("p1", or("p2", and("p3", "p4"))), and("p5", "p6"))),
    roles: [{ name: "r1", permissions: ["p1", "p2", "p3", "p4", "p5", "p6"] }],
    expected: { status: 200, valid: true },
  },
  {
    name: "Complex combination of 'and' and 'or' at different levels (Fail)",
    query: buildQuery(({ and, or }) => or(and("p1", or("p7", and("p3", "p4"))), and("p5", "p7"))),
    roles: [{ name: "r1", permissions: ["p2", "p3", "p4", "p5", "p6"] }],
    expected: { status: 200, valid: false },
  },
  {
    name: "Deep nesting of 'and' and 'or'(Pass)",
    query: buildQuery(({ and, or }) => and("p1", or("p2", and("p3", or("p4", "p5"))))),
    roles: [{ name: "r1", permissions: ["p1", "p2", "p3", "p4", "p5", "p6"] }],
    expected: { status: 200, valid: true },
  },
  {
    name: "Deep nesting of 'and' and 'or' (Fail)",
    query: buildQuery(({ and, or }) => and("p1", or("p7", and("p3", or("p4", "p5"))))),
    roles: [{ name: "r1", permissions: ["p2", "p3", "p4", "p5", "p6"] }],
    expected: { status: 200, valid: false },
  },
])(
  "$name",
  async ({ roles, query, expected }) => {
    const h = await RouteHarness.init();
    const { key } = await h.createKey({ roles });

    const res = await h.post<V1KeysVerifyKeyRequest, V1KeysVerifyKeyResponse>({
      url: "/v1/keys.verifyKey",
      headers: {
        "Content-Type": "application/json",
      },
      body: {
        key,
        apiId: h.resources.userApi.id,
        authorization: query
          ? {
            permissions: query,
          }
          : undefined,
      },
    });
    expect(res.status).toEqual(expected.status);
    expect(
      res.body.valid,
      `key is ${res.body.valid ? "valid" : "not valid"}, received body: ${JSON.stringify(
        res.body,
      )}`,
    ).toBe(expected.valid);
  },
  { timeout: 60_000 },
);
