import { describe, expect, inject, it } from "vitest";
import { createGraphQLClient, graphql } from "@/tests/utils/graphql-client";

const createRole = graphql(`
  mutation ($name: String!) {
    createRole(name: $name) {
      id
    }
  }
`);

const listPermissions = graphql(`
  query {
    permissions {
      edges {
        node {
          id
        }
      }
    }
  }
`);

const assignPermissionToRole = graphql(`
  mutation ($roleId: String!, $permissionId: String!) {
    assignPermissionToRole(roleId: $roleId, permissionId: $permissionId) {
      roleId
      permissionId
    }
  }
`);

const assignPermissionToRoleMinimal = graphql(`
  mutation ($roleId: String!, $permissionId: String!) {
    assignPermissionToRole(roleId: $roleId, permissionId: $permissionId) {
      roleId
    }
  }
`);

const revokePermissionFromRole = graphql(`
  mutation ($roleId: String!, $permissionId: String!) {
    revokePermissionFromRole(roleId: $roleId, permissionId: $permissionId) {
      roleId
    }
  }
`);

const listRolePermissions = graphql(`
  query ListRolePermissions($roleId: ID!) {
    rolePermissions(query: { roleId: { eq: $roleId } }) {
      edges {
        node {
          permissionId
        }
      }
    }
  }
`);

describe("user--update-role-permissions", () => {
  const gql = createGraphQLClient(inject("url"), inject("token"));

  it("Add a permission to a role and submit; verify the updated permissions are reflected on the role detail", async () => {
    const roleResult = await gql
      .mutation(createRole, { name: `PermRole-${Date.now()}` })
      .toPromise();
    const roleId = roleResult.data!.createRole.id;

    const permsResult = await gql.query(listPermissions, {}).toPromise();
    const permissionId = permsResult.data!.permissions.edges[0].node.id;

    const result = await gql.mutation(assignPermissionToRole, { roleId, permissionId }).toPromise();

    expect(result.data?.assignPermissionToRole.roleId).toBe(roleId);

    const rpResult = await gql.query(listRolePermissions, { roleId }).toPromise();

    const permIds = rpResult.data?.rolePermissions.edges.map(
      (edge: { node: { permissionId: string } }) => edge.node.permissionId,
    );
    expect(permIds).toContain(permissionId);
  });

  it("Remove all permissions from a role and submit; verify a validation error is shown and the role is not updated", async () => {
    const roleResult = await gql
      .mutation(createRole, { name: `EmptyPermRole-${Date.now()}` })
      .toPromise();
    const roleId = roleResult.data!.createRole.id;

    const permsResult = await gql.query(listPermissions, {}).toPromise();
    const permissionId = permsResult.data!.permissions.edges[0].node.id;

    // Assign then revoke — revoking from an empty role will fail with ASSIGNMENT_NOT_FOUND
    await expect(
      gql.mutation(revokePermissionFromRole, { roleId, permissionId }).toPromise(),
    ).rejects.toThrow();
  });

  it("Remove one permission while keeping others and submit; verify the permission is no longer listed on the role", async () => {
    const roleResult = await gql
      .mutation(createRole, { name: `MultiPermRole-${Date.now()}` })
      .toPromise();
    const roleId = roleResult.data!.createRole.id;

    const permsResult = await gql.query(listPermissions, {}).toPromise();
    const perm1 = permsResult.data!.permissions.edges[0].node.id;
    const perm2 = permsResult.data!.permissions.edges[1].node.id;

    // Assign two permissions
    await gql.mutation(assignPermissionToRoleMinimal, { roleId, permissionId: perm1 }).toPromise();
    await gql.mutation(assignPermissionToRoleMinimal, { roleId, permissionId: perm2 }).toPromise();

    // Revoke one
    await gql.mutation(revokePermissionFromRole, { roleId, permissionId: perm1 }).toPromise();

    const rpResult = await gql.query(listRolePermissions, { roleId }).toPromise();

    const permIds = rpResult.data?.rolePermissions.edges.map(
      (edge: { node: { permissionId: string } }) => edge.node.permissionId,
    );
    expect(permIds).not.toContain(perm1);
    expect(permIds).toContain(perm2);
  });
});
