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

const listRoles = graphql(`
  query {
    roles {
      edges {
        node {
          id
        }
      }
    }
  }
`);

const createUser = graphql(`
  mutation ($name: String!, $email: String!, $roleIds: [String!]) {
    createUser(name: $name, email: $email, roleIds: $roleIds) {
      id
    }
  }
`);

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

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

const listUserRoles = graphql(`
  query ListUserRoles($userId: ID!) {
    userRoles(query: { userId: { eq: $userId } }) {
      edges {
        node {
          roleId
        }
      }
    }
  }
`);

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

  it("Select a role not yet assigned to the user and confirm; verify the role appears in the user's role list", async () => {
    const rolesResult = await gql.query(listRoles, {}).toPromise();
    const roleId = rolesResult.data!.roles.edges[0].node.id;

    const createResult = await gql
      .mutation(createUser, {
        name: "Assign Role User",
        email: `assign-role-${Date.now()}@example.com`,
        roleIds: [roleId],
      })
      .toPromise();
    const userId = createResult.data!.createUser.id;

    const newRole = await gql
      .mutation(createRole, { name: `AssignTestRole-${Date.now()}` })
      .toPromise();
    const newRoleId = newRole.data!.createRole.id;

    const result = await gql.mutation(assignRoleToUser, { userId, roleId: newRoleId }).toPromise();

    expect(result.data?.assignRoleToUser.userId).toBe(userId);
    expect(result.data?.assignRoleToUser.roleId).toBe(newRoleId);

    const userRolesResult = await gql.query(listUserRoles, { userId }).toPromise();

    const assignedRoleIds = userRolesResult.data?.userRoles.edges.map((edge) => edge.node.roleId);
    expect(assignedRoleIds).toContain(newRoleId);
  });

  it("Attempt to assign a role the user already holds; verify an error is shown and no duplicate assignment is created", async () => {
    const rolesResult = await gql.query(listRoles, {}).toPromise();
    const roleId = rolesResult.data!.roles.edges[0].node.id;

    const createResult = await gql
      .mutation(createUser, {
        name: "Idempotent Role User",
        email: `idem-role-${Date.now()}@example.com`,
        roleIds: [roleId],
      })
      .toPromise();
    const userId = createResult.data!.createUser.id;

    // Assign same role again — command is idempotent, succeeds without error
    const result = await gql.mutation(assignRoleToUser, { userId, roleId }).toPromise();

    expect(result.data?.assignRoleToUser.userId).toBe(userId);
  });
});
