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

const createCategoryIdOnly = graphql(`
  mutation CreateCategoryForMove($code: String!, $name: String!, $parentId: ID) {
    createCategory(code: $code, name: $name, parentId: $parentId) {
      id
    }
  }
`);

const moveCategory = graphql(`
  mutation MoveCategory($id: ID!, $newParentId: ID) {
    moveCategory(id: $id, newParentId: $newParentId) {
      id
    }
  }
`);

describe("product-manager--move-category", () => {
  const gql = createGraphQLClient(inject("url"), inject("token"));

  it("Given a valid category and newParentId set to null, the category is moved to root level", async () => {
    const parentCode = `MOVE-PARENT-${Date.now()}`;
    const parentResult = await gql
      .mutation(createCategoryIdOnly, { code: parentCode, name: "Move Parent" })
      .toPromise();
    const parentId = parentResult.data!.createCategory.id;

    const childCode = `MOVE-CHILD-${Date.now()}`;
    const childResult = await gql
      .mutation(createCategoryIdOnly, { code: childCode, name: "Move Child", parentId })
      .toPromise();
    const childId = childResult.data!.createCategory.id;

    const result = await gql.mutation(moveCategory, { id: childId, newParentId: null }).toPromise();

    expect(result.data?.moveCategory.id).toBe(childId);
  });

  it("Given a valid category and a valid non-descendant newParentId, the category is reparented successfully", async () => {
    const ts = Date.now();
    const catAResult = await gql
      .mutation(createCategoryIdOnly, { code: `MOVE-A-${ts}`, name: "Category A" })
      .toPromise();
    const catAId = catAResult.data!.createCategory.id;

    const catBResult = await gql
      .mutation(createCategoryIdOnly, { code: `MOVE-B-${ts}`, name: "Category B" })
      .toPromise();
    const catBId = catBResult.data!.createCategory.id;

    const result = await gql
      .mutation(moveCategory, { id: catAId, newParentId: catBId })
      .toPromise();

    expect(result.data?.moveCategory.id).toBe(catAId);
  });

  it("Given newParentId equal to the category's own ID, the move fails with a circular reference error", async () => {
    const code = `MOVE-SELF-${Date.now()}`;
    const createResult = await gql
      .mutation(createCategoryIdOnly, { code, name: "Self Move" })
      .toPromise();
    const categoryId = createResult.data!.createCategory.id;

    await expect(
      gql.mutation(moveCategory, { id: categoryId, newParentId: categoryId }).toPromise(),
    ).rejects.toThrow();
  });

  it("Given newParentId equal to a descendant of the category, the move fails with a circular reference error", async () => {
    const ts = Date.now();
    const parentResult = await gql
      .mutation(createCategoryIdOnly, { code: `MOVE-P-${ts}`, name: "Parent" })
      .toPromise();
    const parentId = parentResult.data!.createCategory.id;

    const childResult = await gql
      .mutation(createCategoryIdOnly, {
        code: `MOVE-C-${ts}`,
        name: "Child",
        parentId,
      })
      .toPromise();
    const childId = childResult.data!.createCategory.id;

    await expect(
      gql.mutation(moveCategory, { id: parentId, newParentId: childId }).toPromise(),
    ).rejects.toThrow();
  });

  it("Given a move that would exceed the maximum allowed depth, the move fails with a max depth error", async () => {
    const ts = Date.now();

    // Build a deep chain of categories
    let currentParentId: string | undefined;
    for (let i = 0; i < 10; i++) {
      const result = await gql
        .mutation(createCategoryIdOnly, {
          code: `DEPTH-${ts}-${i}`,
          name: `Depth ${i}`,
          parentId: currentParentId,
        })
        .toPromise();
      currentParentId = result.data!.createCategory.id;
    }

    // Create a separate category and try to move it under the deepest node
    const leafResult = await gql
      .mutation(createCategoryIdOnly, { code: `DEPTH-LEAF-${ts}`, name: "Leaf" })
      .toPromise();
    const leafId = leafResult.data!.createCategory.id;

    await expect(
      gql.mutation(moveCategory, { id: leafId, newParentId: currentParentId }).toPromise(),
    ).rejects.toThrow();
  });
});
