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

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

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

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"));

  // Create our own active role so this file does not depend on seeded roles.
  // Other tests deactivate roles in parallel, so reusing one from the list is unsafe.
  let activeRoleId: string;
  beforeAll(async () => {
    const res = await gql
      .mutation(createRole, { name: `assign-active-${Date.now()}`, permissions: [] })
      .toPromise();
    activeRoleId = res.data!.createRole.id;
  });

  it("Select a role not yet assigned to the user and confirm; verify the role appears in the user's role list", async () => {
    const createResult = await gql
      .mutation(createUser, {
        name: "Assign Role User",
        email: `assign-role-${Date.now()}@example.com`,
        roleIds: [activeRoleId],
      })
      .toPromise();
    const userId = createResult.data!.createUser.id;

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

    // Add the new role while keeping the existing one
    const result = await updateUserRoles(userId, [activeRoleId, newRoleId]);
    expect(result.data!.updateUserRoles.userId).toBe(userId);

    const userRolesResult = await gql.query(listUserRoles, { userId }).toPromise();
    const assignedRoleIds = userRolesResult.data?.userRoles.edges.map(
      (edge: { node: { roleId: string } }) => 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 createResult = await gql
      .mutation(createUser, {
        name: "Idempotent Role User",
        email: `idem-role-${Date.now()}@example.com`,
        roleIds: [activeRoleId],
      })
      .toPromise();
    const userId = createResult.data!.createUser.id;

    // Assign same role again via updateUserRoles — idempotent, succeeds
    const result = await updateUserRoles(userId, [activeRoleId]);
    expect(result.data!.updateUserRoles.userId).toBe(userId);
  });

  // Helper to call updateUserRoles mutation
  async function updateUserRoles(userId: string, roleIds: string[]) {
    const mutation = graphql(`
      mutation ($userId: ID!, $roleIds: [ID!]!) {
        updateUserRoles(userId: $userId, roleIds: $roleIds) {
          userId
        }
      }
    `);
    return gql.mutation(mutation, { userId, roleIds }).toPromise();
  }
});
